继承
类和类的关系有组合、继承和代理。继承的本质就是代码复用。子类继承父类中的一些东西,父类也称为基类,子类也称为派生类。派生类继承了基类除构造函数以外的所有成员。
继承的方式
继承方式有public(公有继承)、private(私有继承)和protected(保护继承)。基类中不同访问限定符下(public、protected、private)的成员以不同的继承方式继承,在派生类中的访问限定也不同,具体如下:
基类的布局优先于派生类
#include<iostream>
class Base
{
public:
Base(int a)
:ma(a)
{
}
public:
int ma;
};
class Derive :public Base
{
public:
Derive(int b)
:mb(b), Base(b)
{
}
public:
int mb;
};
int main()
{
Derive(20);
return 0;
}
我们可以在开发人员命令提示符窗口中进入当前源文件的目录,使用cl ConsoleApplication5.cpp /d1reportSingleClassLayoutDerive来查看派生类Derive的布局如下: 由图可见,基类的布局优先级高于派生类。那么派生对象的构造方式是怎么样的呢? 1.调用基类的构造函数 2.调用派生类的构造函数 派生类的析构可想而知: 1.调用派生类的析构函数 2.调用基类的析构函数
虚函数
如下程序:
class Base
{
public:
Base(int a)
:ma(a)
{}
virtual void Show()
{
std::cout << "Base:Show ma=" << ma << std::endl;
}
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int b)
:mb(b), Base(b)
{
}
void Show()
{
std::cout << "Derive:Show mb=" << mb << std::endl;
}
protected:
int mb;
};
int main()
{
std::cout << "sizeof(Base):" << sizeof(Base) << std::endl;
std::cout << "sizeof(Derive):" << sizeof(Derive) << std::endl;
Base* pb=new Derive(20);
std::cout << typeid(pb).name() << std::endl;
std::cout << typeid(*pb).name() << std::endl;
pb->Show();
return 0;
}
运行结果如下: 上面结果说明一个基类的指针是可以指向其派生类对象的。基类中含有虚函数,那么基类布局中存在一个虚函数指针,指向虚函数表;且其派生类中与其同名同参的函数不需要加virtual也是虚函数。此时基类和派生类的布局如下:
vfptr的指针大小为4(32位机器)。因此基类字节数为8,派生类为12。vfptr指针指向的vftable(虚函数表)中,&Base_meta中存放了RTTI信息(运行时类型信息),也就是class Base,0表示偏移,&Base::Show表示虚函数的入口地址。typeid()可以动态获取类型。 main函数中,生成了一个派生类对象。pb是一个指针类型,它的类型只和定义点有关,因此打印出来pb的类型为class Base类型;而pb是一个自定义类型,动态获取类型时,先通过指针pb解引用找到派生类对象,通过vfptr找到vftable,&Derive_meta中存放了派生类的RTTI信息,其中存放的是class Derive,因此*pb的类型是class Derive。 pb指针调用Show()函数时,发生了动多态。首先通过指针所指向的对象找到vfptr,再找到vftable,获取到Show函数的入口地址,此时 &Derive::Show中存放的是派生类的虚函数入口地址,因此调用的是派生类中的Show()函数。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。