C++
继承
---减少重复的代码
继承的基本语法
继承的基本语法class A : public B;
A类称为 子类 或者派生类
B类称为 父类 或者基类
派生类中的成员分为两种
一种是从基类中继承过来的,一种是自己增加的成员
class Base { public: void header() { cout << "头部" << endl; } void footer() { cout << "底部" << endl; } }; class java : public Base { public: void content() { cout << "java内容" << endl; } }; int main() { java j; j.content(); j.footer(); j.header(); return 0; }
继承的方式
继承的方式一共有三种
-
公共继承
访问权限不变 -
保护继承
父类中的public权限 在子类中变为protected的权限 -
私有继承
父类中的public,protected权限 在子类中变为private权限
总
基类的私有成员在子类都是不可见;基类的其他成员在子类的访问方式就是访问限定符和继承方式中权限更小的那个(权限排序:public>protected>private)。 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,但最好显式地写出继承方式。
class Base { public: int a; protected: int b; private: int c; }; //继承方式:公共继承 class son1 :public Base { public: void fun1() { a;//可以访问 public权限 b;//可以访问 protected权限 //c; 不可以访问 } }; void myclass() { son1 s; s.a;//只能访问公共权限的 //s.b 不能够访问protect //s.c; private 更不允许 } //继承方式:保护继承 class son2 :protected Base { public: void fun2() { a; b; //c; } }; void myclass2() { son2 s; //s.a;不能被访问 } //私有继承 class son3 : private Base { public: void fun3() { a;//可以访问私有权限 b; //c; } }; void myclass3() { son3 s; //s.a; 不能访问子类中私有的属性 } class grson : public son3 { public: void fun33() { //son3 是 私有继承 // son3 继承的Base类中的 属性的 权限 全部变成了 private // 所以继承son3 的 属性 在 grson 中 都不能够访问到 //a; } };
对三种访问修饰符的理解
-
private:只能由1.该类中的函数、2.其友元函数访问。
不能被任何其他访问,该类的对象也不能访问。
-
protected:可以被1.该类中的函数、2.子类的函数、以及3.其友元函数访问。
但不能被该类的对象访问。
-
public:可以被1.该类中的函数、2.子类的函数、3.其友元函数访问,也可以由4.该类的对象访问。
注:友元函数包括3种:设为友元的普通的非成员函数;设为友元的其他类的成员函数;设为友元类中的所有成员函数。
父类继承过来的成员,有哪些属于子类对象
-----(继承中的对象模型)
class Base { public: int a; protected: int b; private: int c; }; class son : public Base { public: int d; }; class son1 :protected Base { }; int main() { cout << sizeof(son) << endl;//16 cout << sizeof(son1) << endl;//12 }
结论:父类中的所有非静态的成员属性,都被子类继承了下来
父类中的私有成员也被子类中继承下来了,只不过是被编译器隐藏了
继承中的构造和析构函数的顺序
class Base { public: Base() { cout << "父类的构造函数调用" << endl; } ~Base() { cout << "父类的析构函数调用" << endl; } }; class son :public Base { public: son() { cout << "子类的构造函数调用" << endl; } ~son() { cout << "子类的析构函数调用" << endl; } }; void test() { son s; } int main() { test(); return 0; }
继承中先调用父类的构造函数在调用子类的构造函数 析构顺序正好相反(栈)
继承中同名成员处理方法
-
访问子类同名成员 直接访问即可
-
访问父类同名成员 需要加作用域
-
当子类中有与父类中同名的成员函数 子类中的成员函数 会隐藏到 父类中 所有同名的成员函数(包括重载)
class Base { public: int a; Base() { a = 100; } void fun() { cout << "父类中的fun()" << endl; } void fun(int a) { cout << "父类中的fun(int a)" << endl; } }; class son :public Base { public: int a; son() { a = 100; } void fun() { cout << "子类中的fun()" << endl; } /*void fun(int a) { cout << "子类中的fun(int a)" << endl; }*/ }; void test() { son s; //如果父类和子类 有同名的 成员属性 //如果不加作用域 则 直接访问的 就是 子类中的 cout << "son下的a" << s.a << endl; cout << "Base下的a" << s.Base::a << endl; s.fun(); //当子类中 有 与 父类 同名的成员函数 // 子类中同名的成员函数 会 隐藏掉 父类中 所有同名的 成员函数 包括重载的 //s.fun(10); s.Base::fun(10); s.Base::fun(); }
继承中同名静态成员处理方法
-
访问子类同名成员 直接访问
-
访问父类同名成员 加作用域访问
-
当子类中有与父类中同名的成员函数 子类中的成员函数 会隐藏到 父类中 所有同名的成员函数(包括重载)
静态成员有两种访问方式
-
通过对象访问
son s; //通过对象来访问 cout << "子类的a=" << s.a << endl; cout << "父类的a=" << s.base::a << endl;
-
通过类名访问
son s; //通过类名访问 cout << "子类的a=" << son::a << endl; //cout << "父类的a=" << base::a << endl; cout << "父类的a = " << son::base::a << endl; //第一个:: 代表 通过类名的方式访问 //第二个:: 代表 访问父类作用域下的a
class base { public: void static fun() { cout << "父类中的static fun()" << endl; } void static fun(int a) { cout << "父类中的static fun(int a)" << endl; } public: static int a; }; int base::a = 10; class son : public base { public: void static fun() { cout << "子类中的static fun()" << endl; } public: int static a; }; int son::a = 10; void test() { son s; //通过对象来访问 cout << "子类的a=" << s.a << endl; cout << "父类的a=" << s.base::a << endl; //通过类名访问 cout << "子类的a=" << son::a << endl; //cout << "父类的a=" << base::a << endl; cout << "父类的a = " << son::base::a << endl; 第一个:: 代表 通过类名的方式访问 第二个:: 代表 访问父类作用域下的a s.fun(); son::fun(); //原因同上 //s.fun(10); 子类会覆盖父类中 同名方法 s.base::fun(); s.base::fun(10); son::base::fun(); son::base::fun(10); }
多继承语法
c++中允许一个类继承多个类
语法:class 子类 :继承方式 父类1,继承方式 父类2 ....
多继承可能会引起父类中有同名成员出现,需要加作用域来区分
class base1 { public: base1() { a = 100; } public: int a; }; class base2 { public: base2() { a = 200;//如果将 变量名 设置为 b 就不冲突 设置 成 a 与 Base1 中的变量 冲突 } int a; }; class son : public base1, public base2 { public: son() { c = 100; d = 200; } int c, d; }; //多继承容易产生成员同名的情况 //可以通过作用域 来区分 是哪个基类的 成员 void test() { son s; cout << sizeof(son) << endl;//16 cout << s.base1::a << endl; cout << s.base2::a << endl; } int main() { test(); return 0; }
菱形继承(钻石继承)
概念
两个派生类继承同一个基类,又有某个类同时继承这两个派生类
存在问题
动物是基类,羊和骆驼是派生类,羊驼又继承了羊和骆驼
1.当羊驼使用数据时,会产生二义性 -------加作用域可解决
2.当羊驼继承来自动物的数据 继承了2份 我们只需要一份即可 ------- 使用虚继承解决
虚继承语法
定义: 虚拟继承是在定义派生类时将基类指明为虚基类,或者说派生类以虚拟的方式从基类派生。虚拟继承的方法是在普通继承的基类名前加上virtual关键字
class 派生类名 : 继承方式 virtual 基类名 {派生类的定义};
对于虚继承理解
vbptr 虚基类指针
vbptr 指向vbtable(虚基类表)
vbtable 中 记录了偏移量
派生类 通过 这个 偏移量 就可以 找到 父类中的 数据
class Animal//虚基类 { public: int age; }; //继承前 加 virtual 关键字 变为 虚继承 class Sheep :virtual public Animal {}; class Tuo:virtual public Animal {}; class ST :public Sheep, public Tuo{}; //ST类 继承了来自动物的数据 两份 造成了资源浪费 我们知道 只需要一份即可 //因此 对于 多重继承 我们可以 通过 增加 关键字 virtual 形成 虚继承 //对于虚继承的理解 /* 虚继承 会在派生类中 生成 虚基类指针 vbptr 该指针 指向 vbtable 虚基类表 v - virtual b - base ptr - pointer vbtable 该表中 存储着 偏移量 派生类 通过 这个 偏移量 就可以 找到 父类中的 数据 */ void test() { ST st; //Sheep 和Tuo 两个类 都继承了 animal类 //ST 继承了 sheep 和 tuo 类 //当 ST 使用数据时 会有二义性 可以用 作用域 来解决 st.Sheep::age = 200; cout << "sheep" << st.Sheep::age << endl; st.Tuo::age = 100; cout << "Tuo" << st.Tuo::age << endl; cout << "st.age = " << st.age << endl; } int main() { test(); }