如何防止构造函数在引发异常时创建对象
How to prevent the constructor from creating an object when an exception is thrown
当构造函数抛出异常时,如何防止创建对象?
在下面的示例中,我创建了一个 Month(( 类,其中int month_
属性的合法值在 1 到 12 的范围内。我用整数值 13 实例化 12 月或dec
。将引发异常,因为它应该是,但仍会创建对象。然后调用析构函数。
如何在引发异常时中止类实例的创建?
输出
-- Month() constructor called for value: 2
-- Month() constructor called for value: 6
-- Month() constructor called for value: 13
EXCEPTION: Month out of range
2
6
13
-- ~Month() destructor called.
-- ~Month() destructor called.
-- ~Month() destructor called.
Press any key to exit
最小、完整且可验证的示例
#include <iostream>
#include <string>
class Month {
public:
Month(int month) {
std::cout << "-- Month() constructor called for value: " << month << std::endl;
try {
// if ((month < 0) || month > 12) throw 100; Good eye, Nat!
if ((month < 1) || month > 12) throw 100;
} catch(int e) {
if (e == 100) std::cout << "EXCEPTION: Month out of range" << std::endl;
}
month_ = month;
}
~Month() {
std::cout << "-- ~Month() destructor called." << std::endl;
}
int getMonth()const { return month_; }
private:
int month_;
};
int makeMonths() {
Month feb(2), jun(6), dec(13);
std::cout << feb.getMonth() << std::endl;
std::cout << jun.getMonth() << std::endl;
std::cout << dec.getMonth() << std::endl;
return 0;
}
int main() {
makeMonths();
std::cout << "Press any key to exit"; std::cin.get();
return 0;
}
如何在抛出异常时中止类实例的创建?
好吧,你在构造函数中抛出了一个异常。但有一个问题:不要抓住它!
如果你抓住它,就像例外从未发生过一样。你抓住了它,所以它不再上升调用堆栈。因此,创建了对象,因为就任何人而言,构造函数没有抛出异常。
如果从构造函数中删除catch
子句,可能会得到类似以下内容:
-- Month() constructor called for value: 2
-- Month() constructor called for value: 6
-- Month() constructor called for value: 13
terminate called after throwing an instance of 'int'
[1] 28844 abort (core dumped) ./main
在这里,构造函数抛出了一个异常,这次它在构造函数之外的调用堆栈中上升,因为没有人在构造函数中捕获它。然后它进入makeMonths
,在那里它也没有被捕获,然后到main
,在那里它也没有被捕获,因此程序异常终止。
默认情况下,在构造函数中引发异常应阻止调用析构函数。但是,您正在捕获异常并处理它。
您可以在 catch 中抛出一个新的异常,然后可以在此范围之外看到该异常,以便不调用析构函数。
如何在抛出异常时中止类实例的创建?
你只是(重新(抛出异常,而不是捕获它,我建议抛出一个std::invalid_argument
或std::out_of_range
异常:
class Month {
public:
Month(int month) {
std::cout << "-- Month() constructor called for value: " << month << std::endl;
if ((month < 0) || month > 12)
throw std::invalid_argument("month");
// or throw std::out_of_range("month");
else
month_ = month;
}
~Month() {
std::cout << "-- ~Month() destructor called." << std::endl;
}
int getMonth()const { return month_; }
private:
int month_;
};
您创建的Month
实例将使用堆栈展开机制正确丢弃。永远不会创建实例,因此根本不调用析构函数。
查看实时示例。
为了使异常更具信息性,您可以使用以下行:
if ((month < 0) || month > 12) {
throw std::out_of_range("'month' parameter must be in the range of 1-12.");
}
另外,如果您不喜欢初始化构造函数主体中的成员变量(就像我所做的那样(
您可以只为有效性检查代码引入一个lambda 表达式:
auto month_check = [](int month) {
if ((month < 0) || month > 12) {
throw std::out_of_range("'month' parameter must be in the range of 1-12.");
}
return month;
};
class Month {
public:
Month(int month) : month_(month_check(month)) {
std::cout << "-- Month() constructor called for value: " << month << std::endl;
}
~Month() {
std::cout << "-- ~Month() destructor called." << std::endl;
}
int getMonth()const { return month_; }
private:
int month_;
};
现场演示
可以使用工厂方法模式来避免在输入无效时调用构造函数。
要点:
-
创建一个名为
.New()
或其他东西的static
方法。 -
外部实体调用
.New()
而不是构造函数。 -
.New()
验证输入。-
如果输入良好,则调用构造函数并返回结果。
-
否则,它将引发异常。 或者,您可以返回
null
或返回非null
默认值。
-
#include <iostream>
#include <string>
class Month
{
public:
static Month New(int month)
{
std::cout << "-- Month.New() factory method called for value: " << month << std::endl;
if (month < 0 || month >= 12)
{
std::cout << "-- Month.New() factory method found that month was invalid; throwing exception" << month << std::endl;
throw /*exception type here*/;
}
return Month(month);
}
~Month()
{
std::cout << "-- ~Month() destructor called." << std::endl;
}
int getMonth()const { return month_; }
private:
int month_;
Month(int month)
{
month_ = month;
}
};
int makeMonths() {
Month feb(2), jun(6), dec(13);
std::cout << feb.getMonth() << std::endl;
std::cout << jun.getMonth() << std::endl;
std::cout << dec.getMonth() << std::endl;
return 0;
}
int main() {
makeMonths();
std::cout << "Press any key to exit"; std::cin.get();
return 0;
}
例如,您必须将异常抛出到 main,关于构造函数的消息 schould 在块try
中。 所以。。。 2. 对象创建以及何时 3.正在创建的对象,异常throw 100
,并在 main 中处理。我认为,这是很多可能性中的一个。
#include <iostream>
#include <exception>
#include <iostream>
#include <string>
class Month {
public:
Month(int month) {
try {
if ((month < 0) || month > 12) throw 100;
std::cout << "-- Month() constructor called for value: " << month << std::endl;
}
~Month() {
std::cout << "-- ~Month() destructor called." << std::endl;
}
int getMonth()const { return month_; }
private:
int month_;
};
int makeMonths() {
Month feb(2), jun(6), dec(13);
std::cout << feb.getMonth() << std::endl;
std::cout << jun.getMonth() << std::endl;
std::cout << dec.getMonth() << std::endl;
return 0;
}
int main() {
try {
makeMonths();
std::cout << "Press any key to exit"; std::cin.get();
}
catch (...)
{
std::cout << "exception" << std::endl;
}
return 0;
}
输出:
-- Month() constructor called for value: 2
-- Month() constructor called for value: 6
-- ~Month() destructor called.
-- ~Month() destructor called.
exception
您应该从构造函数中抛出异常,并在构造函数以外的代码中捕获它,例如尝试创建对象的地方。
如果构造函数通过抛出异常完成,则与对象本身关联的内存将被清理 — 没有内存泄漏
来自 iso/cpp 来源 1
如果构造函数引发异常,则不会运行对象的析构函数。
来自 iso/cpp 源 2
- 使用基类指针创建对象时,缺少派生类析构函数
- 创建具有 new in 函数和"this is nullptr"异常的对象
- 如何创建对象函数指针C++映射?
- C++创建对象数组
- 在 C++ 的 Switch Case 中创建对象后对对象调用方法
- 堆异常 -1073741510对象
- 如何获取在 main() 函数中构造的类的创建对象?
- 基于文件中的条目创建对象
- 错误:创建对象后无法分配区域
- C++ 通过输入创建对象
- 堆还是堆栈用于创建对象?
- 使用 C++ 创建对象数组
- 使用unique_ptr创建对象
- C++递归地在类构造函数中创建对象
- 通过向构造函数其他对象引用页面来创建对象
- ReactiveX (rx) - 在对象上应用可观察对象,而不是在可观察对象中创建对象
- 如何在OSX上正确创建C++对象文件(.o)
- 如何防止构造函数在引发异常时创建对象
- 是否可以对构造函数抛出异常的对象进行异常处理,该对象的异常处理接近其基于堆栈的代码创建
- 如果文件在构造函数中不存在,则抛出异常,并在 main() 中创建对象时尝试/捕获它,如果好 - 开始使用该对象