[英]How to prevent the constructor from creating an object when an exception is thrown
當構造函數引發異常時,如何防止創建對象?
在下面的示例中,我創建一個Month()類,其int month_
屬性的合法值在1到12的范圍內。我用整數值13實例化了December或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_;
};
例如,您必須向main拋出異常,有關構造函數的消息應該在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
您應該從構造函數中拋出異常,並將其捕獲到構造函數以外的其他代碼中,例如嘗試創建對象的地方。
如果構造函數通過拋出異常結束,則與對象本身關聯的內存將被清理-不會發生內存泄漏
如果構造函數引發異常,則不會運行對象的析構函數。
如果輸入無效,則可以使用工廠方法模式來避免調用構造函數。
要旨:
創建一個稱為.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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.