C++继承、虚函数处的面试题

昨天,收到 SenseTime公司面试官的电话面试(一天面了三家公司,收获挺多的),通话时间将近1个半小时,面试过程中暴露出很多知识上的漏洞,本篇文章针对面试过程中继承以及虚函数方面的知识做一总结,查缺补漏,希望对大家有帮助。

单继承下的虚函数表

//单继承下虚函数表:是如何组织的
class A{
public:
	virtual void func(){
		cout << "A::func" << endl;
	}
	virtual void funcA(){
		cout << "A::funcA" << endl;
	}
};

class B:public A{
public:
	virtual void func(){
		cout << "B::func" << endl;
	}
	virtual void funcB(){
		cout << "B::funcB" << endl;
	}
};

class C:public A{
public:
	virtual void func(){
		cout << "C::func" << endl;
	}
	virtual void funcC(){
		cout << "C::funcC" << endl;
	}
};

typedef void (*FUNC)();
int main()
{
	A a;
	B b;
	C c;
	cout << "A::虚表:" << endl;
	((FUNC)(*(int *)(*(int*)(&a))))();
	((FUNC)(*((int*)(*(int*)(&a)) + 1)))();
	cout << "-------------------------------------" << endl;

	cout << "B::虚表:" << endl;
	((FUNC)(*(int *)(*(int*)(&b))))();
	((FUNC)(*((int*)(*(int*)(&b)) + 1)))();
	((FUNC)(*((int*)(*(int*)(&b)) + 2)))();
	cout << "-------------------------------------" << endl;

	cout << "C::虚表:" << endl;
	((FUNC)(*(int *)(*(int*)(&c))))();
	((FUNC)(*((int*)(*(int*)(&c)) + 1)))();
	((FUNC)(*((int*)(*(int*)(&c)) + 2)))();
	system("pause");
	return 0;
}

问题1:三个类中都有虚函数,所以三个类均有各自独立的虚函数表

问题2:三个虚函数表的组织情况,如上图所示

问题3:B、C各自拥有自己的虚函数表,互不影响

问题4:类的对象只存储指向虚函数表的指针vfptr(一般存储在对象内存布局的最前面),虚函数表只有一份,为所有对象所共享,vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),而微软的编译器将虚函数表存放在常量段点击打开链接

typedef void(*FUNC)();
class A{
public:
	virtual void func(){
		cout << "A::func" << endl;
	}
	virtual void funcA(){
		cout << "A::funcA" << endl;
	}
private:
	int a;
};
class B{
public:
	virtual void func(){
		cout << "B::func" << endl;
	}
	virtual void funcB(){
		cout << "B::funcB" << endl;
	}
private:
	int b;
};
class C :public A, public B{
public:
	virtual void func(){
		cout << "C::func" << endl;
	}
	virtual void funcC(){
		cout << "C::funcC" << endl;
	}
private:
	int c;
};typedef void(*FUNC)();
class A{
public:
	virtual void func(){
		cout << "A::func" << endl;
	}
	virtual void funcA(){
		cout << "A::funcA" << endl;
	}
private:
	int a;
};
class B{
public:
	virtual void func(){
		cout << "B::func" << endl;
	}
	virtual void funcB(){
		cout << "B::funcB" << endl;
	}
private:
	int b;
};
class C :public A, public B{
public:
	virtual void func(){
		cout << "C::func" << endl;
	}
	virtual void funcC(){
		cout << "C::funcC" << endl;
	}
private:
	int c;
};

多继承条件下的虚函数表

//多继承条件下的虚函数表
void test()
{
	C c;
	cout << "多继承条件下的虚函数表:" << endl;
	cout << "------------------------" << endl;
	((FUNC)(*((int*)(*(int *)(&c)))))();
	((FUNC)(*((int*)(*(int*)(&c)) + 1)))();
	((FUNC)(*((int*)(*(int*)(&c)) + 2)))();
	cout << "------------------------" << endl;
	((FUNC)(*(int*)(*((int*)(&c) + 2))))();
	((FUNC)(*((int*)(*((int*)(&c) + 2)) + 1)))();
}

对象C的内存空间分布如下图所示:

多继承条件下,基类指针指向派生类后,基类指针所能访问的函数

//多继承条件下,基类指针指向派生类后,基类指针所能访问的函数
void test1()
{
	C c;
	A *pa = &c;
	B *pb = &c;
	C *pc = &c;
	cout << "基类指针pa所能调用的函数:" << endl;
	pa->func();
	pa->funcA();
	//pa->funcB();error:提示类A没有成员funcB、funcC  -->受到类型的限制
	//pa->funcC();error

	cout << "基类指针pb所能调用的函数:" << endl;
	pb->func();
	pb->funcB();
	//pb->funcA();error
	//pb->funcC();error

	cout << "派生类指针pc所能调用的函数:" << endl;
	pc->func();
	pc->funcA();
	pc->funcB();
	pc->funcC();
}

问题1:pa和pc的值相同,都是对象c的首地址,pb和pa至今相差四个字节(int  a造成的-->观察上图的内存空间分配)

问题2:基类指针指向派生类的对象,通过该基类指针所能访问的函数受类型的限制(运行时调用哪个函数受多态的影响)

3:由于多态,会访问C类的func

4:通过加作用域:pa->A::func()

多继承条件下,基类指针指向派生类对象后,基类指针之间强制类型转化之后,所能访问的函数

void test2()
{
	C c;
	A *pa = &c;
	B *pb = &c;
	C *pc = &c;

	pa = reinterpret_cast<A *>(pb);
	pa->func();
	pa->funcA();      

	//pb = reinterpret_cast<B *>(pa);
	//pb->func();
	//pb->funcB();

	//pa = reinterpret_cast<A *>(pc);
	//pa->func();
	//pa->funcA();

}

结果很奇怪(查看汇编分析)

最后一个简单地问题如下

相信你经过以上问题的分析,一定会回答出来(答案已经在图里面了^_^)

时间: 2024-07-31 05:05:58

C++继承、虚函数处的面试题的相关文章

【足迹C++primer】52、转换和继承,虚函数

转换和继承,虚函数 Understanding conversions between base and derived classes is essential to understanding how object-oriented programming works in C++. 理解基类和派生类之间的转换是必不可少的 理解面向对象编程在. Like built-in pointers, the smart pointer classes (§12.1, p. 450) support

【足迹C++primer】52、,转换和继承虚函数

转换和继承,虚函数 Understanding conversions between base and derived classes is essential to understanding how object-oriented programming works in C++. 理解基类和派生类之间的转换是不可缺少的 理解面向对象编程在. Like built-in pointers, the smart pointer classes (§12.1, p. 450) support

一道关于C++ 继承/虚函数 笔试题 [转]

转自:http://www.cnblogs.com/yangyh/archive/2011/06/04/2072393.html 首先这位作者, 因为看了这篇简短的一个博文, 我相同了关于虚函数方面的知识. #include "stdafx.h" #include "stdio.h" #include "string.h" class Father { public: name() {printf("father name\n"

PoEduo - C++阶段班【Po学校】-继承&amp;虚函数&amp;抽象&amp;接口- 课堂笔记

附录(一) 扩展知识:  1--> 面向对象的三个基本特征   1.0 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private,protected,public) 1.1 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力).可视继承(子窗体使用父窗体的外观和实现代码).接口继承(仅使用属性和方法,实现滞后到子类实现).前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式. 1.3 多态:是将

Bug:C++运行时调用纯虚函数

昨天服务器宕机,打印出的日志非常诡异,宕在纯虚函数调用处.    日志显示,战斗对象的虚函数调用,前几次正常,某个时刻过后"丧失多态"了,直接调到父类虚函数处,引发纯虚函数宕机.    且win平台下运行正常,上linux必跪,老项目linux工具不全,debug版本都编不出来,只有Log:windows下还复现不出来. 找这个bug的过程还是蛮有意思的.记录下(*^__^*) 以往没碰到过这种Bug,起初当然毫无头绪.    首先想到,c++中经常的内存改写,但能正常调用到普通虚函数

C++学习笔记——虚函数

基本概念 虚函数是在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为: virtual 函数返回类型 函数名(参数表) {函数体}: C++中用它来实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数. 虚函数实现机制 虚函数是如何做到因对象的不同而调用其相应的函数的呢? 现在我们就来剖析虚函数.我们先定义两个类 class A{//虚函数示例代码 public: virtual voidfun(){cout<<1<<end

c++ virturn function -- 虚函数

pure irtual function  -- 纯虚函数 先看例子 #include <iostream> using namespace std; class Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area() = 0 ;//{return 0;} // _vptr.Polygon show difre

虚函数重写

// 单继承虚函数无overload.cpp : Defines the entry point for the console application.// #include "stdafx.h" class Parent{public: virtual void fun1(){ } virtual void fun2(){ }}; class Sun:public Parent{public: virtual void fun3(){ } virtual void fun4(){

虚函数与多态小览

一.文章来由 Bill又写文章来由了哇~~早就想好好搞清这个问题了,这是c++领域里面比较难搞定的一块知识点,而且最近在看设计模式,里面有涉及这块,之前学过的不用容易玩忘记,于是就干脆研究透一点,也好碰到.用到的时候不心慌~于是有了这篇文章. 二.从编译时和运行时说起 2.1 编译时: 顾名思义就是正在编译的时候.就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字节码,C#中只有CLR能识别的MSIL)