簡體   English   中英

拋出異常時如何防止構造函數創建對象

[英]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_argumentstd::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

您應該從構造函數中拋出異常,並將其捕獲到構造函數以外的其他代碼中,例如嘗試創建對象的地方。

如果構造函數通過拋出異常結束,則與對象本身關聯的內存將被清理-不會發生內存泄漏

從iso / cpp來源1

如果構造函數引發異常,則不會運行對象的析構函數。

來自iso / cpp來源2

如果輸入無效,則可以使用工廠方法模式來避免調用構造函數。

要旨:

  1. 創建一個稱為.New()或諸如此類的static方法。

  2. 外部實體調用.New()而不是構造函數。

  3. .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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM