C++ 中的继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承类似,例如儿子继承父亲的财产。
继承(Inheritance)可以理解为一个类从另一个类获取成员变量和成员函数的过程。例如类 B 继承于类 A,那么 B 就拥有 A 的成员变量和成员函数。
在C++中,派生(Derive)和继承是一个概念,只是站的角度不同。继承是儿子接收父亲的产业,派生是父亲把产业传承给儿子。
被继承的类称为父类或基类,继承的类称为子类或派生类。“子类”和“父类”通常放在一起称呼,“基类”和“派生类”通常放在一起称呼。
派生类除了拥有基类的成员,还可以定义自己的新成员,以增强类的功能。
继承的一般语法为:
class 派生类名:[继承方式] 基类名{
派生类新增加的成员
};
例:
#includeusing namespace std; //基类 Pelple class People{ public: void setname(char *name); void setage(int age); char *getname(); int getage(); private: char *m_name; int m_age; }; void People::setname(char *name){ m_name = name; } void People::setage(int age){ m_age = age; } char* People::getname(){ return m_name; } int People::getage(){ return m_age;} //派生类 Student class Student: public People{ public: void setscore(float score); float getscore(); private: float m_score; }; void Student::setscore(float score){ m_score = score; } float Student::getscore(){ return m_score; } int main(){ Student stu; stu.setname("小明"); stu.setage(16); stu.setscore(95.5f); cout< 二、三种继承方式
- 公共继承
- 保护继承
- 私有继承
- 基类成员在派生类中的访问权限不得高于继承方式中指定的权限。
- 不管继承方式如何,基类中的 private 成员在派生类中始终不能使用(不能在派生类的成员函数中访问或调用)。
- 如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;只有那些不希望在派生类中使用的成员才声明为 private。
- 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected。
- 注意,我们这里说的是基类的 private 成员不能在派生类中使用,并没有说基类的 private 成员不能被继承。实际上,基类的 private 成员是能够被继承的,并且(成员变量)会占用派生类对象的内存,它只是在派生类中不可见,导致无法使用罢了。private 成员的这种特性,能够很好的对派生类隐藏基类的实现,以体现面向对象的封装性。
- 由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public。
class Base1 { public: int m_A; protected: int m_B; private: int m_C; }; //公共继承 class Son1 :public Base1 { public: void func() { m_A; //可访问 public权限 m_B; //可访问 protected权限 //m_C; //不可访问 } }; void myClass() { Son1 s1; s1.m_A; //其他类只能访问到公共权限 } //保护继承 class Base2 { public: int m_A; protected: int m_B; private: int m_C; }; class Son2:protected Base2 { public: void func() { m_A; //可访问 protected权限 m_B; //可访问 protected权限 //m_C; //不可访问 } }; void myClass2() { Son2 s; //s.m_A; //不可访问 } //私有继承 class Base3 { public: int m_A; protected: int m_B; private: int m_C; }; class Son3:private Base3 { public: void func() { m_A; //可访问 private权限 m_B; //可访问 private权限 //m_C; //不可访问 } }; class GrandSon3 :public Son3 { public: void func() { //Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到 //m_A; //m_B; //m_C; } };注意:在使用类创造的对象以及派生类的派生类时,注意访问权限的问题,只有public的继承方式才能访问,(小权限服从大权限)。
三、继承中的对象模型 问题:从父类继承过来的成员,哪些属于子类对象中?一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
- 注意:私有成员只是被隐蔽了,但是会被继承下去
class Base { public: int m_A; protected: int m_B; private: int m_C; //私有成员只是被隐藏了,但是还是会继承下去 }; //公共继承 class Son :public Base { public: int m_D; }; void test01() { cout << "sizeof Son = " << sizeof(Son) << endl; } int main() { test01(); return 0; }四、继承中的构造与析构顺序 子类继承父类后,当创建子类对象,也会调用父类的构造函数 问题:父类和子类的构造和析构顺序是谁先谁后?class Base { public: Base() { cout << "Base构造函数!" << endl; } ~Base() { cout << "Base析构函数!" << endl; } }; class Son : public Base { public: Son() { cout << "Son构造函数!" << endl; } ~Son() { cout << "Son析构函数!" << endl; } }; void test01() { //继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反 Son s; } int main() { test01(); return 0; }总结:在继承类中,先调用父类构造函数,在调用子类构造函数;析构函数与之相反。
五、继承同名成员处理方式 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
- 访问子类同名成员 直接访问即可
- 访问父类同名成员 需要加作用域
class Base { public: Base() { m_A = 100; } void func() { cout << "Base - func()调用" << endl; } void func(int a) { cout << "Base - func(int a)调用" << endl; } public: int m_A; }; class Son : public Base { public: Son() { m_A = 200; } //当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数 //如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域 void func() { cout << "Son - func()调用" << endl; } public: int m_A; }; void test01() { Son s; cout << "Son下的m_A = " << s.m_A << endl; cout << "Base下的m_A = " << s.Base::m_A << endl; s.func(); s.Base::func(); s.Base::func(10); } int main() { test01(); return EXIT_SUCCESS; }总结: 1. 子类对象可以直接访问到子类中同名成员 2. 子类对象加作用域可以访问到父类同名成员 3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数 六、多继承 语法: class 子类 :继承方式 父类1 , 继承方式 父类2...
- 多继承可能会引发父类中有同名成员出现,需要加作用域区分
- C++实际开发中不建议用多继承
class Base1 { public: Base1() { m_A = 100; } public: int m_A; }; class Base2 { public: Base2() { m_A = 200; //开始是m_B 不会出问题,但是改为mA就会出现不明确 } public: int m_A; }; //语法:class 子类:继承方式 父类1 ,继承方式 父类2 class Son : public Base2, public Base1 { public: Son() { m_C = 300; m_D = 400; } public: int m_C; int m_D; }; //多继承容易产生成员同名的情况 //通过使用类名作用域可以区分调用哪一个基类的成员 void test01() { Son s; cout << "sizeof Son = " << sizeof(s) << endl; cout << s.Base1::m_A << endl; cout << s.Base2::m_A << endl; } int main() { test01(); return 0; }七、菱形继承 两个派生类继承同一个基类 又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石继承。
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
- 利用虚继承可以解决菱形继承问题
class Animal { public: int m_Age; }; //继承前加virtual关键字后,变为虚继承 //此时公共的父类Animal称为虚基类 class Sheep : virtual public Animal {}; class Tuo : virtual public Animal {}; class SheepTuo : public Sheep, public Tuo {}; void test01() { SheepTuo st; st.Sheep::m_Age = 100; st.Tuo::m_Age = 200; cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl; cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl; cout << "st.m_Age = " << st.m_Age << endl; } int main() { test01(); system("pause"); return 0; }



