在 C++ 中调用析构函数

Invoking of destructors in C++

下面的一段代码有一个名为 String 的用户定义数据类型。这个 class 的对象存储一个字符指针 str(字符串的缩写)和 length.

#include<iostream>
#include<cstring>
using namespace std;
class String
{
    char* str;
    int length;
public:
    String(){}                     //default constructor 
    String(const char* s)
    {
        length=strlen(s);
        str=new char[length+1];   
        strcpy(str,s);
    }
    void add(String a,String b)   //function to concatenate strings
    {
        length=a.length+b.length;
        str=new char[length+1];
        strcpy(str,a.str);
        strcat(str,b.str);
    }
    void display()
    {
        cout<<str<<endl;
    }
    ~String()                                //destructor 
    {
        delete str;
        cout<<"Destructor invoked!";
    }
};
int main()
{
    String s1;
    String s2("Well done!");
    String s3("Boy");
    s1.add(s2,s3);
    s1.display();
    s2.display();
    s3.display();
}

输出:

Destructor invoked!Destructor invoked!Well done!boy
X!!;
<!!;
Destructor invoked!Destructor invoked!Destructor invoked!

如果未定义析构函数,我将得到以下输出(如预期):

Well done!boy
Well done!
boy 

为此您需要复制构造函数:void add(String a,String b); 最好通过引用传递字符串:

void add(const String &a, const String &b);

同样在析构函数中,您需要使用 delete[] 释放内存。

delete[] str;

现在可以使用了。

这个默认构造函数

String(){} 

不初始化任何东西,因为两个数据成员是基本类型,而基本类型不提供自动初始化。

因此,对结果实例的任何使用都将使用(数据成员的)不确定值,因此将具有未定义的行为。

修复:初始化数据成员。


您还需要负责复制和移动,例如避免双重 deletes – 这也是未定义的行为。

示例代码在调用中复制 String 个实例

s1.add(s2,s3);

... 因为 add 按值获取参数。

修复:至少定义一个复制构造函数。


在析构函数中释放内存

delete str;

…不匹配str的分配,它使用了new[]

它可以在实践中工作,但它是未定义的行为,因此会导致各种问题。

修复:对使用 new[] 创建的内容使用 delete[]


修复以上代码后仍会泄漏内存。一个简单的修复可能是定义一个连接构造函数,让 add 使用它来创建一个新的 String 实例,然后 swap 该实例和当前实例的状态。这种方法的优点是自动(好吧,几乎是自动)是异常安全的。


一般建议:

  • 而不是 <cstring>,只需使用 <string.h>。一个优点是,与同名的 C 头文件一样,它保证将名称放在全局命名空间中。因此,如果您使用像 strcpy 这样的非限定名称,它将可以移植地工作,而不仅仅是手头编译器的偶然机会。

  • 不是像void add(String a,String b)那样按值传递潜在的大对象,而是按对[=27的引用传递它们=],如 void add(String const& a,String const& b),以避免不必要的复制。

  • 最好不要在与 i/o 无关的 class 中执行 i/o,如 void display() 成员函数。例如,display() 函数在图形用户界面中不起作用。