36、为什么基类的析构函数需要定义为虚函数?
为了实现动态绑定,基类指针指向派生类对象,如果析构函数不是虚函数,那么在对象销毁时,就会调用基类的析构函数,只能销毁派生类对象中的部分数据,所以必须将析构函数定义为虚函数,从而在对象销毁时,调用派生类的析构函数,从而销毁派生类对象中的所有数据。
37、构造函数和析构函数能抛出异常吗?
(1)从语法的角度来说,构造函数可以抛出异常,但从逻辑和风险的角度来说,尽量不要抛出异常,否则可能导致内存泄漏。
(2)析构函数不可以抛出异常,如果析构函数抛出异常,则异常点之后的程序,比如内存释放等操作,就不会倍执行,从而造成内存泄漏的问题,而且当异常发生时,C++通常会调用对象的析构函数来释放资源,如果此时析构函数也抛出异常,即前一个异常未处理又出现新的异常,从而造成程序崩溃的问题。
38、如何是一个类不能实例化?
把类定义为抽象类,也就是使其成员函数含有纯虚函数,或者把构造函数访问权限设为private
39、const成员变量、static成员变量以及static const成员变量之间的区别
(1)const成员变量
const成员变量需要在实例化类对象时由构造函数进行初始化,一旦类对象构造完成,const成员即不可修改。
(2)static成员变量
(a)static成员变量并非类对象所有,而是类所有。可以直接使用类名加双冒号引用。
(b)它在类对象生成前就已经构造完成,可以对其进行修改。
(c)static成员变量的初始化需要放在类外进行。如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。且初始化时不需要加static。
(d)static成员变量不能在头文件初始化,只能在.cpp文件初始化。因为如果有多个文件include了这个头文件,那么static就拥有多份,这与其唯一性矛盾。
(3)static const成员变量
(a)static const和const static两种写法的效果是一样的
(b)static const 类型成员变量可以在类中初始化也可以在类外初始化,因为加上const后不能再修改,保证了static唯一性,即便多个对象创建也是只有唯一一份static const成员变量。
40、拷贝构造函数和赋值运算符重载之间有什么区别?
(1)拷贝构造函数用于构造新的对象:
Server s;
Server s1 = s; // 隐式调用拷贝构造函数
Server s2(s); // 显式调用拷贝构造函数
(2)赋值运算符重载用于将源对象的内容拷贝到目标对象中,而且若源对象中含有未释放的内存需要先将其释放
Server s;
Server s1;
s1 = s; // 使用赋值运算符
一般情况下,类中包含指针变量时需要重载拷贝构造函数、赋值运算符和析构函数。
(如果含有指针变量,可能要进行深拷贝,所以要重载拷贝构造函数;赋值运算符重载是因为需要释放原指针变量指向的内存;而析构函数也是因为需要释放内存所以要重载)
41、C++空类中有哪些成员函数?
(1)缺省的构造函数
(2)缺省的拷贝构造函数
(3)缺省的析构函数
(4)赋值运算符
(5)取值运算符
(6)取值运算符const
只有当实际使用这些函数的时候,编译器才会去定义它们。
42、拷贝初始化和直接初始化,初始化和赋值的区别?
(1)直接初始化:编译器根据我们提供的参数选择最匹配的构造函数来对对象进行初始化。
#include<iostream>
using namespace std;
class Server
{
public:
Server(){cout<<"Server()"<<endl;}
Server(int a){cout<<"Server(int a)"<<endl;}
Server(const Server&){cout<<"Server(const Server&)"<<endl;}
};
int main()
{
Server s1(10);
Server s2(s1);
}
/*
输出结果:
Server(int a)
Server(const Server&)
*/
通过上述的例子,我们可以发现,直接初始化也有可能调用拷贝构造函数,不能想当然以为直接初始化就不是拷贝初始化就不会调用拷贝构造函数。
(2)拷贝初始化:用=这个符号来定义变量时会发生拷贝初始化
Server s3 = s2; // #1
Server s4 = 3; // #2
Server s5 = Server(6); // #3
/*
输出结果:
Server(const Server&)
Server(int a)
Server(int a)
*/
(a)#1中,用s2去初始化化s3,编译器会把其优化为Server s3(s2),这样就是采用拷贝初始化
(b)#2中,会先调用构造函数Server tmp(3),生成一个临时对象,再用临时对象去初始化s4,即:Server tmp(3)–>Server s4(tmp) -->Server s4(3) == Server s4 = 3,但是编译器会一步优化到位直接看作Server s4(3),所以此处调用的是Server(int a)
(c)#3中,也是同样的道理会被编译器一步优化到Server s5(6),因此也是调用Server(int a)