C++——两个关于继承,多态的奇妙问题

文章目录

  • 问题一(有关菱形虚拟继承)
  • 问题二(有关多态)
  • 总结

问题一(有关菱形虚拟继承)

前言:该问题涉及菱形虚拟继承的问题,如果不知道菱形虚拟继承是什么的,可以看看博主的另一篇博客,链接如下:
C++——深究继承
这篇博客中含有从语法和底层讲解菱形虚拟继承的部分

#include
using namespace std;


class A
{
public:
	A(const char* a) { cout << a << endl; }
	~A() {}
};

class B : virtual public A
{
public:
	B(const char* a, const char* b)
		:A(a)
	{
		cout << b << endl;
	}
};

class C:virtual public A
{
public:
	C(const char* a, const char* c)
		:A(a)
	{
		cout << c << endl;
	}
};

class D : public B, public C
{
public :
	D(const char* a, const char* b, const char* c, const char* d)
		:B(a, b)
		,C(a, c)
		,A(a)
	{
		cout << d << endl;
	}
};


int main()
{
	D* p = new D("class A", "class B", "class C", "class D");
	delete p;
	return 0;
}

上面这段代码输出的结果是什么呢?我们知道,初始化列表的执行顺序是按照成员在类中的声明顺序执行的,那么在这道题中是否是先初始化B成员,再初始化C成员呢?我们首先来看一下答案,再说结论。
C++——两个关于继承,多态的奇妙问题_第1张图片
答案是,先初始化A成员,然后按顺序初始化B和C,最后才初始化D自己的成员。

那么,为什么会这样呢?答案是由于这里B和C使用的是虚拟继承,所以在D中将只会有一份A成员放在D对象的最后,也就是说D对象中的A成员是独立于B,C的。
因此,C++语法规定在使用初始化列表初始化的时候,将会优先初始化A对象(菱形继承最上面的基类对象),然后再根据继承顺序分别调用中间基类的初始化,但要注意的是,在中间基类的初始化过程中,对其基类的初始化列表将不起作用,也就是不会执行!(这点从打印结果就可以看出,并没有打印三次A类构造),并且如果D对象的初始化列表没有初始化A类对象将会报错。
在这里插入图片描述

从这里就可以看出,虚继承是非常复杂的,所以我们一定要谨慎使用虚继承,能不用就不用!

问题二(有关多态)

//下面的代码输出结果是什么?
#include
using namespace std;
class A
{
public:
	virtual void func(int val = 1) { cout << "A->" << val << endl; }
	virtual void test() { func(); }
};

class B : public A
{
public :
	void func(int val = 0) { cout << "B->" << val << endl; }
};

int main()
{
	B* p = new B;
	p->test();
	return 0;
}

这题有两个考点,一个是多态,另一个是一个非常坑的细节。

首先是第一点,p指向的是一个B类型的对象,我们首先来分析一下该题B类型对象的虚表,如下:

__vfptr
(B对象重写)B::func()
A::test()

用p调用test()之后,test()内部又用this指针调用func(),而由于该this指针是B类型的指针,所以调用的应该是重写的func()。

到了这一步之后,肯定很多人会觉得答案是B->0,但是答案真的是这个吗?
我们先来看答案:
C++——两个关于继承,多态的奇妙问题_第2张图片
没错,答案是输出B->1!!!是不是很惊讶,那么为什么输出的是这个呢?

这是因为虚函数重写的底层只重写了函数的实现,函数接口仍然用的是基类的接口,所以其缺省函数仍然是1!

总结

通过这两道题,我们更能发现c++这门语言的奇妙了,因此,在实际使用过程中,才更应该加倍小心!

你可能感兴趣的:(C++,c++,开发语言)