继承:面向对象程序设计 使代码可以复用,并允许程序员在原有类特性的基础上进行扩展,增加功能。
继承定义格式
class Base
{
public:
Base()
{
cout << "B()" << endl;
}
~Base()
{
cout << "~B()" << endl;
}
public:
int pri;
protected:
int pro;
private:
int pub;
};
class Derived : public Base
{
public:
Derived()
{
cout << "D()" << endl;
}
~Derived()
{
cout << "~D()" << endl;
}
public:
int _pri;
protected:
int _pro;
private:
int _pub;
};
总结:
1.基类的private成员在派生类中是不能被访问的,基类的protected成员和public成员可以在派生类内部被访问;
2.public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用;
3.protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是has-a原则;
4.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public;
继承与转换 赋值兼容规则 public继承
1.子类对象可以赋值给父类对象;
2.父类对象不能赋值给子类对象;
3.父类指针/引用可以指向子类对象;
4.子类指针/引用不能指向父类对象。
单继承和多继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或两个以上直接父类时称这个继承关系为多继承
继承关系中的构造函数和析构函数调用顺序
#include<iostream>
using namespace std;
class C1
{
public:
C1()
{
cout << "C1()" << endl;
}
~C1()
{
cout << "~C1()" << endl;
}
public:
int _c1;
};
class C2
{
public:
C2()
{
cout << "C2()" << endl;
}
~C2()
{
cout << "~C2()" << endl;
}
public:
int _c2;
};
class D : public C1,public C2
{
public:
D()
//call C1()
//call C2()
{
cout << "D()" << endl;
}
~D()
{
cout << "~D()" << endl;
//call ~C2()
//call ~C1()
}
int _d;
};
void test()
{
D d;
}
通过上面的结果我们可以理解:
构造函数的调用顺序
基类的构造函数——>派生类对象构造函数——>派生类构造函数体
构造函数的调用顺序
派生类的析构函数——>派生类对象析构函数——>基类析构函数
菱形继承和菱形虚拟继承
D的对象中有两份B成员,菱形继承存在二义性和数据冗余的问题
下面用菱形虚拟继承解决二义性问题
#include<iostream>
using namespace std;
class B
{
public:
int _b;
};
class C1 :virtual public B
{
public:
int _c1;
};
class C2 :virtual public B
{
public:
int _c2;
};
class D :public C1, public C2
{
public:
int _d;
};
void test()
{
D d;
d._b = 0;
d._c1 = 1;
d._c2 = 2;
d._d = 3;
}
int main()
{
test();
system("pause");
return 0;
}
通过监视,我们可以看到d对象的两个_b成员值都被赋为0:
再来通过内存看一下菱形虚拟继承的对象模型
虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余和浪费空间的问题。
上图中解决二义性使用的是偏移量。