派生类继承基类中除构造函数和析构函数外的所有成员(is-a)
三种派生方式
包括公有继承、保护继承和私有继承3种,权限逐级降低
默认为私有继承
- 公有继承
基类的 public 和 protected 成员在派生类中不变,private 成员派生类不可访问 - 保护继承
基类的 public 和 protected 成员在派生类中变为 protected,private 成员派生类不可访问
(protected 对类外与private一样,不可访问) - 私有继承
基类的 public 和 protected 成员在派生类中变为 private,private 成员派生类不可访问
基类与派生类构造函数的关系
- 先执行基类构造函数,再执行派生类构造函数;先执行派生类析构函数,再执行基类构造函数
- 多重继承时按照类声明时的继承顺序依次执行构造函数,与派生类构造函数的参数列表顺序无关
- 先执行参数列表中的类构造函数,再执行成员初始化;成员初始化顺序按派生类中成员声明的顺序,与构造函数的参数列表顺序无关
#include "stdafx.h"
#include "iostream"
using namespace std;
class A
{
public:
A(int a)
{
keep = a;
cout << "A constructed: "<<a<<endl;
}
~A()
{
cout << "A de-constructed: "<<keep<<endl;
}
private:
int keep;
};
class B
{
public:
B(int b)
{
keep = b;
cout << "B constructed: "<<b<<endl;
}
~B()
{
cout << "B de-constructed: "<<keep<<endl;
}
private:
int keep;
};
class C:public B,public A
{
public:
C(int a, int b, int c, int d, int e):A(a),B(b),memberB(d),memberA(c)
{
cout << "C constructed: "<<e<<endl;
}
~C()
{
cout << "C de-constructed"<<endl;
}
private:
A memberA;
B memberB;
};
int _tmain(int argc, _TCHAR* argv[])
{
C* objC = new C(1,2,3,4,5);
delete objC;
return 0;
}
// 输出为
// B constructed: 2
// A constructed: 1
// A constructed: 3
// B constructed: 4
// C constructed: 5
// C de-constructed
// B de-constructed: 4
// A de-constructed: 3
// A de-constructed: 1
// B de-constructed: 2
- 先执行 B(b)再执行A(a),是因为继承顺序:class C:public B,public A
- 先执行memberB(d)再执行memberA(c),是因为声明顺序:private: A memberA; B memberB;
- 析构函数执行顺序相反
虚继承
对于菱形继承的模式(B1和B2继承自A,C继承B1和B2),普通继承的方式会构造两次A(B1和B2各一次)
#include "stdafx.h"
#include "iostream"
using namespace std;
class A
{
public:
A()
{
cout << "A constructed"<<endl;
}
~A()
{
cout << "A de-constructed"<<endl;
}
public:
int value;
void fun(){
cout<<"func of class A: "<<value<<endl;
}
};
class B1: public A
{
public:
B1()
{
cout << "B1 constructed"<<endl;
}
~B1()
{
cout << "B1 de-constructed"<<endl;
}
};
class B2: public A
{
public:
B2()
{
cout << "B2 constructed"<<endl;
}
~B2()
{
cout << "B2 de-constructed"<<endl;
}
};
class C:public B1,public B2
{
public:
C()
{
cout << "C constructed"<<endl;
}
~C()
{
cout << "C de-constructed"<<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
C* objC = new C();
//objC->value = 1; //会报错,因为构建了两个A,value和fun不明确
//objC->fun();
objC->B1::value = 1;
objC->B1::fun();
objC->B2::value = 2;
objC->B2::fun();
delete objC;
return 0;
}
// 输出
// A constructed
// B1 constructed
// A constructed
// B2 constructed
// C constructed
// func of class A: 1
// func of class A: 2
// C de-constructed
// B2 de-constructed
// A de-constructed
// B1 de-constructed
// A de-constructed
为了减少一次 A 的构造,节省内存,可以同时在 B1 和 B2 上虚继承 A
// 上面代码在 B1 和 B2 类声明处做如下修改
class B1:virtual public A
class B2:virtual public A
// main 函数的 objC->B2::fun() 改为
objC->B1::fun();
// 输出
// A constructed
// B1 constructed
// B2 constructed
// C constructed
// func of class A: 1
// func of class A: 2
// C de-constructed
// B2 de-constructed
// B1 de-constructed
// A de-constructed
可以看到,虽然虚继承后表面上还能以 objC->B1:: 的形式调用 fun,但实际上此时 fun 与 objC->B2是同一份拷贝
继承的赋值兼容
#include "stdafx.h"
#include "iostream"
using namespace std;
class A
{
public:
A()
{
cout << "A constructed"<<endl;
}
public:
void printA(){cout<<"A::printA()"<<endl;}
};
class B: public A
{
public:
B()
{
cout << "B constructed"<<endl;
}
public:
void printB(){cout<<"B::printB()"<<endl;}
};
int _tmain(int argc, _TCHAR* argv[])
{
B* objB = new B;
A objB2A = *objB;
objB2A.printA();
B objB2A2B = *(B*)(&objB2A);
objB2A2B.printB();
delete objB;
return 0;
}
// 输出
// A constructed
// B constructed
// A::printA()
// B::printB()
如果换为虚继承
// class B:public class A 换成
class B:virtual public class A
则 A -> B 无法转换,即
B objB2A2B = *(B*)(&objB2A); // 报错
文章《c++ 虚继承与继承的差异》提到
对于普通继承,B 与 A 是一种“is a”的关系,即B类是A类
对于虚继承,B 与 A 是一种“has a”的关系,B类有一个指向A类的虚指针
但一般来说父类转子类是不安全的,需要用 dynamic_cast 做安全检查