继承是复用代码的重要手段也为动态多态提供了平台
定义格式:
一、 继承关系
基类(父类)
派生类(子类)
派生类对象模型如图:
类 缺少继承权限:默认权限为private继承
结构体缺少继承权限:默认权限为public继承
#include <iostream>
using namespace std;
class Base
{
public:
Base(int a = 1)
:_b(a)
{}
~Base()
{
cout << "Base()" << endl;
}
int _B;
private:
int _b;
};
class Derive :public Base
{
public:
Derive(int a)
:Base(a)
,_d(a)
{}
~Derive()
{
cout << "Derive()" << endl;
}
private:
int _d;
};
class D :protected Base
{
public:
D(int a)
:Base(a)
, _D(a)
{}
~D()
{
cout << "D()" << endl;
}
private:
int _D;
};
class DD :private Base
{
public:
DD(int a)
:Base(a)
, _DD(a)
{}
~DD()
{
cout << "DD()" << endl;
}
private:
int _DD;
};
int main()
{
Derive a(1);
D b(1);
DD c(1);
/*Base b(1);*/
a._B = 10;
b._B = 1;
c._B = 1;
system("pause");
return 0;
}
二、
派生类构造函数的初始化链表先完成基类对象构造(调用基类的构造函数)最后构造派生类自己特有的成员
派生类先调用自己的析构函数最后调用基类析构函数
1、基类没有缺省构造函数,派生类必须要在初始化列表中显式给出基类名和参数列表。
2、基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数。
3、基类定义了带有形参表构造函数,派生类就一定定义构造函数。
否则:出现如下错误
#include <iostream>
using namespace std;
class Base
{
public:
Base(int a )
:_b(a)
{}
~Base()
{
cout << "Base()" << endl;
}
int _B;
private:
int _b;
};
class B
{
private:
int _B1;
};
class Derive :public Base
{
public:
Derive(int a)
:Base(a)
,_d(a)
{}
~Derive()
{
cout << "Derive()" << endl;
}
private:
int _d;
};
class D :public Base
{
public:
D(int a)
: _D(a)
{}
~D()
{
cout << "D()" << endl;
}
private:
int _D;
};
class DD :public B
{
public:
~DD()
{
cout << "DD()" << endl;
}
private:
int _DD;
};
int main()
{
Derive a(1);
D b1;
D b(1);
DD c;
/*Base b(1);*/
/*a._B = 10;
b._B = 1;
c._B = 1;*/
a.~Derive();
system("pause");
return 0;
}
三、继承体系中的作用域
1. 在继承体系中基类和派生类是两个不同作用域。(不能构成重载)
2. 子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问。(在子类成员函数中,可以
使用 基类::基类成员 访问)--隐藏 --重定义
3. 注意在实际中在继承体系里面最好不要定义同名的成员。
#include <iostream>
using namespace std;
class Base
{
public:
Base(int a = 1)
:_b(a)
{}
void FunTest(int a)
{ }
~Base()
{
cout << "Base()" << endl;
}
int _B;
private:
int _b;
};
class Derive :public Base
{
public:
Derive(int a)
:Base(a)
,_d(a)
{}
void FunTest()
{ }
~Derive()
{
cout << "Derive()" << endl;
}
int _B;
private:
int _d;
};
int main()
{
Derive a(1);
Base B;
/*D b1;
D b(1);
DD c;*/
/*Base b(1);*/
/*a._B = 10;
b._B = 1;
c._B = 1;*/
/*a.FunTest(a);*/// 不能形成重载
a._B = 1; // 不能给基类赋值 a.Base::_B = 1; 对基类赋值
a.~Derive();
system("pause");
return 0;
}
四、继承与转换--赋值兼容规则--public继承
1. 子类对象可以赋值给父类对象(切割/切片)
2. 父类对象不能赋值给子类对象
3. 父类的指针/引用可以指向子类对象
4. 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
友元关系不可继承
五、多继承
#include <iostream>
using namespace std;
class B1
{
public:
int _B1;
};
class B2
{
public:
int _B2;
};
class D :public B1,public B2 //继承B1和B2
{
public:
int _D;
};
int main()
{
D d;
cout << sizeof(d) << endl;
d._B1 = 0;
d._B2 = 1;
d._D = 2;
system("pause");
return 0;
}
调试得到:
对象d 的大小为3*4 = 12个字节, 取d地址, 从内存中可以看出 d的对象模型为
六、菱形继承
如图所示继承:
#include <iostream>
using namespace std;
class B
{
public:
int _B;
};
class C1:public B
{
public:
int _c1;
};
class C2:public B
{
public:
int _c2;
};
class D :public C1,public C2 //继承B1和B2
{
public:
int _D;
};
int main()
{
D d;
cout << sizeof(d) << endl;
//d._B = 0; 产生二义性问题
d.C1::_B = 0;
d.C2::_B = 1;
d._c1 = 2;
d._c2 = 3;
d._D = 4;
system("pause");
return 0;
}
调试结果如图
得到对象模型
七、虚继承
虚继承关键字 :virtual
虚继承 避免了二义性问题
#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 //继承B1和B2
{
public:
int _D;
};
int main()
{
D d;
cout << sizeof(d) << endl;
d._B = 0; // 产生二义性问题
d._c1 = 1;
d._c2 = 2;
d._D = 3;
system("pause");
return 0;
}
调试:
d的大小为24, 对象模型为:
取d的地址发现前四个字节不是某个类成员变量的值,它其实是个地址 ,在内存2中输入前四个字节的内容发现 它前四个字节为0x00 00 00 00转化为十进制就是0 接着为0x 14 00 00 00 转化为十进制就是20。
这个地址是偏移量表格的地址。
偏移量表格里面放的是 相对于自己的偏移量以及相对于基类的偏移量。
如内存2中存放的内容:
第一个字节放的为0 相对于C1偏移量为0。
第二个字节放的为20 相对于B偏移量为20 即 &d+20 得到存放基类的地址。
欢迎各位,批评指正。