在 C++ 顯式呼叫解構函式

Jinku Hu 2021年4月29日
在 C++ 顯式呼叫解構函式

本文將介紹幾種如何在 C++ 中顯式呼叫解構函式的方法。

使用 obj.~ClassName() 表示法明確呼叫解構函式

解構函式是特殊函式,當物件自動超出範圍或由使用者顯式呼叫刪除時,解構函式將執行。注意,這些函式通常用於釋放給定物件使用的資源。即使可以將解構函式顯式呼叫為成員函式,也無需這樣做。在大多數情況下,動態分配類資料成員會導致資源的雙重釋放。後一種情況通常會導致程式異常終止。

在下面的示例中,我們演示定義的類-MyClass,該類具有兩個建構函式和一個內建方法來檢索唯一資料成員的值。還定義了解構函式,並使用建構函式將解構函式將相應的訊息列印到 cout 流中,以使我們更容易調查行為。

請注意,以下示例程式碼顯示了兩個解構函式訊息,其中一個由顯式使用者呼叫觸發,而另一個則在程式退出時自動呼叫。雖然,如果在建構函式中為 MyClass 資料成員分配了 new 運算子,則此示例將導致程式異常終止-可能是雙重錯誤。

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class MyClass {
public:
    explicit MyClass(string s):
            str(std::move(s)) { cout << "Constructor 1 executed\n"; }

    MyClass(const MyClass &s):
            str(string(s.str)) { cout << "Constructor 2 executed\n"; }

    ~MyClass() { cout << "Destructor executed\n"; }

    string& getString() { return str; };
private:
    string str;
};

int main()
{
    MyClass str1("Hello There! ");

    cout << endl;
    cout << "str1: " << str1.getString() << endl;
    cout << endl;

    str1.~MyClass();

    return EXIT_SUCCESS;
}

輸出:

Constructor 1 executed

str1: Hello There!

Destructor executed
Destructor executed

另外,我們可以看到以下版本的程式碼觸發了相同數量的建構函式/解構函式,這實際上是該概念背後的思想。因此,即使最後一個示例可以正常工作,也不建議顯式呼叫解構函式。

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class MyClass {
public:
    explicit MyClass(string s):
            str(std::move(s)) { cout << "Constructor 1 executed\n"; }

    MyClass(const MyClass &s):
            str(string(s.str)) { cout << "Constructor 2 executed\n"; }

    ~MyClass() { cout << "Destructor executed\n"; }

    string& getString() { return str; };
private:
    string str;
};

int main()
{
    MyClass str1("Hello There! ");
    MyClass str2(str1);

    cout << endl;
    cout << "str2: " << str2.getString() << endl;
    cout << endl;

    return EXIT_SUCCESS;
}

輸出:

Constructor 1 executed
Constructor 2 executed

str2: Hello There!

Destructor executed
Destructor executed

要考慮的另一種情況是,用 new 運算子分配了類物件,並且在程式退出之前,在同一物件上呼叫了 delete。請注意,在執行描述符函式後,將列印最後一個 cout 語句,這意味著在呼叫 delete 運算子時會呼叫該描述符。

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class MyClass {
public:
    explicit MyClass(string s):
            str(std::move(s)) { cout << "Constructor 1 executed\n"; }

    MyClass(const MyClass &s):
            str(string(s.str)) { cout << "Constructor 2 executed\n"; }

    ~MyClass() { cout << "Destructor executed\n"; }

    string& getString() { return str; };
    string* getStringAddr() { return &str; };
private:
    string str;
};

int main()
{
    auto *str4 = new MyClass("Hello There! ");

    cout << endl;
    cout << "str4: " << str4->getString() << endl;
    cout << endl;

    delete str4;
    cout << "exiting" << endl;

    return EXIT_SUCCESS;
}

輸出:

Constructor 1 executed

str4: Hello There!

Destructor executed
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn

相關文章 - C++ Class