初识C++ · 多态(2)

目录

前言:

1 抽象类

2 单继承与多继承中的虚函数表

2.1 单继承中的虚表

2.2 多继承中的虚表

3 菱形继承 菱形虚拟继承


前言:

多态(1)好像也不是很难?不不不,这里的菱形(虚拟)继承的多态是有点难度的,所以,打起精神来!!!


1 抽象类

抽象类是这个类里面包含了纯虚函数,就叫做抽象类,那么什么是纯虚函数呢?

class Car
{
public:
	virtual void Func() = 0;
};

在虚函数后面加一个 =0;,这就是纯虚函数了,此时这个类叫做抽象类,抽象类的特点是不能实例化对象。

那么继承了抽象类的类能否实例化呢?

class Car
{
public:
	virtual void Func() = 0;
};

class Benz :public Car
{
public:

};

在我没有重写虚函数之前,是不能实例化对象的,只有重写了虚函数,才可以重写虚函数:

class Car
{
public:
	virtual void Func() = 0;
};
class Benz :public Car
{
public:
	virtual void Func()
	{
		cout << "hahaha" << endl;
	}
};
int main()
{
	//Car c1;
	Benz b1;
	b1.Func();
	return 0;
}

所以继承了抽象类的派生类必须要重写虚函数,那么这里呢,我们就可以知道抽象类其实变相的相当于强制要求重写虚函数。这里和override是有些不同的,override是检查有没有重写,抽象类是强制要求重写,二者是有差别的。

既然基类不能实例化了,是不是基类这里就没什么用了?不是的,子类里面不是还有基类吗?

我们就可以通过指针来访问基类,如下:

int main()
{
	Car* p1 = new Benz;
	p1->Func();
	p1->_a;
	return 0;
}

在抽象类这里引入两个概念,一个是接口继承,一个是实现继承,普通函数的继承就是继承的是实现继承,而虚函数的继承就是一种接口继承,相当于只用到了它的声明,真正的实现还是要用到重写后的函数体。


2 单继承与多继承中的虚函数表

2.1 单继承中的虚表

单继承中的虚表在多态(1)里面已经提及到了,这里呢就简单过一下。

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base:Func1()" << endl;
	}	
	virtual void Func2()
	{
		cout << "Base:Func2()" << endl;
	}	
	void Func3()
	{
		cout << "Base:Func3()" << endl;
	}	
};
class Drive :public Base
{
public:
	virtual void Func1()
	{
		cout << "Drive:Func1()" << endl;
	}
	virtual void Func3()
	{
		cout << "Drive:Func3()" << endl;
	}
};

单继承的多态,原理就是传指针或者引用的时候,参数指向的是不同的虚表指针,

根据不同的虚表指针,不同的指令,来调用不同的函数,当然,这里的部分函数是被编译器隐藏了的,上篇提到过,这里不再说了就。

2.2 多继承中的虚表

class Base1 
{
public:
	virtual void func1() { cout << "Base1::func1" << endl; }
	virtual void func2() { cout << "Base1::func2" << endl; }
private:
	int b1;
};

class Base2 
{
public:
	virtual void func1() { cout << "Base2::func1" << endl; }
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int b2;
};

class Derive : public Base1, public Base2 
{
public:
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func3" << endl; }
private:
	int d1;
};

多继承的多态,我们先从一个问题开始入手,sizeof(Derive)的大小是多少?(在X86的环境下)

答案是20,是不是有点匪夷所思了?可能有答案是8,说Derive里面有一个虚表指针,一个int类型,所以是8。

这里呢,是因为有两个虚表指针,一个存放在Base1 一个存放在Base2里面,有人问,为什么派生类里面没有虚表指针?

观察最前面的代码就可以发现,子类有虚函数,继承的父类中有虚函数就有虚表,子类就不需要虚表。这个现象可能都没注意到,所以虚表基本上都是继承下来的父类里面的,切片的时候好访问,如果再来一个虚表,存放什么?还能构成多态吗?

所以这里,我们可以画出Derive的对象模型:

所以大小为 4+4+4+4+4,一共为20,两个指针三个整型。

在地址打印出来后可以看到,派生类中没有被重写的虚函数,是放在Base1的虚表里面的,实际上就是谁先继承谁先放了。


3 菱形继承 菱形虚拟继承

这个内容选学就好了,主要是了解即可。

class A
{
public:

	virtual void func1() { cout << "A::func1" << endl; }
	int _a;
};
class B : public A
{
public:
	virtual void func2() { cout << "B::func2" << endl; }
	int _b;
};
class C : public A
{
public:
	virtual void func3() { cout << "C::func3" << endl; }
	int _c;
};
class D : public B, public C
{
public:
	virtual void func4() { cout << "D::func4" << endl; }
	int _d;
};
int main()
{
	D d;
	cout << sizeof(d) << endl;
	return 0;
}

目前是菱形继承,请问sizeof(d)的大小是多大?

我们画出d的对象模型就知道了,最顶层是b,b里面有一个a,b的下面有c,c的里面有一个a,加上两个虚表指针,这里d也是应该没有虚表指针的,虚函数的地址应该是防止B里面,所以打印出来的结果就应该是28。

实际上菱形继承在这里和多继承中的多态是没有啥差别的,可以说是一模一样的。

难的实际上是菱形虚拟继承:

class A
{
public:

	virtual void func1() { cout << "A::func1" << endl; }
	int _a;
};
class B : virtual public A
{
public:
	virtual void func2() { cout << "B::func2" << endl; }
	int _b;
};
class C : virtual public A
{
public:
	virtual void func3() { cout << "C::func3" << endl; }
	int _c;
};
class D : public B, public C
{
public:
	virtual void func4() { cout << "D::func4" << endl; }
	int _d;
};

还是从sizeof(D)的大小是多少入手。

sizeof(D)的大小是36,没想到吧?除了4个整型,B和C的虚表指针,还有什么呢?

这里直接来看D的对象模型:

int main()
{
	D d;
	cout << sizeof(d) << endl;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

蓝色部分是B,红色部分是C,绿色部分是A,蒙了吧哈哈哈。这里直接说结论了,想要证明就写代码试试,毕竟挺绕的。

B里面有一个虚表指针,一个虚基表指针,虚基表指针是第二个(存放偏移量的),一个整型,C里面同理,A里面也有一个虚表指针,因为A是共享的,所以B和C的虚函数就不能放到A里面去,A的虚表指针存放的是A自己的虚函数。

所以大小是36,实际生活中不要写菱形继承,太麻烦了。


感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值