继承
一个类,继承另一个已有的类。(在一个已存在的类的基础上建立一个新的类,并拥有其特性)
是一个父类(基类)派生出子类(派生类)的过程。
派生类往往是基类的具象化,基类则是派生类的抽象。
基类与派生类是相对的,一个类可能存在又是基类又是派生类的情况。
继承提高了代码的复用性。
在实际的使用中,派生类会做出一些与基类的差异化:
● 修改继承来自的基类内容
属性:公有属性直接修改,私有属性通过接口修改
函数:函数隐藏,通过派生类实现一个同名同参数的函数,来隐藏基类的函数。
● 新增派生类的内容
#include <iostream>
using namespace std;// 基类
class Father
{
private:string name = "张";public:void set_name(const string &name){this->name = name;}string get_name(){return name;}void work(){cout << "我的工作是厨师,我负责炒菜。" << endl;}
};// 派生类继承基类
class Son:public Father
{
public:void init(){set_name("王"); // 私有属性通过接口修改}void work(){cout << "我是飞行员,我要上天。" << endl;}void game(){cout << "三硝基甲苯。" << endl;}
};int main()
{Son s;cout << s.get_name() << endl; // 张s.init();cout << s.get_name() << endl; // 王s.work(); // s.game();// 调用基类被隐藏的成员函数s.Father::work();return 0;
}
继承的格式、权限
class 类名:父类名 {}; // 默认权限是 private
class 类名:继承的权限 父类名 {};
继承时类中的特殊成员函数不会被继承
构造函数
派生类中的任意一个构造函数,都必须直接或者间接调用基类的一个构造函数。
problem:
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string secret, string fname, float asset):secret(secret), firstname(fname), asset(asset){cout << "A constructor of father class with parameters. " << endl;}
};class Son:public Father
{string secret;
public:void show(){cout << "First name: " << firstname << endl;}};int main()
{// Son s1; // 找不到基类的无参构造函数 // error,因为父类只有有参构造,而子类中没有提供 能够调用父类有参构造的构造函数,不能成功创建父类的空间// Son s2("~", "0", 1); // 找不到匹配的构造函数cout << "Size of father: " << sizeof(Father) << endl;cout << "Size of son: " << sizeof(Son) << endl;return 0;
}
solution1:补充基类的无参构造函数
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father():secret("~"), firstname("w"), asset(1000000) { }Father(string secret, string fname, float asset):secret(secret), firstname(fname), asset(asset){cout << "A constructor of father class with parameters. " << endl;}
};class Son:public Father
{string secret;
public:void show(){cout << "First name: " << firstname << endl;}};int main()
{Son s1;s1.show();// Son s2("~", "0", 1); // 找不到匹配的构造函数cout << "Size of father: " << sizeof(Father) << endl;cout << "Size of son: " << sizeof(Son) << endl;return 0;
}
solution2:透传构造
需要 在子类构造函数的初始化列表中 显性调用父类的构造函数:
当父类中只有有参构造,子类在创建类对象时,必须手动调用父类的构造函数。
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with parameters. " << endl;}
};class Son:public Father
{string secret;
public: Son():Father("default string", 120000)//, secret("0") // 这里使用具体的值{ }// Father(fname, asset) 和 secret(secret) 可以交换,但有警告Son(string fname, float asset, string secret):Father(fname, asset), secret(secret){}void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s1("Xing", 120000, "secret1");s1.show();Son s2;s2.show();return 0;
}
solution3:继承构造(C++ 11 支持)
C++支持 不用在子类中再写一遍父类的构造函数 ——
using Father::Father; // 在子类中使用父类的构造函数
直接使用继承构造的方式,不能对子类成员初始化。
继承构造本质上并不是把父类中的构造函数继承给子类,编译器自动根据父类中构造函数的格式,提供出派生的构造函数(个数、参数都和父类中的构造函数一致),主要还是通过透传构造创建父类的空间。
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father():secret("~"), firstname("w"), asset(1000000){cout << "A constructor of father class with no parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(Father &other):firstname(other.firstname), asset(other.asset) // 拷贝构造{cout << "Duplicator_constructor of father class. " << endl;}
};class Son:public Father
{string secret;using Father::asset; // 把父类中公有继承下来的 asset 成员,在子类中改成私有权限using Father::Father;public:void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s0;s0.show();Son s1(120000);s1.show();Son s2("Xing", 120000);s2.show();Son s3 = s2;s3.show();return 0;
}
solution4:委托构造
一个类的构造函数可以调用这个类中的另一个构造函数,但是要避免循环委托。
委托构造的性能低于透传构造,但是代码维护性更好,因为通常一个类中构造函数都会委托给能力最强(参数最多)的构造函数,代码重构时,只需要更改这个参数最多的构造函数即可。
只有一个类
并不直接通过无参构造实例化对象,而是无参构造委托有参构造,从而实例化对象。
#include <iostream>
using namespace std;class Son
{string secret;
public:Son():Son("secret0"){cout << "A constructor of child class without parameters. " << endl;}Son(string secret):secret(secret){cout << "A constructor of child class with parameter(s). " << endl;}void show(){cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s0;s0.show();Son s1("secret1");s1.show();return 0;
}
父子类的继承
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}
};class Son:public Father
{string secret;public:Son():Son("secret0"){cout << "A constructor of child class without parameters. " << endl;}Son(string secret):Father("Xing", 120000), secret(secret){cout << "A constructor of child class with parameter(s). " << endl;}void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s0;s0.show();return 0;
}
拷贝构造函数
需要在初始化列表中显性调用父类的拷贝构造,传 other 对象到父类的拷贝构造中。
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}Father(Father &other):firstname(other.firstname), asset(other.asset){cout << "Duplicator_constructor of father class. " << endl;}
};class Son:public Father
{string secret;public:Son(string fname, float asset, string secret):Father(fname, asset), secret(secret){}Son(Son &other):Father(other), secret(other.secret){cout << "Duplicator_constructor of child class. " << endl;}void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s1("Xing", 120000, "secret1");s1.show();Son s2 = s1;s2.show();return 0;
}
继承时构造和析构的时机
子类在继承父类时,会先把父类中的成员保留一份,再来创建子类自己的成员。
父类先构造,子类后构造。子类先析构,父类后析构。
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}~Father() // 注意这里没有用虚析构函数{cout << "Destructor of father class. " << endl;}
};class Son:public Father
{string secret;
public:Son():Father("default string", 120000) // 这里使用具体的值{cout << "A constructor of child class with no parameters. " << endl;}Son(string fname, float asset, string secret):Father(fname, asset), secret(secret){cout << "A constructor of child class with parameters. " << endl;}~Son(){cout << "Destructor of child class. " << endl;}
};int main()
{Son *s = new Son;delete s;s = nullptr;cout << endl;Son *ss = new Son("z", 1200000, "0");delete ss;ss = nullptr;cout << endl;Father *sss = new Son();delete sss;sss = nullptr;cout << endl;Father *ssss = new Son("z", 1200000, "0");delete ssss;ssss = nullptr;cout << endl;return 0;
}
当父子类中存在同名成员
访问时不会发生冲突,默认访问子类的成员变量。
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float *asset;Father():asset(new float){cout << "A constructor of father class without parameters. " << endl;}~Father(){delete asset;cout << "Destructor of father class. " << endl;}
};class Son:public Father
{string secret;public:float *asset;Son():asset(new float){cout << "A constructor of child class without parameters. " << endl;}~Son(){delete asset;cout << "Destructor of child class. " << endl;}};int main()
{Son *m = new Son;*(m->asset) = 140000;cout << "*(m->asset): " << *(m->asset) << endl;cout << "*(m->Father::asset): " << *(m->Father::asset) << endl;delete m;m = nullptr;return 0;
}
多重继承
一个子类,继承自多个基类。
格式
class 类名:继承权限 父类名, 继承权限 父类名, …
{
};
1、当多个父类中包含同名成员
多个父类中包含同名成员,通过 域限定符 访问指定的父类中成员。
#include <iostream>
using namespace std;class Father
{string secret;
public:void act_1(){cout << "Earn money. " << endl;}
};class Son:public Father
{string secret;public:void act_1(){cout << "Save money. " << endl;}void act_2(){cout << "Inherit money. " << endl;}
};class Grandchild:public Father, public Son
{
};int main()
{Son s;s.act_1();s.Father::act_1();Grandchild g;g.act_2();g.Son::act_2();return 0;
}
2、菱形继承(钻石继承)
A ------> 公共基类/ \B C ------> 中间子类\ /D ------> 汇集子类
汇集子类中,会包含多份公共基类中的内容。
Problems ——>
1、会发生二义性的问题(同一个变量或者函数,可以通过两种方法访问)
2、如果公共基类过大,会造成汇集子类中的代码 膨胀 / 冗余。
#include <iostream>
using namespace std;class A
{
public:int a;
};class B:public A
{
};class C:public A
{
};class D:public C, public B
{
};int main()
{D d0;d0.C::a = 100;cout << "In C: " << d0.C::a << endl; // 100d0.B::a = 90; // 可以直接通过中间子类访问,直接访问 B 中的 a成员cout << "In B: " << d0.B::a << endl; // 90// d0.B::A::a = 80; // 以下四行报错// cout << d0.B::A::a << endl; // d0.C::A::a = 70;// cout << d0.C::A::a << endl; // 会发生二义性,因为有两条路径都可以访问到 Areturn 0;
}
虚继承(virtual)
虚继承,指对公共基类的虚继承。
主要用于解决菱形继承问题,采用虚继承后,公共基类中的内容,只会在汇集子类中存在一份,在汇集子类中,可以通过任意一条路径访问到公共基类中的成员。在中间子类继承时加 virtual。
虚继承解决二义性主要通过虚基类指针和虚基类表实现,下例中的 类B、类C 会分别创建一个虚基类指针和虚基类表。所有同类型对象共用一张虚基类表,每个对象内部增加一个隐藏的虚基类指针成员变量,这个虚基类指针指向虚基类表。派生类D 中,也有隐藏的虚基类指针,但是没有自己虚基类表。在调用 A的成员时, D对象 会通过虚基类指针找到对应的虚基类表,通过查表避免二义性。
——> Solution
#include <iostream>
using namespace std;class A
{
public:int a;
};class B:virtual public A
{
};class C:virtual public A
{
};class D:public B, public C
{
};int main()
{D d0;d0.B::A::a = 90;cout << d0.C::A::a << endl; // 成功打印 90,上一个问题得以解决return 0;
}
为每一个类添加构造函数
#include <iostream>
using namespace std;class A
{
public:int a;A(int a):a(a) {cout << "A" << endl; }
};class B:virtual public A
{
public:int b;B(int a, int b):A(a), b(b) {cout << "B" << endl; }
};class C:virtual public A
{
public:C(int a):A(a) {cout << "C" << endl; } // B 和 C 哪一个先构造,取决于 D 在继承时的顺序
};class D:public C, public B // 决定结果的顺序 为 C、B
{
public:D(int a, int b):B(a, b), C(a), A(a) {cout << "D" << endl; } // B、C 可交换顺序,结果不变
};int main()
{D d0(12, 5);cout << d0.a << '\t' << d0.b << endl;d0.B::A::a = 90;cout << d0.C::A::a << endl;return 0;
}
#include <iostream>
using namespace std;class A
{
public:int a;A(int a):a(a) {cout << "A" << endl; }
};class B:virtual public A
{
public:int b;B(int a, int b):A(a), b(b) {cout << "B" << endl; }
};class C:virtual public A
{
public:C(int a):A(a) {cout << "C" << endl; } // B 和 C 哪一个先构造,取决于 D 在继承时的顺序
};class D:public B, public C
{
public:D(int a, int b):C(a), B(a, b), A(a) {cout << "D" << endl; } // B、C 可交换顺序
};int main()
{D d0(12, 5);cout << d0.a << '\t' << d0.b << endl;d0.B::A::a = 90;cout << d0.C::A::a << endl;return 0;
}
B 和 C 哪一个先构造,取决于 D 在继承时的顺序。