先来几个常见的面试题:
1、如何实现不能被继承的类?
需要的知识点:
私有继承以后不可见,构造函数合成
解决方法:
class AA
{
public:
void func1()
{}
static AA* Getvariable1()//static 是为了没有对象也能调//无this指针就要加static
{
return new AA;//直接new出一个对象
}
static AA Getvariable2()
{
return AA();//利用拷贝构造
}
private:
AA()//拷贝构造
{
}
int _a;//变量
};
class BB:public AA
{};
int main()
{
AA*p1 = AA::Getvariable1();
AA p2 = AA::Getvariable2();
system("pause");
return 0;
}
2、如何实现一个类,定义出的对象都在堆上?
如何实现一个类,定义出的对象都在栈上?
堆:
思路:
代码:
class AA
{
public:
void func1()
{}
static AA* Getvariable1()
{
return new AA;//直接new出一个对象(对象都在堆上)
}
//static AA Getvariable2()
//{
// return AA();//利用拷贝构造
//}
private:
AA()//拷贝构造
{
}
AA(const AA& a);//构造
AA& operator=(const AA&a);//赋值运算符重载
int _a;//变量
};
int main()
{
AA* p1 = AA::Getvariable1();
AA p2 = *p1;
system("pause");
return 0;
}
栈:
方法一:
class AA
{
public:
void* operator new(size_t size);
void operator delete(void* p);
private:
int _a;//变量
};
AA a;//堆上
int main()
{
AA p1;
AA * p2 = new AA;
system("pause");
return 0;
}
有缺陷,在代码块中创建的可能不在栈上
方法二:
同堆的方法
接下来进入知识点部分:
一、虚函数:
虚函数&多态
虚函数–类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数重写–当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
多态:(1、虚函数的重写。2、对象的指针或者引用)
总结:
1. 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
协变:不构成重写,返回值类型不同,一个为父类返回值,一个为子类返回值。
2. 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3. 只有类的成员函数才能定义为虚函数。
4. 静态成员函数不能定义为虚函数。
5. 如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
6. 构造函数不能为虚函数,虽然可以将operator=定义为虚函数,但是最好不要将operator=定义为虚函数,因为容易使用时容易引 起混淆。
赋值运算符重载不会构成重写,因为返回值类型不同,最好不要定义为虚函数是因为它没有必要构成多态,没意义
7. 不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
8. 最好把基类的析构函数声明为虚函数。(why?另外析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但 是构成覆盖,这里是因为编译器做了特殊处理)
编译器将父类与子类的析构函数都处理为destructor,加上virtual后构成重写
面试题:
什么情况下把基类的析构函数定义为虚函数?
1)free delete
2)fclose
二、重载、重写、重定义、协变的区别
重载:在同一个作用域,函数名形同,参数类型或参数个数不同
重写(覆盖):在两个作用域,即基类和子类,函数名形同,返回值相同,参数个数和类型相同
协变:与重写基本相同,除了返回值类型不同
重定义(隐藏):与重写相似,不构成重写,就是重定义
三、纯虚函数: 纯虚函数让子类必须重写虚函数
在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚 函数在派生类中重新定义以后,派生类才能实例化出对象。
//抽象类、接口类
class Person
{
virtual void Display () = 0; // 纯虚函数 protected : string _name ; // 姓名
};
class Student : public Person {};
四、友元与继承
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
五、继承与静态成员
基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。