C++继承和多态中常考到的笔试面试题

如何定义一个不能被继承的类?

方式一:基类构造函数私有化,派生类对象实例化时,无法调用构造函数

方式二:使用final

class Person final
{
public:
	Person()
	{

	}
protected:
	int _a;
};

class Student : public Person
{

};

int main()
{
	Student s;
}

什么是菱形继承?菱形继承的问题是什么?

 菱形继承是多继承的一种特殊情况,菱形继承的问题是数据冗余和二义性

什么是菱形虚拟继承,如何解决数据冗余和二义性的

菱形虚拟继承是在菱形继承的基础上在中间的继承中的前面加关键字virtual。

菱形虚拟继承是为了解决菱形继承中数据冗余和二义性问题,使用了虚基表,例如上图中如果没有使用虚继承,那么Student和Teacher中各自都继承了一份person,而到了Assistant又同时继承了Student和Teacher,所以在Assistant中就有两份person,这就造成了数据冗余,当Assistant需要访问person中的内容时,不知道是访问Student中的person还是Teacher中的person,这就造成了二义性。在使用了虚拟继承之后,在Assistant中将person单独拿出来了对于student和teacher来说就成为了一个公共区域,那么Student和Teacher不在存放person,而是存放一个指针,这个指针指向的是虚基表,虚基表中存放的是student和Teacher分别到person的偏移量,这就解决了数据冗余和二义性的。

多态和继承的程序题

#include<iostream>
using namespace std;

class A {
public:
	A(const char* s) { cout << s << endl; }
	~A() {}
};
class B :virtual public A
{
public:
	B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class C :virtual public A
{
public:
	C(const char* s1, const char* s3) :A(s1) { cout << s3 << endl; }
};
class D :public B, public C
{
public:
	D(const char* s1, const char* s2, const char* s3, const char* s4) 
		:B(s1, s2)
		,C(s1, s3)
		,A(s1)
	{
		cout << s4 << endl;
	}
};
int main() 
{
	D* p = new D("class A", "class B", "class C", "class D");
	delete p;
	return 0;
}

1、上面程序输出结果是什么(A)

A:class A class B class C class D         B:class D class B class C class A
C:class D class C class B class A         D:class A class C class B class D

解释分析

派生类对象初始化先调用基类构造再调派生类构造。

派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main(){
Derive d;
Base1* p1 = &d;
Base2* p2 = &d;
Derive* p3 = &d;
return 0;
}

 2、多继承中指针偏移问题,对于上面代码下列选项中说法正确的是(C)

A:p1 == p2 == p3        B:p1 < p2 < p3         C:p1 == p3 != p2          D:p1 != p2 != p3

解释分析

这里就涉及到了继承的概念,对于多态不熟悉的话先可以看看这篇文章——C++继承详解

先来看看d的内存对象模型C++继承详解

在派生列表中谁在前就先继承谁,所以在上面的代码中先继承Base1再继承Base2(可以用VS2022的监视窗口进行验证)。

首先要知道由派生类赋值给基类对象的指针,会发生切片,也就是说将派生类中基类的那一部分切给基类对象的指针。

指针p1是基类Base1类型的指针,所以将d对象赋给指针p1时,发生切片,p1就指向了d对象中Base1的那一部分,同理p2指向d对象中Base2的那一部分的内容。而指针p3类型是Derive类型当然就指向自己本身。所以答案就是p1 == p2 != p3。当然如果选项中有p1 == p2 > p3那么也是对的。

class A
{
public:
	virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
virtual void test() { func(); }
};
class B : public A
{
public:
	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
	B* p = new B;
	p->test();
	return 0;
}

 3、对于上面代码,下面输出结果正确的是(B)

A: A->0        B: B->1        C: A->1        D: B->0        E: 编译出错      F: 以上都不正确
解释分析

这里就涉及到了多态的概念,对于多态不熟悉的话先可以看看这篇文章——C++多态详解

在这段代码中对func虚函数实现了重写,指针p是B类型的指针,在用p调用test函数时,test中又调用了func函数,test函数中还有个隐藏的this指针,这个this指针属于基类,用基类的指针调用虚函数,多态的两个条件正好满足,所以B中对func函数的实现会覆盖掉A中对func函数的实现,又因为虚函数的继承是接口继承,所以A中func函数的接口会被B继承下来。所以答案是B。

如有写的不对的地方还望指出,谢谢!!! 

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值