面试CPP整理

如何在main函数之前执行代码?

  • 使用关键字 __attribute() void before_main(){}

CPP 多态

  • 多态:通过基类访问派生类定义的函数。多态性使得程序调用的函数实在运行时动态确定的,而不是编译时静态确定的。”一个接口,多种方法“

    • 多态的目的:多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
    • C++ 支持两种多态性
      • 编译时多态性(静态): 通过重载函数实现: 先期联编 early binding
        • 编译器编译期间可以确定函数调用的地址/形式参数(指参数的个数、类型或者顺序)必须不同
      • 运行时多态性(动态): 通过虚函数实现
        • 子类重新定义父类的做法称为覆盖(Override)
    • 虚函数: 在类成员方法的声明语句前加 virtual, virtual void func()
    • 子类可以(也可以不)重新定义基类的虚函数,该行为称之为复写(Override)
    • 当使用类的指针调用成员函数时,普通函数由指针类型决定,而虚函数由指针指向的实际类型决定
    • 通过对象内存中的虚函数指针vptr找到虚函数表vtbl,再通过vtbl中的函数指针找到对应虚函数的实现区域并进行调用
    • 虚函数指针和虚函数表
      • C++中虚函数使用虚函数表和 虚函数表指针实现,虚函数表是一个类的虚函数的地址表,用于索引类本身以及父类的虚函数的地 址,假如子类的虚函数重写了父类的虚函数,则对应在虚函数表中会把对应的虚函数替换为子类的 虚函数的地址;虚函数表指针存在于每个对象中(通常出于效率考虑,会放在对象的开始地址处), 它指向对象所在类的虚函数表的地址;在多继承环境下,会存在多个虚函数表指针,分别指向对应 不同基类的虚函数表。
  • 纯虚函数:在虚函数后加 =0virtual void func() = 0
    • 子类必须提供虚函数的个性化实现
    • 含有纯虚函数的类称之为抽象类, 不能生成对象,只能创建它的派生类的实例
  • 构造函数不能时虚函数, 析构函数可以时虚函数且最好设置为虚函数
    • 纯虚函数通常没有定义体, 但也完全可以哟个有
    • 析构函数可以时纯虚的, 但纯虚析构函数必须由定义体,因为析构函数的调用是在子类中隐含的
    • 非纯的虚函数必须有定义体,不然是个错误

CPP类型转换

  • 四种类型强制转换
    • const_cast
      • 常量指针被转化成非常量的指针,并且仍然指向原来的对象;
      • 常量引用被转换成非常量的引用,并且仍然指向原来的对象;
      • const_cast一般用于修改指针。如const char *p形式。
    • static_cast
      • static_cast 作用和C语言风格强制转换的效果基本一样,由于没有运行时类型检查来保证转换的安全性,所以这类型的强制转换和C语言风格的强制转换都有安全隐患。
      • 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。注意:进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
      • 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性需要开发者来维护。
      • static_cast不能转换掉原有类型的const、volatile、或者 __unaligned属性。(前两种可以使用const_cast 来去除)
      • 在c++ primer 中说道:c++ 的任何的隐式转换都是使用 static_cast 来实现。
    • dynamic_cast
      • dynamic_cast强制转换,应该是这四种中最特殊的一个,因为他涉及到面向对象的多态性和程序运行时的状态,也与编译器的属性设置有关.所以不能完全使用C语言的强制转换替代,它也是最常有用的,最不可缺少的一种强制转换.
      • 对于从子类到基类的指针转换 ,dynamic_cast 成功转换,没有什么运行异常,且达到预期结果。而从基类到子类的转换 , dynamic_cast 在转换时也没有报错,但是输出给 base2sub 是一个 nullptr ,说明dynami_cast 在程序运行时对类型转换对“运行期类型信息”(Runtime type information,RTTI)进行了检查. 这个检查主要来自虚函数(virtual function) 在C++的面对对象思想中,虚函数起到了很关键的作用,当一个类中拥有至少一个虚函数,那么编译器就会构建出一个虚函数表(virtual method table)来指示这些函数的地址,假如继承该类的子类定义并实现了一个同名并具有同样函数签名(function siguature)的方法重写了基类中的方法,那么虚函数表会将该函数指向新的地址。此时多态性就体现出来了:当我们将基类的指针或引用指向子类的对象的时候,调用方法时,就会顺着虚函数表找到对应子类的方法而非基类的方法。因此注意下代码中 Base 和 Sub 都有声明定义的一个虚函数 ” i_am_virtual_foo” ,我这份代码的 Base 和 Sub 使用 dynami_cast 转换时检查的运行期类型信息,可以说就是这个虚函数
    • reinterpret_cast
      • reinterpret_cast是强制类型转换符用来处理无关类型转换的,通常为操作数的位模式提供较低层次的重新解释!但是他仅仅是重新解释了给出的对象的比特模型,并没有进行二进制的转换!

空类

  • C++空类的大小不为0,不同编译器设置不一样,vs设置为1
  • 原因:

    • C++标准指出,不允许一个对象(当然包括类对象)的大小为0,不同的对象不能具有相同的地址; 带有虚函数的C++类大小不为1,因为每一个对象会有一个vptr指向虚函数表,具体大小根据指针大小确定(32位的操作系统,指针大小为4个字节);
    • C++中要求对于类的每个实例都必须有独一无二的地址,那么编译器自动为空类分配一个字节大小,这样便保证了每个实例均有独一无二的内存地址
  • 空类默认会添加哪些东西
    • Empty(); // 缺省构造函数//
    • Empty( const Empty& ); // 拷贝构造函数//
    • ~Empty(); // 析构函数//
    • Empty& operator=( const Empty& ); // 赋值运算符//
    • Empty* operator&(); // 取址运算符
    • const Empty* operator&() const; // 取址运算符 const

类成员变量布局