继承
一个类,继承另一个已有的类。(在一个已存在的类的基础上建立一个新的类,并拥有其特性)
是一个父类(基类)派生出子类(派生类)的过程。
派生类往往是基类的具象化,基类则是派生类的抽象。
基类与派生类是相对的,一个类可能存在又是基类又是派生类的情况。
继承提高了代码的复用性。
在实际的使用中,派生类会做出一些与基类的差异化:
● 修改继承来自的基类内容
属性:公有属性直接修改,私有属性通过接口修改
函数:函数隐藏,通过派生类实现一个同名同参数的函数,来隐藏基类的函数。
● 新增派生类的内容
cpp
#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:
cpp
#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:补充基类的无参构造函数
cpp
#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:透传构造
需要 在子类构造函数的初始化列表中 显性调用父类的构造函数 :
当父类中只有有参构造,子类在创建类对象时,必须手动调用父类的构造函数。
cpp
#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++支持 不用在子类中再写一遍父类的构造函数 ------
cpp
using Father::Father; // 在子类中使用父类的构造函数
直接使用继承构造的方式,不能对子类成员初始化 。
继承构造本质上并不是把父类中的构造函数继承给子类,编译器自动根据父类中构造函数的格式,提供出派生的构造函数(个数、参数都和父类中的构造函数一致),主要还是通过透传构造创建父类的空间。
cpp
#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:委托构造
一个类的构造函数可以调用这个类中的另一个构造函数,但是要避免循环委托 。
委托构造的性能低于透传构造,但是代码维护性更好,因为通常一个类中构造函数都会委托给能力最强(参数最多)的构造函数,代码重构时,只需要更改这个参数最多的构造函数即可。
只有一个类
并不直接通过无参构造实例化对象,而是无参构造委托有参构造,从而实例化对象。
cpp
#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;
}
父子类的继承
cpp
#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 对象到父类的拷贝构造中。
cpp
#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;
}
继承时构造和析构的时机
子类在继承父类时,会先把父类中的成员保留一份,再来创建子类自己的成员。
父类先构造,子类后构造。子类先析构,父类后析构。
cpp
#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;
}
当父子类中存在同名成员
访问时不会发生冲突,默认访问子类的成员变量。
cpp
#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、当多个父类中包含同名成员
多个父类中包含同名成员,通过 域限定符 访问指定的父类中成员。
cpp
#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、如果公共基类过大,会造成汇集子类中的代码 膨胀 / 冗余。
cpp
#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; // 100
d0.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; // 会发生二义性,因为有两条路径都可以访问到 A
return 0;
}
虚继承(virtual)
虚继承,指对公共基类 的虚继承。
主要用于解决菱形继承问题,采用虚继承后,公共基类中的内容,只会在汇集子类中存在一份,在汇集子类中,可以通过任意一条路径访问到公共基类中的成员。在中间子类继承时加 virtual。
虚继承解决二义性主要通过虚基类指针和虚基类表实现,下例中的 类B、类C 会分别创建一个虚基类指针和虚基类表。所有同类型对象共用一张虚基类表,每个对象内部增加一个隐藏的虚基类指针成员变量,这个虚基类指针指向虚基类表。派生类D 中,也有隐藏的虚基类指针,但是没有自己虚基类表。在调用 A的成员时, D对象 会通过虚基类指针找到对应的虚基类表,通过查表避免二义性。
------> Solution
cpp
#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;
}
为每一个类添加构造函数
cpp
#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;
}
cpp
#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 在继承时的顺序。