C++总结(2)

C++第二次总结,结合常见问题

内存相关

C++程序内存分区

  • 栈区:函数中定义的局部变量
  • 堆区:动态分配的内存空间
  • 全局区:全局变量、静态数据、常量
  • 常量区:常量字符串
  • 代码区:代码二进制码

存储在全局区的变量什么时候进行初始化

  • C语言中程序开始运行时即完成初始化
  • C++中初始化发生在执行相关代码之前

C++中是否可以使用变量初始化静态局部变量

  • 只要变量的定义在静态局部变量执行之前即可

什么时浅复制与深复制

  • 浅复制:只是拷贝基本类型数据,而引用类型数据复制后仍指向原引用对象
  • 深复制:在计算机中开辟新内存地址存放复制对象

浅复制会带来什么问题

  • 动态分配内存的变量A浅复制给变量B,则析构时会发生两次释放内存,出现运行错误

C中的struct与C++中的struct有什么区别

  • C中struct是用户自定义数据类型;C++中的struct是抽象数据类型,支持成员函数、继承、多态
  • C中struct没有权限设置;C++中有权限控制
  • C中定义struct变量时要加上struct;C++不用

关键字相关

static的作用

  • 隐藏:修饰的函数与变量仅当前文件可见
  • 持久:修饰的变量生存期到程序结束
  • 初始化:修饰的变量默认初始化为0
  • 公用:类中被修饰的变量和函数被该类所有对象共有

static修饰的成员函数(类中)能否使用this指针

  • 不能使用,因为static修饰的成员函数被所有对象共有,且可通过类名::直接调用,故不能使用this指针

类内使用static有什么要注意的

  • static修饰的类对象必须在类外进行初始化
  • static修饰的成员函数不能访问非static成员变量
  • static修饰的成员函数不能被virtual修饰

为什么static类对象必须在类外初始化

  • static修饰的变量在相关代码开始运行时即初始化,先于对象存在,因此需要类外初始化
  • 如果不初始化会报连接错误

为什么static成员函数不能访问非static成员变量

  • C++访问成员变量的方法是通过this指针,但static函数没有this指针,因此不能访问非static成员函数

为什么static成员函数不能被virtual修饰

  • 虚函数的实现是为每一个对象分配一个vptr指针,指向虚函数表,而vptr是通过this指针调用的,static函数没有this指针,故不能被virtual修饰

const的作用

  • 修饰变量:修饰的变量不可被改变
  • 修饰指针:可指定指针或指针指向的数据不可改变
  • 修饰引用:修饰的引用所指的对象不可改变
  • 修饰参数:修饰的变量在函数内部不可改变
  • 修饰成员函数:修饰函数不可改变成员变量

类的常对象可以访问类的非常成员函数吗

  • 不能,任何没被const修饰的成员函数被认为会修改类的成员变量

非const成员函数可以访问const对象的数据成员吗

  • 不能,任何没被const修饰的成员函数被认为会修改类的成员变量

const成员函数可以访问非const对象的数据成员吗

  • 可以访问,因为const成员函数只是不能修改成员变量,并非不能访问成员变量

使用const关键字需要注意些什么

  • const变量定义时必须初始化

const修饰指针的几种情况

  • 指针不可变:int * const p;
  • 指针指向的值不可变:int const *p;
  • 指针与指针指向的值均不可变:int const * const p;

extern的作用

  • 引入外部变量/函数:修饰的变量会自动去别的文件查找
  • 指示调用C/C++库函数:extern "C"声明函数为C函数

有全局变量了为什么还要用extern

  • 如果不同文件共同include同一个文件,则被引用文件中的变量定义会重复,使用extern可以只声明而不定义。

inline的作用

  • 内联函数,提高效率

内联函数的原理

  • 在编译期间,对调用内联函数的地方的代码替换成函数代码

类相关

构造函数与析构函数的作用

  • 构造函数起初始化值的作用,会在对象实例化时自动调用
  • 析构函数用于撤销对象的操作,无参数,无返回值,不能重载,一个类只有一个析构函数,对象销毁时会自动调用析构函数

类成员初始化的方式有哪些

  • 赋值初始化:会产生临时对象
  • 初始化列表

赋值初始化和列表初始化有什么区别

  • 赋值初始化在所有数据成员被分配内存空间后才进行
  • 列表初始化在给数据成员分配内存空间时即进行

为什么列表初始化比赋值初始化要快一些

  • 对于内置类型,不会调用构造函数,因此列表初始化与赋值初始化性能相同
  • 对于类类型,因为在进入函数体之前,所有对象已构造完成,列表初始化在进入函数体之前,因此只需要构造一次,而赋值初始化在进入函数体之后进行,需要在进行一次赋值操作,会产生临时对象,进行拷贝构造

构造函数的执行顺序

  • 虚基类构造函数
  • 基类构造函数
  • 类类型成员对象构造函数
  • 派生类自己的构造函数

解释一下什么是虚基类

  • 虚基类是为了防止重复继承而出现的机制,比如A继承于B和C,B和C均继承与D,那么A就会继承两份D的成员变量,将D声明为虚基类后,A只会继承一份

必须使用成员列表初始化的情况有哪些

  • 初始化引用成员时,因为引用不可以被赋值
  • 初始化常量成员时,因为常量不可以被赋值
  • 基类构造函数有参数时,因为没有默认构造函数,无法空参构造
  • 成员类构造函数有参数时,因为没有默认构造函数,无法空参构造

构造函数为什么不能是虚函数

  • 虚函数是通过vptr指针实现指向虚函数表的,如果构造函数是虚函数,那么必须调用vptr指针,但此时对象还没实例化,昔找不到vptr,也就无法运行

基类析构函数为什么要是虚函数

  • 为了防止内存泄漏,因为如果基类析构函数不是虚函数,那么如果派生类中申请了内存空间,并且通过基类指针指向的派生类删除时,只会调用基类的析构函数,派生类申请的空间无法释放

构造函数和析构函数中可以调用虚函数吗

  • 可以,但不提倡,构造函数和析构函数中调用虚函数,使用的是该类中定义的虚函数,不会进行动态联编,如果子类中没有定义虚函数但构造函数中调用了它,会报错

虚函数的调用关系

  • this -> vptr -> ctable -> virtual function

虚函数可以声明为inline吗

  • 不能,虚因为函数在运行时进行类型确定,而内联函数在编译时完成函数替代

泛型相关

C++模板的底层实现

  • 编译器对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译

模板为什么要经过两次编译

  • 模板函数要被是李丽华之后才能成为真正的函数,如果只进行一次编译,那么若是引入的头文件中只有声明,没有定义,那么编译器无法实例化,造成连接错误
  • © 2020 QSH
  • Powered by Hexo Theme Ayer
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信