c++八股文整理(十一)

1. 什么情况会自动生成默认构造函数?

  1. 带有默认构造函数类成员对象,如果一个类没有任何构造函数,但它含有一个成员对象,而后者有默认构造函数,那么编译器就为该类合成出一个默认构造函数。不过这个合成操作只有在构造函数真正被需要的时候才会发生;
  2. 如果一个类A含有多个成员类对象的话,那么类A的每一个构造函数必须调用每一个成员对象的默认构造函数而且必须按照类对象在类A中的声明顺序进行
  3. 带有默认构造函数的基类,如果一个没有任务构造函数的派生类派生自一个带有默认构造函数基类,那么该派生类会合成一个构造函数调用上一层基类的默认构造函数;
  4. 带有一个虚函数的类
  5. 带有一个虚基类的类
  6. 合成的默认构造函数中,只有基类子对象和成员类对象会被初始化。所有其他的非静态数据成员都不会被初始化。

2. 初始化列表相关

构造函数初始化的缺点:

构造函数初始化的本质是赋值操作("="),这个方法存在两个问题,一个是比起初始化列表和就地初始化,此方式的效率偏低;第二个是可能存在错误隐患。

第一个,赋值过程中会产生临时对象,临时对象的构造析构会造成效率损耗,初始化列表的方式就避免了产生临时对象缩带来的问题。

第二个是,如果你没有重写或者禁止赋值构造函数,c++会悄悄的加上默认的赋值构造函数,这个时候也有可能带来问题。

必须使用初始化列表的时候

  • 当初始化⼀个引⽤成员变量时;
  • 初始化⼀个 const 成员变量时;
  • 当调⽤⼀个基类的构造函数,⽽构造函数拥有⼀组参数时;
  • 当调⽤⼀个成员类的构造函数,⽽他拥有⼀组参数;

最后两个可以总结为:没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

class Test1
{
public:
    Test1(int a) :i(a) {}
    int i;
};
class Test2
{
public:
    Test1 test1;
    Test2(Test1 &t1)
    {
        test1 = t1;
    }
};

以上代码无法通过编译,因为Test2的构造函数中test1 = t1这一行实际上分成两步执行:
1. 调用Test1的默认构造函数来初始化test1

2.通过赋值运算符将test1赋值给test2???

由于Test1没有默认的构造函数,所以1 无法执行,故而编译错误。正确的代码如下,使用初始化列表代替赋值操作

class Test2
{
public:
    Test1 test1;
    Test2(int x) :test1(x) {}
}

成员变量的顺序

list中的项⽬顺序是由类中的成员声明顺序决定的,不是初始化列表中的排列顺序决定的

成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的

class foo
{
public:
    int i; int j;
    foo(int x) :j(x), i(j) {} // i值未定义
};

这里i的值是未定义的因为虽然j在初始化列表里面出现在i前面,但是i先于j定义,此时j尚未初始化,所以导致i的值未定义。
 

3. 一个类中的全部构造函数的扩展过程是什么?

  1. 记录在成员初始化列表中的数据成员初始化操作会被放在构造函数的函数体内,并与成员的声明顺序为顺序;
  2. 如果一个成员并没有出现在成员初始化列表中,但它有一个默认构造函数,那么默认构造函数必须被调用;
  3. 如果class有虚表,那么它必须被设定初值
  4. 所有上一层的基类构造函数必须被调用;
  5. 所有虚基类的构造函数必须被调用。​​​​​​​

4. 构造函数的执行顺序是什么?

  1. 在派生类构造函数中,所有的虚基类及上一层基类的构造函数调用;
  2. 对象的vptr被初始化;
  3. 如果有成员初始化列表,将在构造函数体内扩展开来,这必须在vptr被设定之后才做;
  4. 执行程序员所提供的代码

5. 为什么拷贝构造函数必须传引用不能传值?

拷贝构造函数使用值传递会产生无限递归调用,内存溢出。

  1. 拷贝构造函数的作用就是用来复制对象的,在使用这个对象的实例来初始化这个对象的一个新的实例。
  2. 拷贝构造函数用来初始化一个非引用类类型对象,如果用传值的方式进行传参数,那么构造实参需要调用拷贝构造函数,而拷贝构造函数需要传递实参,所以会一直递归​​​​​​​

6. 模板和实现可不可以不写在一个文件里面?为什么?不太懂

因为在编译时模板并不能生成真正的二进制代码,而是在编译调用模板类或函数的CPP文件时才会去找对应的模板声明和实现,在这种情况下编译器是不知道实现模板类或函数的CPP文件的存在,所以它只能找到模板类或函数的声明而找不到实现,而只好创建一个符号寄希望于链接程序找地址。

模板类或函数的实现并不能被编译成二进制代码,结果链接程序找不到地址只好报错了。 《C++编程思想》第15章(第300页)说明了原因:模板定义很特殊。由template<…>处理的任何东西都意味着编译器在当时不为它分配存储空间,

它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值