C++部分
1. new / delete malloc / free的异同
- malloc和free是函数,new和delete是操作符
- malloc/free只会开辟,释放空间;而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可。
- malloc开辟内存失败,是通过返回值和nullptr做比较;而new开辟内存失败,是通过抛出bad_alloc类型的异常来判断的。
2. 既然有了malloc/free,C++中为什么还需要new/delete呢?直接用malloc/free不好吗?
在对非基本数据类型的对象使用的时候,对象创建的时候还需要执行构造函数,销毁的时候要执行析构函数。而malloc/free是库函数,是已经编译的代码,所以不能把构造函数和析构函数的功能强加给malloc/free,所以c++
中定义new/delete操作符。
3. 引用与指针
指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;
引用:和原来的变量实质上是同一个东西,相当于是原变量的一个别名。可以看作一种更安全的指针。
引用是必须初始化的,指针可以不初始化。
指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了,从一而终。
4. 在传递函数参数时,什么时候该使用指针,什么时候该使用引用呢?
指针:适合需要表示对象可以不存在(nullptr)、需要修改指针本身、需要指针运算,或者需要传递数组时。
引用:适合确保一定有一个有效对象传入、不需要修改引用本身,而只需要修改对象的内容或属性,以及需要简化接口设计和代码时。
5. 堆和栈的区别
- 栈内存由编译器自动分配和释放,内存通常是连续的,分配效率高;堆内存手动分配和释放(new和delete),内存块在堆中动态分配,分配效率较低。
- 栈的大小通常较小,依赖于操作系统和编译器的设置;堆内存的大小通常较大,仅受系统的物理内存限制。
- 栈生命周期短暂(随着函数调用的结束或作用域的退出自动释放),堆生命周期持久(手动释放前会一直存在)。
- 栈上的数据通过变量名直接访问(访问效率高),栈上的数据通过变量名直接访问。
6. 说说const的作用?
const的语义为只读。修饰的值不能改变;必须在定义时就给它赋予初值,初始化后不能再作为左值,在C++中const常量名字都被常量的初始化替换了。
- const 变量:变量声明为 const 后,其值在初始化后不能再被修改。
- const 形参:当函数的形参是 const 时,表示在函数体内不能修改该参数,但const形参可以接收const和非const类型的实参。
- const 成员变量:如果类中的成员变量被声明为 const,它必须在构造函数的初始化列表中进行初始化,并且初始化后不能修改。(C++11 及以后的标准引入了类内初始化)。
- const成员函数:表示该函数不会修改类的成员变量,const对象不可以调用非const成员函数,非const对象都可以调用。
7. 对象的浅拷贝和深拷贝
浅拷贝:指复制对象时,只复制对象的指针,而不复制对象本身。这意味着新对象和原对象将共享相同的内存地址。如果原对象发生变化,新对象也会受到影响。
深拷贝:指复制对象时,先创建足够的空间,再把蓝本的内容拷贝过去。这意味着新对象和原对象不共享相同的内存地址。如果原对象发生变化,新对象不会受到影响。
编译器默认为浅拷贝,深拷贝需要自己进行编写。
8. C++中的重载、重写(覆盖)和隐藏的区别?
- 重载
一组函数要重载,必须处在同一个作用域当中,而且函数名字相同,参数列表不同。 - 隐藏
在继承结构当中,派生类中定义了一个与基类同名的函数,基类中的同名函数被隐藏。 - 覆盖
派生类中定义了一个与基类中同名且参数列表相同的虚函数,派生类的虚函数表将会覆盖基类的虚函数表中的相应条目。基类指针指向哪个派生类对象,就会调用哪个派生类对象的同名覆盖方法,称为多态。
9. inline内联函数 和 普通函数的区别???
内联函数:在编译过程中,就没有函数的调用开销了,在函数的调用点直接把函数的代码进行展开处理了,inline函数不再生成相应的函数符号。
普通函数:有标准的函数调用过程,参数压栈,函数栈帧的开辟和回退过程,有函数调用的开销。
注意:
inline只是建议编译器把这个函数处理成内联函数,但是不是所有的inline都会被编译器处理成内联函数。
debug版本上,inline是不起作用的;inline只有在release版本下才能出现。
10. C++的异常处理的方法
- try - catch :try 块用于包含可能引发异常的代码;当程序在 try 块中遇到异常时,会跳转到匹配的 catch 块。
int main() {
try {
int result = 10 / 0; // 这里会引发除以零的异常
} catch (...) {
std::cout << "An exception occurred!" << std::endl;
}
return 0;
}
- throw 关键字:用于引发异常,将异常对象传递到 catch 块。
if (n == e) throw - 1; //抛出int型异常
- C++标准异常类exception
bad_cast:在用dynamic_cast进行从多态基类对象(或引用)到派生类的引用的强制类型转换时,如果转换是不安全的,则会抛出此异常
bad_alloc:在用new运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常
out_of_range:用vector或 string的at成员函数根据下标访问元素时,如果下标越界,则会抛出此异常
11. static的用法和作用?
static数据不属于对象,在内存中单独储存,仅有一份,且没有指针。即加上static之后,这个数据/函数就不属于这个对象了,跟这个对象脱离。
有什么用?举个例子,银行开户有500人,属于一般数据,而利率这种东西和开户人无关,且每个开户人的利率相同,应该单独存储。
static函数没有指针。不能像一般成员函数处理一般数据,只能处理静态数据。