C++继承多态面试题

(一)类的编译顺序,构造顺序,析构顺序

类的编译顺序:类名->成员名->成员方法体
类的构造顺序:成员对象->类对象
子类构造顺序:父类->子类
子类析构顺序:子类->父类

(二)重载,隐藏,覆盖

重载:函数名相同 参数列表不同 作用域要相同
隐藏:子类隐藏父类中同名的成员方法
覆盖:子类覆盖父类中相同的虚成员方法 (相同表示 同名 同返回值 同参数列表)

(三)四种类型强转
(1)const_cast:去掉const属性的类型强转
如:const int a = 1; int b = const_cast(a);  //去掉a修饰的const
(2)static_cast:编译器认为安全的
如:int a; double b = static_cast(a);
(3)dynamic_cast:运行时期的类型强转,依靠RTTI(重要)
1.必须是指针
2.必须是继承关系的类型指针
3.有RTTI(存在于虚表里,运行时的类型),即有虚表

(4)reinpreter_cast:强制转换 (int)a

(四)虚表什么时候产生?存放在哪里?
答:存放在.rodata段,数据段编译期生成

(五)静多态,动多态                                                                                                                                                                    静多态:编译时期决定的多态   如:重载(编译期调用),模板
动多态:运行时期的多态,运行时才知道调用哪个成员方法。如:继承中的多态

(六)动多态调用的条件:
1.利用指针或引用调用
2.调用该类型下的虚函数
3.对象完整(完整表示构造函数之后~析构函数之前)

(七)基类指针指向指针对象,指针是什么类型? *指针是什么类型(基类中有虚函数)
父类:Base      子类:Drive
指针:class Base*类型
*指针:class Drive类型

class Base
{
public:
	virtual void show()
	{
		cout<<"Base::show()"<

(八)基类存在虚函数,子类覆盖,子类的内存布局

 Base::
         vfptr
         val
 Derive::val
 //子类继承父类的所有成员变量和方法,加上子类特有的成员变量和方法

(九)基类无虚函数,子类有虚函数,子类的内存布局,基类指针指向堆上的子类对象,delete指针,崩溃,重载子类new基类delete打印指针值,指针指向子类对象基类开始部分

class Base
{
public:
	void show()
	{
		cout << "Base::void show()" << endl;
	}
	void operator delete(void *p)
	{
		cout << p << endl;
		free(p);
	}
};
class Derive : public Base
{
public:
	virtual void show()
	{
		cout << "Derive :: virtual void show()" << endl;
	}
	void *operator new(size_t size)
	{
		void *p = malloc(size);
		cout << p << endl;
		return p;
	}
};
/*
vfptr
Base::-------------
Derive::
*/
int main()
{
	Base * pb = new Derive();
	pb->show();
	delete (pb-4);
	return 0;
}

(十)基类定义虚函数和普通重载函数,子类定义同名函数隐藏普通函数和函数

#include 
using namespace std;

class Base
{
public:
	//以下两种方法构成重载
	void show()
	{
		cout<<"Base::void show()"<show();         //Base:void show()
	base->show(10);       //Base:virtual void show(10)
	derive->show();       //Derive:void show()
	derive->Base::show(); //Base:void show()

	return 0;
}

(十一)基类指针指向堆上的子类对象,此时delete指针,子类析构函数未调用,有可能造成内存泄漏,基类析构函数写成虚函数

class Base
{
public:
	Base()
	{
		cout << "Base()" << endl;
	}

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

class Derive : public Base
{
public:
	Derive()
	{
		cout << "Derive()" << endl;
	}
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
};

int main()
{
	Base* base = new Derive();
	delete base;
	return 0;
}
//Base()
//Derive()
//~Derive()
//~Base()

(十二)基类构造清除所有数据,基类指针调用自己的虚函数,子类调用虚函数

class Base
{
public:
	Base()
	{
		memset(this, 0, sizeof(Base));   //vfptr 00000000
	}

	virtual void show()
	{
		cout << "Base::virtual void show()" << endl;
	}
};
class Derive : public Base
{
public:
	Derive()
		:Base()
	{
	}
	void show()
	{
		cout << "Derive::void show()" << endl;
	}
};

int main()
{
	//Base *pb = new Base();
	//pb->show();error
	//执行出错。当基类构造的时候将空间内所有字节都置为0,包括vfprt
        //当访问虚函数的时候就会进入地址为0的空间,访问出错。

	Derive* pd = new Derive();
	pd->show();
	//正常执行,虽然基类构造的时候将空间内所有字节包括vfptr都置为0
        // 但是子类在构造的时候将vfprt又置为自己的Show函数入口地址,因此访问正常

	return 0;
}

(十三)基类和派生类构造函数、析构函数中调用虚函数,会不会产生多态调用 
答:不会,因为此时对象不完整。对象在构造函数结束到析构函数之前是完整的。

(十四)基类虚函数放到共有,派生类虚函数放到私有,外部基类指针指向子类对象,能否派生类私有函数否访问?
答:可以,因为该指针是基类的指针,但是其又可以指向子类的示例,因此在调用之前(进入虚函数表中函数地址之前)无法确定调用的究竟是谁。

class Base
{
public:
	virtual void show(int)
	{
		cout << "Base :: virtual void show(int)" << endl;
	}
};

class Derive : public Base
{
public:

private:
	void show(int)
	{
		cout << "Derive :: void show(int)" << endl;
	}
};

int main()
{
	Base* pb = new Derive();
	pb->show(10);
	return 0;
}

(十五)基类和子类给不同的函数参数默认值,基类指针指向子类对象,调用函数

class Base
{
public:
	virtual void show(int a = 20)
	{
		cout << "a=" << a <<"==================" <

你可能感兴趣的:(总结复习,C++)