16、成员初始化列表为什么比赋值初始化快
赋值初始化:通过在函数体内进行赋值初始化,是在所有的数据成员被分配内存空间后才进行的
列表初始化:在冒号后使用初始化列表进行初始化,是给数据成员分配内存空间时就进行初始化。就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),那么分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行。
无默认构造函数的成员,const 成员,引用成员必须通过初始值列表初始化
为什么快呢?
赋值初始化是在构造函数当中做赋值的操作,而列表初始化是做纯粹的初始化操作。我们都知道, C++的赋值操作是会产生临时对象的。临时对象的出现会降低程序的效率。
17、explicit关键字的作用⭐️
在c++种explicit关键字只能用来修饰构造函数, explicit用来防止由构造函数定义的隐式转换。使用explicit可以禁止编译器自动调用拷贝初始化,还可以禁止编译器对拷贝函数的参数进行隐式转换。
class A{
public:
A(int x){
cout<<"我被用了"<<endl;
}
//如果只想用f(1)而不是f(A(1)),那就用explicit A(int x){}。
};
void f(A a)
{
}
int main( ){
f(1);// 被隐式转换为f(A(1)) ,本来是1却被自动调用了A(1)这就是拷贝初始化
//输出:"我被调用了"
return 0;
}
18、构造函数、析构函数的执行顺序
1) 构造函数顺序
①先调用基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序。
②再调用成员类对象构造函数。如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。
最后派生类构造函数。
2)析构函数顺序
①调用派生类的析构函数;
2调用成员类对象的析构函数;
3调用基类的析构函数。
19、虚函数和纯虚函数的区别
20、多态的实现
(1)编译时的多态性:编译时的多态性是通过重载(overload)来实现的。对于非虚的成员来说。系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。
(2)运行时的多态性:运行时的多态性就是直到系统运行时,才根据实际情况决定实现何种操作。C++ 中,运行时的多态性通过虚函数实现。
21、 变量的声明和定义有什么区别
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。
一个变量可以在多个地方声明,但是只在一个地方定义。
加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
22、 sizeof 和 strlen 的区别
sizeof 和 strlen 有以下区别:
1、 sizeof 是一个操作符,strlen 是库函数。
2 、sizeof 的参数可以是数据的类型,也可以是变量,而 strlen 只能以结尾为‘\0‘的字符串作参数。
3 、编译器在编译时就计算出了 sizeof 的结果。而 strlen 函数必须在运行时才能计算出来。并且 sizeof 计算的是数据类型占内存的大小,而 strlen 计算的是字符串实际的长度。
4 、数组做 sizeof 的参数不退化,传递给 strlen 就退化为指针了。
23、 拷贝构造函数和赋值运算符的区别
拷贝构造函数和赋值运算符重载有以下两个不同之处:
(1) 拷贝构造函数生成新的类对象,而赋值运算符不能。
(2) 由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉注意:当有类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。
24、 C++文件编译与执行的四个阶段⭐️
1)预处理:根据文件中的预处理指令来修改源文件的内容
2)编译:编译成汇编代码
3)汇编:把汇编代码翻译成目标机器指令
4)链接:链接目标代码生成可执行程序
25、 volatile关键字在程序设计中有什么作用
volatile是“易变的”、“不稳定”的意思。 它用来解决变量在“共享”环境下容易出现读取错误的问题。
26、 线程安全和线程不安全
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可以使用,不会出现数据不一致或者数据污染。
线程不安全就是不提供数据访问保护,有可能多个线程先后更改数据所得到的数据就是脏数据。