✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/fYaBd
📚专栏简介:在这个专栏中,我将会分享 C++ 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
34. 构造函数和析构函数可否抛出异常?
1. C++ 只会析构已经完成的对象,对象只有在其构造函数执行完毕才算是完全构造妥当。在构 造函数中发生异常,控制权转出构造函数之外。
因此,在对象 b 的构造函数中发生异常,对象 b 的析构函数不会被调用。因此会造成内存泄漏。
-
用 auto_ptr 对象来取代指针类成员,便对构造函数做了强化,免除了抛出异常时发生资源泄漏的危机,不再需要在析构函数中手动释放资源;
-
如果控制权基于异常的因素离开析构函数,而此时正有另一个异常处于作用状态,C++ 会调用 terminate 函数让程序结束;
2. 如果异常从析构函数抛出,而且没有在当地进行捕捉,那个析构函数便是执行不全的。如果析构函 数执行不全,就是没有完成他应该执行的每一件事情。
在析构函数内部处理异常:
上面左边的方法是直接调用 abort 终止程序,右边则是直接吞下异常,只是记录个日志,后面再处理。
但是这两种方法都有个缺点就是用户无法参与操作,因此可以写成下面的方式:
用户可以自己实现一个 close 函数来进行关闭,如果关闭的顺利则 closed=true,反之关闭失败则会进行异常捕捉,在析构函数中帮助用户关闭。
总结:
析构函数可以抛出异常,但是这种做法是非常危险的,通常不推荐。因为析构函数具有一种清理资源的特性,如果析构函数本身抛出异常,可能导致以下问题:
-
资源泄露:当一个对象被析构时,析构函数负责释放该对象持有的资源。如果析构函数抛出异常,这个过程可能会中断,导致资源泄露。
-
叠加异常:如果析构函数在处理另一个异常时抛出异常,会导致异常叠加。这种情况下,程序将无法处理两个异常,从而可能导致未定义行为或程序崩溃。
为了避免这些问题,通常建议在析构函数中处理异常或者避免执行会抛出异常的函数,可以在析构函数中使用 try-catch 块来捕获和处理潜在的异常,确保资源得到正确释放和清理保证程序的稳定性和健壮性。
35. 构 造函数的几种关键字
default
default 关键字可以显式要求编译器生成合成构造函数,防止在调用时相关构造函数类型没有定义而报错。
#include <iostream>
using namespace std;
class CString
{
public:
CString() = default; //语句1
//构造函数
CString(const char* pstr) : _str(pstr){}
void* operator new() = delete;//这样不允许使用new关键字
//析构函数
~CString(){}
public:
string _str;
};
int main()
{
auto a = new CString(); //语句2
cout << "Hello World" <<endl;
return 0;
}
//运行结果
//Hello World
如果没有加语句 1,语句 2 会报错,表示找不到参数为空的构造函数,将其设置为 default 可以解决这个问题。
delete
delete 关键字可以删除构造函数、赋值运算符函数等,这样在使用的时候会得到友善的提示。
#include <iostream>
using namespace std;
class CString
{
public:
void* operator new() = delete;//这样不允许使用new关键字
//析构函数
~CString(){}
};
int main()
{
auto a = new CString(); //语句1
cout << "Hello World" <<endl;
return 0;
}
在执行语句 1 时,会提示 new 方法已经被删除,如果将 new 设置为私有方法,则会报惨不忍睹的错误,因此使用 delete 关键字可以更加人性化的删除一些默认方法
0
将虚函数定义为纯虚函数(纯虚函数无需定义,= 0 只能出现在类内部虚函数的声明语句处;当然,也可以为纯虚函数提供定义,函数体可以定义在类的外部也可以定义在内部)。
36. 构造函数、拷贝构造函数和赋值操作符的区别
构造函数
对象不存在,没用别的对象初始化,在创建一个新的对象时调用构造函数
拷贝构造函数
对象不存在,但是使用别的已经存在的对象来进行初始化
赋值运算符
对象存在,用别的对象给它赋值,这属于重载 "=" 号运算符的范畴,"=" 号两侧的对象都是已存在的
举个例子:
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "我是构造函数" << endl;
}
A(const A& a)
{
cout << "我是拷贝构造函数" << endl;
}
A& operator = (A& a)
{
cout << "我是赋值操作符" << endl;
return *this;
}
~A() {};
};
int main()
{
A a1; //调用构造函数
A a2 = a1; //调用拷贝构造函数
a2 = a1; //调用赋值操作符
return 0;
}
//输出结果
//我是构造函数
//我是拷贝构造函数
//我是赋值操作符