C++面试资料总结

C++

复习内容持续更新,或有缺陷…

malloc 和 new 的区别

mallocnew
malloc是C的库函数new是C++的运算符
malloc成功时返回的是void*,通常需要强制类型转换new成功时返回的是指向指定类型的指针
malloc是从堆上动态分配内存new是从自由存储区上为对象分配内存
malloc失败时,返回的是NULLnew失败时,会抛出bad_alloc的错误
malloc需要手动设置分配内存的大小new根据数据类型分配内存大小
malloc分配内存,不会调用对象的构造函数new分配内存会调用对象的构造函数
malloc和free相对应new和delete相对应

C++ 编译过程

  1. 预处理阶段:对源代码中包含关系(头文件),预编译语句(宏定义)进行分析和替换,生成预编译文件。
  2. 编译阶段:将预编译处理后得到的文件转换为特定的汇编代码,生成汇编文件。
  3. 汇编阶段:将编译阶段生成的汇编文件转换为机器码,生成可重定位文件。
  4. 链接阶段:将目标文件和所需要的库链接成最终的可执行文件。
  5. 静态库和动态库
    1. 静态库(.a .lib):静态库对函数库的链接是在编译时完成的,静态库进行更新,所有的程序都需要重新编译。
    2. 动态库(.so .dll):动态库对函数库的链接是在运行时加载的,系统只加载一次,多个程序公用,节省内存。

C++ 多态

  1. 多态包括编译时多态和运行时多态:

    编译时多态:函数重载和泛型编程

    运行时多态:虚函数,声明基类指针指向任意子类对象,调用相应的虚函数

  2. 每个虚函数都有虚函数表,在调用相应的函数时,并不是直接调用这个函数,而是根据虚函数表找到相应的地址,然后调用该函数。

  3. 只有声明为virtual的函数才有多态的效果,否则为原型调用。

  4. 构造函数不能够声明为virtual,因为调用虚函数的过程需要用到虚函数表,若将构造函数声明为virtual,则在构造函数调用前无虚函数表,构造函数无法调用,构成矛盾。

  5. 析构函数需声明为virtual,否则子类对象在释放内存的过程中无法动态调用子类的析构函数,造成内存泄漏。

  6. 多态的缺点:

    1. 运行效率低
    2. 造成空间浪费
  7. 静态成员函数不能是虚函数,他不属于任何对象,使用virtual会报错。访问虚函数表需要用到虚函数指针,而虚函数指针是类的成员变量,所以静态函数无法实现为虚函数。

  8. 内联函数不能是虚函数,如果内联函数被声明为virtual,则计算机会忽略inline使其变成纯虚函数。虚函数只有在运行时才知道要调用哪个函数,而内联函数在编译时就已经展开。

  9. 一个类对应一个虚函数表,一个对象有一个虚函数指针。


静态类成员函数

  1. 当成员被声明为static,则无论类有多少个对象,它在内存中都只有一份拷贝。
  2. 未初始化的static成员函数被程序自动初始化为0.
  3. 静态成员变量在文件内部是可见的,在文件的外部是不可见的。
  4. 静态变量保存在全局静态区域,包括局部静态变量。
  5. 使用静态函数的好处是,在其他文件中定义相同的名字,不会发生冲突。
  6. 类的静态成员是属于类的,而不是属于类的对象的。
  7. 在调用静态成员的时候,直接用类名和与运算符来获取。
  8. 类的静态成员函数没有this指针。
  9. 类的静态成员函数只能访问类的静态成员变量和静态成员函数,不能访问非静态的成员和函数,因为非静态的成员函数是属于类的对象的,而静态成员函数是属于类的,没有具体的对象可以操作。

拷贝构造函数和赋值函数的区别

  1. 拷贝构造函数是初始化一块内存区域,而赋值函数是对一个已经初始化的区域进行赋值操作。
  2. 拷贝构造函数大多数情况下是复制,而赋值函数则是引用。
  3. 拷贝构造函数是通过参数初始化一个对象,赋值函数则是把新对象值赋值给旧的对象,在赋值前需要检测是否是同一个对象,并且如果原先有动态分配内存,需要先将内存释放掉。

深拷贝和浅拷贝的区别

  1. 浅拷贝:如果复制的对象中有一个外部内容,那么复制这个对象的时候,让新旧两个对象指向相同的一个外部内容,就是浅拷贝。
  2. 深拷贝:制作了新的外部对象的独立复制。

C++四种强制类型转换

  1. static_cast:可实现C++内置数据类型的基本转换,但是不能进行无关指针之间的转换。
  2. const_cast:将非const数据类型转换为const
  3. reinterpret_cast:数据二进制形式重新解释,可以将任何内置数据类型转换为其他数据类型。
  4. dynamic_cast:不能用于内置数据类型的转换,可用于基类指针引用安全转换为子类指针引用,使用时,基类一定要用virtual.

智能指针

智能指针主要用来解决悬垂指针的问题,当有多个指针指向同一个对象时,如果此时某个指针释放了该对象,则其他的指针并不知道对象已经被释放掉,这个时候访问指针就会发生错误,程序容易出现崩溃。

智能指针是一个类,类的构造函数传入一个普通指针,析构函数释放传入的指针。

常用的智能指针:std::auto_ptr,unique_ptr,shared_ptr(基于计数器的智能指针),weak_ptr(弱引用,只引用不计数,需检查是否为空)


内存概念

  1. 内存溢出:在申请动态内存的时候,没有足够的空间。
  2. 内存泄漏:在使用完动态申请的内存以后,没有进行释放。
  3. 内存越界:在向内存申请一块区域后,使用的时候超出范围。

指针和引用的区别

  1. 指针是变量,只不过该变量是地址,指向内存的存储单元;

    引用是原变量的别名,与原变量实质上是相同的。

  2. 有const指针,但是没有const引用:

    const int* p = &a; // a不可变,*p=100合法
    int* const p = &a; // a可变,*p=100非法,即有const的指针
    
    const int &p = a; //正确
    int const &p = a; //错误,即无const的引用
    
  3. 指针有多级指针,引用只能1级。

  4. 指针的值可以为空,引用的值不能为空,并且引用在定义时必须初始化。

  5. 指针在初始化后可以改变,引用在初始化后不能改变。

  6. 引用的sizeof得到的是引用变量的大小,指针的sizeof得到的是指针本身的大小。

  7. 引用和指针的自增运算符的含义不同。


sizeof

定义空类型(里面没有任何成员变量和成员函数),则sizeof的结果为1,因为类型的实例必须要在内存中占用一定的空间,否则无法使用这些实例(区分不同的类实例)。如果类里面加上构造函数和析构函数,sizeof的结果仍然为1,因为这些函数都至于类型有关,与类型的实例无关。如果把析构函数定义为虚函数,则系统会生成虚函数表,实例会生成指向虚函数的指针,在64位机器中,sizeof的结果为8

class Empty {};
class HoldsAnInt {
    int x;
    Empty e;
};

在上述代码中,空类的一个字节是会被计算到HoldsAnInt的空间中去的,由于地址对齐的机制,所以最后计算的结果为4+4=8

成员函数最终被编译为与对象无关的普通函数,当不涉及到类成员变量的时候,直接调用即可,如果涉及到类成员变量,则需要传入this指针进行操作。

动态绑定:类指针或者引用在调用一个虚函数时,不是指向该函数的地址,而是指向这个虚函数表。

A& a;
a->func()

静态绑定:直接指向函数的地址。

A a;
a.func();

a.func()调用的是A类中的虚函数,而不是父类中的虚函数


struct 和 class 的区别

  1. 默认情况下,struct 的成员变量是 public,class 的成员变量是 private.
  2. struct 默认是 public 继承,class 默认是 private 继承。
  3. struct不能用模版,class可以。

define 和 const 的区别

  1. define 定义的是宏,只进行宏替换,生命周期终止于编译器,没有具体数据类型;

  2. const 是限定修饰符,const 变量存储于程序的数据段,并且在堆栈中分配了存储空间,有确定的数据类型。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值