继承

1. 继承的概念

  • 继承是在现有类的基础上创建新类的一种手段,不需要知道现有类的实现
  • 现有的类称为基类,创建的新类称为派生类
  • 基类也称为父类,派生类也称为子类
  • 父类派生了子类,子类继承于父类
  • 子类可以在父类基础上添加新成员变量(属性)和新成员函数(方法),还可以重写父类成员函数的实现
  • 继承表达的是子类和父类的”is-a”关系,不满足”is-a”关系的继承是不合理的
  • 举例:学生是人,Student和Person就是继承关系
  • 继承方式:

    • public
    • protected
    • private

      2. 子类与父类的关系

  • 除了三大成员函数,子类继承了父类的全部成员变量和全部成员函数与

  • 未被继承的父类的三大成员函数是:
    • 1> 构造函数(包括拷贝构造函数)
    • 2> 析构函数
    • 3> 赋值运算符函数
  • 示例代码: ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(){ cout << “Person()” << endl; } Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

class Student : public Person{ };

class Teacher : protected Person{ };

class Coder : private Person{ };

class Kong{ };

int main(int argc, const char** argv){ cout << “sizeof(Person) : “ << sizeof(Person) << endl; cout << “sizeof(Student) : “ << sizeof(Student) << endl; cout << “sizeof(Teacher) : “ << sizeof(Teacher) << endl; cout << “sizeof(Coder) : “ << sizeof(Coder) << endl; cout << “sizeof(Kong) : “ << sizeof(Kong) << endl;

  1. Student st;
  2. st.show();
  3. Teacher th;
  4. //th.show(); // 无法调用show()
  5. Coder cd;
  6. //cd.show(); // 无法调用show()
  7. return 0;

}

  1. <a name="oG3Yv"></a>
  2. ## 3. 继承方式与访问权限
  3. - 子类继承了父类的成员,但是并没有继承相应的访问权限,其从父类继承过来的成员的访问权限,受继承方式的影响。
  4. ![](https://cdn.nlark.com/yuque/0/2022/jpeg/2902589/1647187099373-75a4e70e-8262-473d-9de3-63c916703753.jpeg)
  5. - 示例代码:
  6. ```cpp
  7. using namespace std;
  8. #include <iostream>
  9. class Person{
  10. private:
  11. string name_;
  12. bool gender_;
  13. int age_;
  14. public:
  15. Person(){
  16. cout << "Person()" << endl;
  17. }
  18. Person(string name, bool gender, int age){
  19. name_ = name;
  20. gender_ = gender;
  21. age_ = age;
  22. cout << "Person(string, bool, int)" << endl;
  23. }
  24. void show(){
  25. cout << "本人名叫" << name_ << endl;
  26. cout << (gender_ == true ? "帅哥一枚" : "美女一枚") << endl;
  27. cout << "年方" << age_ << "青春无敌"<< endl;
  28. }
  29. protected:
  30. void protectedFunc(){
  31. cout << "protectedFunc()" << endl;
  32. }
  33. };
  34. class Student : public Person{
  35. public:
  36. void testRight(){
  37. protectedFunc(); // protected
  38. //cout << name << endl; // error, 不可访问
  39. }
  40. };
  41. class Teacher : protected Person{
  42. };
  43. class Coder : private Person{
  44. };
  45. int main(int argc, const char** argv){
  46. Student st;
  47. st.show();
  48. //st.protectedFunc(); // protected
  49. st.testRight();
  50. return 0;
  51. }

4. 同名隐藏

  • 子类对象=父类部分+子类新增部分
  • 成员同名时,子类成员隐藏父类成员,包括成员变量和成员函数
  • 示例代码: ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(){ cout << “Person()” << endl; } Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

class Student : public Person{ private: int number; public: void show(){ Person::show(); // 调用父类的show(); cout << “学号 : “ << number << endl; } };

int main(int argc, const char** argv){ Student st; st.show();

  1. cout << "sizeof(Student) : " << sizeof(Student) << endl;
  2. return 0;

}

  1. <a name="VpKaD"></a>
  2. ## 5. 子类构造函数
  3. - 父类部分的初始化,由父类构造函数负责
  4. - 子类新增部分的初始化,由子类构造函数负责
  5. - 默认的子类构造函数:
  6. - 编译器会给子类提供无参构造函数,并自动调用父类的无参构造函数
  7. - 自定义子类构造函数:
  8. - 必须在子类构造函数的初始化列表中指定父类的构造函数
  9. - 构造函数的执行顺序:
  10. - 先父类构造函数,后子类构造函数
  11. - 示例代码:
  12. ```cpp
  13. #include <iostream>
  14. using namespace std;
  15. class Person{
  16. private:
  17. string name;
  18. bool gender;
  19. int age;
  20. public:
  21. Person(){
  22. cout << "Person()" << endl;
  23. }
  24. Person(string name_, bool gender_, int age_){
  25. name = name_;
  26. gender = gender_;
  27. age = age_;
  28. cout << "Person(string, bool, int)" << endl;
  29. }
  30. void show(){
  31. cout << "本人名叫" << name << endl;
  32. cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
  33. cout << "年方" << age << "青春无敌"<< endl;
  34. }
  35. };
  36. class Student : public Person{
  37. private:
  38. int number;
  39. public:
  40. Student() : Person(){
  41. cout << "Student()" << endl;
  42. }
  43. Student(string name, bool gender, int age, int number) : Person(name, gender, age){
  44. this->number = number;
  45. cout << "Student(string, bool, int, int)" << endl;
  46. }
  47. void show(){
  48. Person::show(); // 调用父类的show();
  49. cout << "学号 : " << number << endl;
  50. }
  51. };
  52. int main(int argc, const char** argv){
  53. Student st;
  54. st.show();
  55. cout << "-------------------" << endl;
  56. Student st1("rose", false, 18, 1907201);
  57. st1.show();
  58. return 0;
  59. }

6. 子类拷贝构造函数

  • 默认的子类拷贝构造函数:
    • 编译器会提供默认的拷贝构造函数,自动调用父类拷贝构造函数
  • 自定义子类拷贝构造函数:
    • 必须要在子类拷贝构造函数的初始化列表中指定父类的拷贝构造函数
  • 构造函数的执行顺序:
    • 先 父类拷贝构造函数, 后 子类拷贝构造函数
  • 示例代码: ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(){ cout << “Person()” << endl; } Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

class Student : public Person{ private: int number; public: Student() : Person(){ cout << “Student()” << endl; } Student(string name, bool gender, int age, int number) : Person(name, gender, age){ this->number = number; cout << “Student(string, bool, int, int)” << endl; }

  1. Student(const Student& that) : Person(that){
  2. number = that.number;
  3. cout << "Student(const Student&)" << endl;
  4. }
  5. void show(){
  6. Person::show(); // 调用父类的show();
  7. cout << "学号 : " << number << endl;
  8. }

};

int main(int argc, const char** argv){ Student st; st.show();

  1. cout << "-------------------" << endl;
  2. Student st1("rose", false, 18, 1907201);
  3. st1.show();
  4. cout << "-------------------" << endl;
  5. Student st2(st1);
  6. st2.show();
  7. return 0;

}

  1. <a name="vfg3R"></a>
  2. ## 7. 子类析构函数
  3. - 父类部分的清理工作由父类的析构函数负责
  4. - 子类部分的清理工作由子类的析构函数负责
  5. - 父类析构函数由子类析构函数自动调用,无须指定
  6. - 示例代码:
  7. ```cpp
  8. #include <iostream>
  9. using namespace std;
  10. class Person{
  11. private:
  12. string name;
  13. bool gender;
  14. int age;
  15. public:
  16. ~Person(){
  17. cout << "~Person()" << endl;
  18. }
  19. Person(){
  20. cout << "Person()" << endl;
  21. }
  22. Person(string name_, bool gender_, int age_){
  23. name = name_;
  24. gender = gender_;
  25. age = age_;
  26. cout << "Person(string, bool, int)" << endl;
  27. }
  28. void show(){
  29. cout << "本人名叫" << name << endl;
  30. cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
  31. cout << "年方" << age << "青春无敌"<< endl;
  32. }
  33. };
  34. class Student : public Person{
  35. private:
  36. int number;
  37. public:
  38. ~Student(){
  39. cout << "~Student()" << endl;
  40. }
  41. Student() : Person(){
  42. cout << "Student()" << endl;
  43. }
  44. Student(string name, bool gender, int age, int number) : Person(name, gender, age){
  45. this->number = number;
  46. cout << "Student(string, bool, int, int)" << endl;
  47. }
  48. void show(){
  49. Person::show(); // 调用父类的show();
  50. cout << "学号 : " << number << endl;
  51. }
  52. };
  53. int main(){
  54. Student st;
  55. st.show();
  56. cout << "-------------------" << endl;
  57. Student st1("rose", false, 18, 1907201);
  58. st1.show();
  59. return 0;
  60. }

8. 子类赋值运算符函数

  • 默认的子类赋值运算符函数:
    • 编译器会提供默认的赋值运算符函数,该函数会自动调用父类的赋值函数完成父类部分的赋值
  • 自定义赋值运算符函数:
    • 必须在函数体里显式调用父类的赋值函数
  • 示例代码: ```cpp using namespace std;

    include

class Person{ private: string name; bool gender; int age; public: ~Person(){ cout << “~Person()” << endl; } Person(){ cout << “Person()” << endl; } Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

class Student : public Person{ private: int number; public: ~Student(){ cout << “~Student()” << endl; } Student() : Person(){ cout << “Student()” << endl; } Student(string name, bool gender, int age, int number) : Person(name, gender, age){ this->number = number; cout << “Student(string, bool, int, int)” << endl; }

  1. void show(){
  2. Person::show(); // 调用父类的show();
  3. cout << "学号 : " << number << endl;
  4. }
  5. Student& operator=(const Student& other){
  6. Person::operator=(other);
  7. number = other.number;
  8. cout << "operator=(const Student&)" << endl;
  9. }

};

int main(){ Student st; st.show();

  1. cout << "-------------------" << endl;
  2. Student st1("rose", false, 18, 1907201);
  3. st1.show();
  4. cout << "-------------------" << endl;
  5. st = st1;
  6. st.show();
  7. return 0;

}

<a name="jXuTY"></a>
## 9. 转型

- 向上转型:
   - 将子类类型的对象名或者指针的引用转换为父类类型的对象或者指针或引用。这种操作范围缩小的类型转换,在编译器看来是安全的,所以可以隐式转换

![image.png](https://cdn.nlark.com/yuque/0/2022/png/2902589/1647271663486-dcb2f6fc-0922-4632-9bec-a914eaa0c94c.png#clientId=u23c5d4d7-e733-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=80&id=u39ab30d5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=80&originWidth=223&originalType=binary&ratio=1&rotation=0&showTitle=false&size=8335&status=done&style=none&taskId=u4acb3c48-196b-4050-9d92-19fe582cbc2&title=&width=223)

- 向下转型:
   - 将父类类型指针或引用转换为子类类型的指针或引用。这种操作范围放大的类型转换,编译器禁止父类对象名强制转换为子类对象名。

![image.png](https://cdn.nlark.com/yuque/0/2022/png/2902589/1647271686518-39b6d626-8b5a-44ed-a192-be9eaf87b174.png#clientId=u23c5d4d7-e733-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=137&id=u3c3eea07&margin=%5Bobject%20Object%5D&name=image.png&originHeight=137&originWidth=656&originalType=binary&ratio=1&rotation=0&showTitle=false&size=48928&status=done&style=none&taskId=u51740ee7-4188-47e4-b15f-1ecc9fb60e7&title=&width=656)

- 示例代码:
```cpp
#include <iostream>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int age;
    public:
        Person(){
            cout << "Person()" << endl;
        }
        Person(string name_, bool gender_, int age_){
            name = name_;
            gender = gender_;
            age = age_;
            cout << "Person(string, bool, int)" << endl;
        }
        void show(){
            cout << "本人名叫" << name << endl;
            cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
            cout << "年方" << age << "青春无敌"<< endl;
        }
};

class Student : public Person{
    private:
        int number;
    public:
        Student() : Person(){
            cout << "Student()" << endl;
        }
        Student(string name, bool gender, int age, int number) : Person(name, gender, age){
            this->number = number;
            cout << "Student(string, bool, int, int)" << endl;
        }

        void show(){
            Person::show(); // 调用父类的show();
            cout << "学号 : "  << number << endl;
        }
};


int main(){
    cout << "-------向上转型" << endl;
    Student tom("tom", true, 18, 1907201);
    Person ps = tom;

    Person* pps = &tom;

    Person& rps = tom;

    cout << "-------向上转型" << endl;
    Person jerry("jerry", true, 19);

    Student* pst = reinterpret_cast<Student*>(&jerry);
    pst->show();

    Student& rst = reinterpret_cast<Student&>(jerry);
    rst.show();

    //Student st = static_cast<Student>(jerry); // error

    return 0;
}

10. 多重继承

  • 概念:一个子类继承多个父类

image.png

  • 钻石形继承:
    • 多个父类源自同一个”爷类”,称为钻石形继承
    • “爷类”在子类中存在多个副本,通过子类对象访问”爷类”成员时会因为存在多个继承路径导致结果是随机的

image.png

  • 虚继承:
    • 为了确保子类中只有一个”爷类”的副本,将父类的继承类型改为虚继承(解决钻石形继承问题的办法)。

image.png

  • 示例代码: ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(){ cout << “Person()” << endl; } Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

class Student : virtual public Person{ public: void study(){ cout << “擅长学习” << endl; } };

class Teacher : virtual public Person{ public: void teaching(){ cout << “擅长教学” << endl; }

};

class Assistant : public Student, public Teacher{ };

int main(){ Assistant ast; ast.teaching(); ast.study();

cout << "sizeof(Person) : " << sizeof(Person) << endl;
cout << "sizeof(Student) : " << sizeof(Student) << endl;
cout << "sizeof(Teacher) : " << sizeof(Teacher) << endl;
cout << "sizeof(Assistant) : " << sizeof(Assistant) << endl;
return 0;

}

<a name="x0U4D"></a>
## 11. 组合

- 在一个类中使用另一个类的对象作为成员,称为**_组合_**
- 实际开发中多继承使用较少,因为它的开发和调试难度都较大,通常使用单继承+组合来替代多继承实现相同功能
- 示例代码:
```cpp
#include <iostream>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int age;
    public:
        Person(){
            cout << "Person()" << endl;
        }
        Person(string name_, bool gender_, int age_){
            name = name_;
            gender = gender_;
            age = age_;
            cout << "Person(string, bool, int)" << endl;
        }
        void show(){
            cout << "本人名叫" << name << endl;
            cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
            cout << "年方" << age << "青春无敌"<< endl;
        }
};

class Student :  public Person{
    public:
        void study(){
            cout << "擅长学习" << endl;
        }
};

class Teacher :  public Person{
    public:
        void teaching(){
            cout << "擅长教学" << endl;
        }

};

class Assistant : public Student{
    private:
        Teacher t;
    public:
        void teaching(){
            t.teaching();
        }
};

int main(){
    Assistant ast;
    ast.teaching();
    ast.study();

    return 0;
}