C++:继承与多态(2)

继承方式:

私有继承:has_a 关系

保护、公有:is_a 关系

隐藏:(有,看不见)继承关系下,不同的作用域。

覆盖:(不存在)派生类里面的同名函数把基类的覆盖了(相当于替换)

基类和派生类的相互指向

#include<iostream>

using namespace std;

class Base//这个类中,两个Show()是重载关系
{
public:

	Base(int a) :ma(a)
	{
		cout << "Base::Base() " << endl;
	}
	~Base()
	{
		cout << "Base::~Base()" << endl;
	}
protected://可以扩展,且不能在类外访问
	int ma;
};

class Derive :public Base
{
public:
	Derive(int b) :mb(b),Base(b)
	{
		cout << "Derive::Derive() " << endl;
	}
	~Derive()
	{
		cout << "Derive::~Derive()" << endl;
	}
private:
	int mb;
};

int main()
{
	Base base(10);
	Derive derive(20);
	//Derive* pd = &base;//  ×
	Base* pb = &derive;//  √
	return 0;
}

 

解释:

            

解释:

假如把基类比作普通人,学生比作派生类,学生会做的事情一个普通的人并非会做,比如解高数。

Derive* pd = &base;// × 相当于把base(人)这个对象赋给pd(学生), 让一个普通人去做学生做的事情,比如解高数。
Base* pb = &derive;// √ 相当于把pd(学生)   这个对象赋给base(人),   让一个学生去做普通人做的事情,学生本身即是人继承来的,理所应当会。

同理:

Base& rb = derive;   //√
//Derive& rd = base; //  ×

总结:基类的指针或引用可以指向或者引用派生类对象,派生类的指针或引用不可指向或者引用基类对象。

观察下面的代码:

#include<iostream>
#include<string>
#include<vector>
using namespace std;

class Base
{
public:

	Base(int a) :ma(a)
	{
		cout << "Base::Base() " << endl;
	}
	~Base()
	{
		cout << "Base::~Base()" << endl;
	}
	void Show()
	{
		cout << "Base::Show()" << endl;
		cout << "ma " << ma << endl;
	}
protected:
	int ma;
};

class Derive :public Base
{
public:
	Derive(int b) :mb(b),Base(b)
	{
		cout << "Derive::Derive() " << endl;
	}
	~Derive()
	{
		cout << "Derive::~Derive()" << endl;
	}
	void Show()
	{
		cout << "Derive::Show()" << endl;
		cout << "ma " << ma << endl;
		cout << "mb " << mb << endl;
	}
private:
	int mb;
};

int main()
{
	cout << sizeof(Base) << endl;//4
	cout << sizeof(Derive) << endl;//8

	Base* pb = new Derive(10);
	cout << typeid(pb).name() << endl;//class Base *
	cout << typeid(*pb).name() << endl;//class Base

	pb->Show();//Base::Show()
	return 0;
}

打印结果:

                        

若将Base里面的Show()函数修改如下:

	virtual void Show()
	{
		cout << "Base::Show()" << endl;
		cout << "ma " << ma << endl;
	}

打印结果:

                 

对比观察两个图:

               

virtual 关键字带来的改变:

            

基类中是虚函数,派生类中同名且参数类型相同的函数也是虚函数。

在基类中再加入这样的代码:

	virtual void Show(int)
	{
		cout << "Base::Show(int)" << endl;
		cout << "ma " << ma << endl;
	}

                       

虚函数表:

         

虚函数指针指向虚表,这里的RTTI指的是: runtime tyoe information 运行时类型信息

内存布局中,vfptr优先级高,这里的 0 指的是vfptr相对于内存布局的偏移,所以为0。

Base* pb = new Derive(10);   *pb解引用,对象是派生类对象

                       

这里派生类  &Base::Show是带有一个整形参数是继承基类得来的。

这里有没有奇怪?为什么只有一个虚函数指针?派生类的vfptr难道不该有两个吗?

解释:派生类的向基类中的合并,合并规则:沿着继承链。一个虚表对应一个类。

    

注意:虚函数表是在编译期间确定的,编译期间进行语法语义的分析,存放在 .rodata(只读数据段),运行时vfptr指向这个表。

虚函数的机制是为了实现动多态。

早绑定、晚绑定

普通函数调用 :call函数入口地址(编译期间确定)

虚函数调用:     call寄存器(运行期间)

早绑定(静态绑定)(编译期间确定函数的调用)

晚绑定(动态绑定)(运行期间确定函数的调用)(动多态)

程序怎样拿到虚函数入口地址?

将虚函数的入口地址放到 .rodata 中, .rodata会加载到内存中,call eax时,可以找到函数入口地址。

哪些函数可以成为虚函数?

1)函数能取地址(内联函数不可以,不生成符号,无法取地址)

2)必须依赖对象调用(通过对象内存中的vfptr找vftable)(构造、static修饰的成员方法不可以)

3)析构函数,普通函数,可以设置为虚函数

动多态发生条件:

对象调用虚函数是静多态,指针调用虚函数是动多态且对象必须调用完整(也就是说,指针必须指向一个完整的对象,然后在在调用虚函数)。若在构造中调用虚函数,则对象不完整,为静多态。若在析构中调用虚函数,则对象不完整,为静多态。

观察下面的代码

#include<iostream>
#include<string>
#include<vector>
using namespace std;

class Base
{
public:

	Base(int a) :ma(a)
	{
		cout << "Base::Base() " << endl;
	}
	~Base()
	{
		cout << "Base::~Base()" << endl;
	}
	virtual void Show()
	{
		cout << "Base::Show()" << endl;
		cout << "ma " << ma << endl;
	}
	void Show(int a)
	{
		cout << "Base::Show(int)" << endl;
		cout << "ma " << ma << endl;
	}
protected:
	int ma;
};

class Derive :public Base
{
public:
	Derive(int b) :mb(b),Base(b)
	{
		cout << "Derive::Derive() " << endl;
	}
	~Derive()
	{
		cout << "Derive::~Derive()" << endl;
	}
	void Show()
	{
		cout << "Derive::Show()" << endl;
		cout << "ma " << ma << endl;
		cout << "mb " << mb << endl;
	}
	void Show(int a)
	{
		cout << "Derive::Show(int)" << endl;
		cout << "ma " << ma << endl;
		cout << "mb " << mb << endl;
	}
private:
	int mb;
};

int main()
{
	Base* pb = new Derive(10);
	pb->Show(); //动多态
	delete pb;
	return 0;
}

打印结果:

         

仔细观察,会发现没有派生类的析构。

delete pb;  //pb->~Base(); //静态绑定  只释放了基类的,没有释放派生类自己的,内存泄漏。

将基类析构函数代码改为:

	virtual ~Base()
	{
		cout << "Base::~Base()" << endl;
	}

继承关系中,基类的析构和派生类的析构是覆盖关系。(基类是虚析构,同名的派生类中析构也将变为虚析构)

调用派生类析构时,系统自动调用基类析构。

打印结果:

                       

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章