C++继续

打印 上一主题 下一主题

主题 1898|帖子 1898|积分 5694

一、继续的基本概念

C++中的继续是面向对象编程的核心概念之一,它答应一个类(子类)继续另一个类(父类)的属性和举动。通过继续,子类可以重用父类的代码,还可以添加新的成员,从而实当代码的复用和扩展。
1.语法

     class 子类名 : 继续方式 父类名 {
     子类成员;
     }
2.子类和父类

父类(基类):被继续的类,它包罗了可以被子类继续的成员变量和方法。
子类(派生类):通过继续父类创建出来的新类,它不仅包罗了父类的全部成员,还可以添加新的成员。
3.子类继续的特点



  • 子类包罗了从父类继续过来的成员和自己新增的成员。
  • 子类可以利用父类的方法,还可以对父类的成员函数举行重写。
子类构造函数不能直接初始化继续的成员,必须利用父类的方法。具体地说,子类构造函数必须调用父类构造函数。
创建子类对象时,步伐起首调用父类构造函数,然后再调用子类构造函数。父类构造函数负责初始化继续的数据成员,子类构造函数主要用于初始化新增的数据成员。子类的构造函数总是调用一个父类构造函数。可以利用初始化列表语法指明要利用的父类构造函数,否则将利用默认的父类构造函数。
①子类构造函数的要点:


  • 创建子类对象时,步伐会先创建父类对象
  • 子类构造函数应通过初始化列表将父类信息传递给父类构造函数
  • 子类构造函数应初始化子类新增的数据成员
②哪些不能被继续:
父类的私有成员可以被继续,但子类访问不到,只能通过父类的公有和掩护方法访问。构造函数、析构函数、赋值运算符以及友元关系都不能被继续。
4.构造和析构顺序

构造顺序:先构造父类,再构造子类
析构顺序:先析构子类,再析构父类
  1. class Employee {
  2. private:
  3.         string name;
  4.         double salary;
  5. public:
  6.         Employee(string n,double s)
  7.                 :name(n),salary(s){}
  8.         void display() {
  9.                 cout << name << " " << salary << " ";
  10.         }
  11. };
  12. class Manager :public Employee {
  13. private:
  14.         string department;
  15. public:
  16.         Manager(string n,double s,string d)
  17.                 :Employee(n,s),department(d){}
  18.         void print() {
  19.                 display();
  20.                 cout << department << endl;
  21.         }
  22. };
复制代码
  1. Manager m1("Mike",35000,"IT");
  2. m1.print();
  3. //输出Mike 35000 IT
复制代码
 
 
二、继续方式与类关系

1.继续方式



  • 公有继续:父类的公有成员和掩护成员在子类中保持原有状态,而私有成员不可被子类访问。
  • 掩护继续:父类的公有成员和掩护成员在子类中变为掩护成员,而私有成员不可被子类访问。
  • 私有继续:父类的公有成员和掩护成员在子类中变为私有成员,而私有成员不可被子类访问。

利用私有继续时,第二代子类将不能利用父类的方法,这是因为父类的公有方法在第一代子类中变成了私有方法;利用掩护继续时,父类的公有方法在第一代子类中变成受掩护的,因此第二代子类可以利用它们。
2.类与类之间的关系

①is-a关系(继续关系):
is-a关系就是继续关系,表现了面向对象编程中的继续性,是实当代码复用和扩展性的紧张本领,且不同的继续方式会影响子类对父类成员的访问权限。
例如,一个Student类可以继续自Person类,表示门生是一类人,具有人的基本属性和举动,同时还可以有门生特有的属性和方法。
  1. class Person {
  2. protected:
  3.         string name;
  4.         int age;
  5. public:
  6.         Person(string n,int a)
  7.                 :name(n),age(a) {}
  8.         void introduce() const {
  9.                 cout << name << " " << age << endl;
  10.         }
  11. };
  12. class Student :public Person {
  13. private:
  14.         string major;
  15. public:
  16.         Student(string n,int a,string m)
  17.                 :Person(n,a),major(m) {}
  18.         void study() const {
  19.                 cout << major << endl;
  20.         }
  21. };
复制代码
  1. Student s1("Tom", 20, "cs");
  2. s1.introduce();
  3. s1.study();
  4. //输出
  5. Tom 20
  6. cs
复制代码
②has-a关系(组合关系):
has-a关系表示一个类(整体类)包罗另一个类(部分类)的对象作为其成员变量,它们是整体与部分的关系,而且生命周期通常是一致的,整体类负责构建和烧毁部分类的对象。
通常,应利用包罗来建立has-a关系;假如新类需要访问原有类的掩护成员,或需要重新定义虚函数,则应利用私有继续。
 例如,一个Car类可能包罗一个Engine类的对象,表示汽车有一个发动机。
  1. class Engine {
  2. public:
  3.         void start() const {
  4.                 cout << "Engine started." << endl;
  5.         }
  6. };
  7. class Car {
  8. public:
  9.         Engine engine;
  10.         void drive() const {
  11.                 cout << "Driving the car." << endl;
  12.                 engine.start();
  13.         }
  14. };
复制代码
  1. Car my_car;
  2. my_car.drive();
  3. //输出
  4. Driving the car.
  5. Engine started.
复制代码
③use-a关系(依赖关系):
use-a关系表示一个类(利用者类)利用另一个类(被利用者类)的对象作为其函数的参数,或者在函数中创建和利用被利用者类的对象。这种关系表现了类之间的依赖,但利用者类并不负责被利用者类对象的生命周期管理。
例如,一个Police类的方法可能需要接收一个Car范例的参数来举行超速检查,这时Police类和Car类之间就是use-a关系。
  1. class Car {
  2. public:
  3.         int speed;
  4.        
  5.         Car(int s = 0) :speed(s) {}
  6.         void displaySpeed() const {
  7.                 cout << "The car's speed is " << speed << " km/h." << endl;
  8.         }
  9. };
  10. class Police {
  11. public:
  12.         void checkSpeed(const Car& car) const {
  13.                 car.displaySpeed();
  14.                 if (car.speed > 100) {
  15.                         cout << "Speeding. Please slow down." << endl;
  16.                 }
  17.         }
  18. };
复制代码
  1. Car my_car(105);
  2. Police p;
  3. p.checkSpeed(my_car);
  4. //输出
  5. The car's speed is 105 km/h.
  6. Speeding. Please slow down.
复制代码
 
 
三、子类和父类之间的特殊关系

1.赋值转换

 在继续中,子类对象可以赋给父类对象、父类指针或父类的引用,但反之不行,父类对象不能赋值给子类对象。
父类指针可以在不举行显示范例转换地环境下指向子类对象,父类引用也可以在不举行显示范例转换地环境下引用子类对象。
父类的指针或引用可以通过欺凌范例转换赋值给子类的指针或引用,但这种转换必须是安全的,即父类指针确实指向子类对象。
  1. class Person {
  2. private:
  3.         int age;
  4.         string name;
  5. };
  6. class Student :public Person{
  7. private:
  8.         int id;
  9. };
复制代码
  1. Student student;
  2. Person person;
  3. person = student;
  4. Person* p1 = &student;
  5. Person& p2 = student;
复制代码
此外,函数参数为父类的引用对象或指针时,函数将答应子类对象的传入。
  1. void func1(const Person& p1) {
  2. }
  3. void func2(Person* p1) {
  4. }
复制代码
  1. Student student;
  2. Person person;
  3. func1(student);
  4. func1(person);
  5. func2(&student);
  6. func2(&person);
复制代码
2.拷贝构造函数和赋值运算符

当一个子类对象被复制或赋值时,假如没有显式指定怎样举行,编译器会自动调用父类的拷贝构造函数和赋值运算符。
  1. class Base {
  2. private:
  3.         int data;
  4. public:
  5.         Base(int value = 0)
  6.                 :data(value) {}
  7.         Base(const Base& other) {
  8.                 cout << "拷贝构造函数调用" << endl;
  9.                 data = other.data;
  10.         }
  11.         Base& operator=(const Base& other) {
  12.                 cout << "赋值运算符调用" << endl;
  13.                 data = other.data;
  14.                 return *this;
  15.         }
  16. };
  17. class Derived :public Base {
  18. private:
  19.         int number;
  20. public:
  21.         Derived(int d = 0, int n = 0)
  22.                 :Base(d), number(n) {}
  23. };
复制代码
  1. Derived d1(10, 20);
  2. Derived d2(d1);  //拷贝构造函数调用
  3. Derived d3;
  4. d3 = d1;  //赋值运算符调用
复制代码
3.同名成员处理方式

当子类与父类出现同名的成员时,子类对象可以直接访问到子类的同名成员,通过添加作用域可以访问到父类中的同名成员。同名静态成员也是如此。
  1. class Base {
  2. public:
  3.         int data = 10;
  4.         static void print() {
  5.                 cout << "10" << endl;
  6.         }
  7. };
  8. class Derived :public Base {
  9. public:
  10.         int data = 20;
  11.         static void print() {
  12.                 cout << "20" << endl;
  13.         }
  14. };
复制代码
  1. Derived d;
  2. cout << d.data << endl;  //20
  3. cout << d.Base::data << endl;  //10
  4. d.print();  //20
  5. d.Base::print();  //10
复制代码
 
 
四、继续和动态内存分配

1.父类不利用new,子类利用new

此时子类需要显式定义拷贝构造函数和赋值运算符,且子类的拷贝构造函数中需要显式调用父类的拷贝构造函数;子类的赋值运算符也要显式调用父类的赋值运算符,并加上作用域。
  1. class Person {
  2. protected:
  3.         string name;
  4.         int age;
  5. public:
  6.         Person(string n = "", int a = 0)
  7.                 :name(n), age(a) {
  8.         }
  9. };
  10. class Student :public Person {
  11. private:
  12.         string* major;
  13. public:
  14.         Student(string n = "", int a = 0, string m = "")
  15.                 :Person(n, a) {
  16.                 major = new string(m);
  17.         }
  18.         ~Student()
  19.         {
  20.                 delete major;
  21.         }
  22.         Student(const Student& s) :Person(s) {
  23.                 major = new string(*s.major);
  24.         }
  25.         Student& operator=(const Student& s) {
  26.                 if (this != &s) {
  27.                         delete major;
  28.                         major = new string(*s.major);
  29.                         Person::operator=(s);
  30.                 }
  31.                 return *this;
  32.         }
  33.         void print() {
  34.                 cout << name << " " << age << " " << *major << endl;
  35.         }
  36. };
复制代码
2.父类利用new,子类不利用new

此时父类需要显式定义拷贝构造函数和赋值运算符。
  1. class Person {
  2. protected:
  3.         string name;
  4.         int* age;
  5. public:
  6.         Person(string n = "", int a = 0) {
  7.                 name = n;
  8.                 age = new int(a);
  9.         }
  10.         ~Person() {
  11.                 delete age;
  12.         }
  13.         Person(const Person& p) {
  14.                 name = p.name;
  15.                 age = new int(*p.age);
  16.         }
  17.         Person& operator=(const Person& p) {
  18.                 if (this->age != p.age) {
  19.                         delete age;
  20.                     name = p.name;
  21.                     age = new int(*p.age);
  22.                 }
  23.                 return *this;
  24.         }
  25. };
  26. class Student :public Person {
  27. private:
  28.         string major;
  29. public:
  30.         Student(string n = "", int a = 0 , string m = "")
  31.                 :Person(n,a) {
  32.                 major = m;
  33.         }
  34.         void print() {
  35.                 cout << name << " " << *age << " " << major << endl;
  36.         }
  37. };
复制代码
3.父类和子类都利用new

此时父类和子类都需要显式定义拷贝构造函数和赋值运算符,且子类的拷贝构造函数中需要显式调用父类的拷贝构造函数;子类的赋值运算符也要显式调用父类的赋值运算符,并加上作用域。
  1. class Person {
  2. protected:
  3.         string name;
  4.         int* age;
  5. public:
  6.         Person(string n = "", int a = 0) {
  7.                 name = n;
  8.                 age = new int(a);
  9.         }
  10.         ~Person() {
  11.                 delete age;
  12.         }
  13.         Person(const Person& p) {
  14.                 name = p.name;
  15.                 age = new int(*p.age);
  16.         }
  17.         Person& operator=(const Person& p) {
  18.                 if (this->age != p.age) {
  19.                         delete age;
  20.                         name = p.name;
  21.                         age = new int(*p.age);
  22.                 }
  23.                 return *this;
  24.         }
  25. };
  26. class Student :public Person {
  27. private:
  28.         string* major;
  29. public:
  30.         Student(string n = "", int a = 0, string m = "")
  31.                 :Person(n, a) {
  32.                 major = new string(m);
  33.         }
  34.         ~Student() {
  35.                 delete major;
  36.         }
  37.         Student(const Student& s):Person(s) {
  38.                 major = new string(*s.major);
  39.         }
  40.         Student& operator=(const Student& s) {
  41.                 if (this != &s) {
  42.                         delete major;
  43.                         major = new string(*s.major);
  44.                         Person::operator=(s);
  45.                 }
  46.                 return *this;
  47.         }
  48.         void print() {
  49.                 cout << name << " " << *age << " " << *major << endl;
  50.         }
  51. };
复制代码
测试:
  1. Student s1("Mike", 20, "cs");
  2. Student s2(s1);
  3. Student s3;
  4. s3 = s1;
  5. s1 = s1 = s1;
  6. s1.print();  //Mike 20 cs
  7. s2.print();  //Mike 20 cs
  8. s3.print();  //Mike 20 cs
复制代码
总结:子类和父类哪个利用new,哪个就要显式定义拷贝构造函数和赋值运算符。当子类显式定义时,子类要显式调用父类的拷贝构造函数和赋值运算符。
 
 
五、多继续

1.多继续的基本概念

C++答应一个类同时继续多个类。这种特性可以看作是单继续的扩展,使得子类可以或许从多个父类中获取属性和方法。
①语法:
class 子类名 : 继续方式 父类1,继续方式 父类2 { }
②多继续的构造函数执行顺序:
先执行父类的构造函数,再执行子类本身的构造函数。父类构造函数的执行顺序取决于定义子类时父类出现的顺序,与子类构造函数中成员初始化列表的顺序无关。
析构函数的执行顺序与构造函数的执行顺序相反。
③定名冲突:
当多个父类中存在同名成员时,直接访问该成员会产生定名冲突。此时需要加上作用域来区分。
  1. class Base1 {
  2. public:
  3.         int value;
  4.         Base1() {
  5.                 value = 10;
  6.         }
  7. };
  8. class Base2 {
  9. public:
  10.         int value;
  11.         Base2() {
  12.                 value = 20;
  13.         }
  14. };
  15. class Derived :public Base1, public Base2 {
  16. };
复制代码
  1. Derived d1;
  2. //cout << d1.value << endl;  //错误
  3. cout << d1.Base1::value << endl;  //10
  4. cout << d1.Base2::value << endl;  //20
复制代码
④多继续的优缺点:
虽然多继续提供了灵活性,但它也增长了代码的复杂度。在C++实际开辟中不建议利用多继续。
2.菱形继续

菱形继续是指两个子类继续同一个父类,而这两个子类又被一个共同的子类继续。这种继续结构会导致数据冗余和二义性问题,因为子类会从两个不同的路径继续雷同的数据。
C++引入虚继续来办理这个问题。利用虚继续,即使两个子类都继续了某个父类,该父类也只会在最终的子类中出现一次。要实现虚继续,需要在父类的继续声明前加上virtual关键字。
  1. class A {
  2. public:
  3.         int value;
  4.         A() {
  5.                 value = 10;
  6.         }
  7. };
  8. class B1 :virtual public A {
  9. };
  10. class B2 :virtual public A {
  11. };
  12. class C :public B1, public B2 {
  13. };
复制代码
  1. C c1;
  2. cout << c1.value << endl;  //10
复制代码
 
 
 
 
 
 
 
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表