【问题标题】:C++: Union DestructorC++:联合析构函数
【发布时间】:2013-04-11 09:03:51
【问题描述】:

联合是一种用户定义的数据或类类型,在任何给定时间,它只包含其成员列表中的一个对象。假设需要动态分配所有可能的候选成员。例如。

// Union Destructor
#include <string>
using namespace std;

union Person
{
private:
    char* szName;
    char* szJobTitle;
public:
    Person() : szName (nullptr), szJobTitle (nullptr) {}
    Person (const string& strName, const string& strJob)
    {
        szName = new char[strName.size()];
        strcpy (szName, strName.c_str());

        szJobTitle = new char [strJob.size()];
        strcpy (szJobTitle, strJob.c_str());    // obvious, both fields points at same location i.e. szJobTitle
    }
    ~Person()   // Visual Studio 2010 shows that both szName and szJobTitle
    {           // points to same location.
        if (szName) {
            delete[] szName;     // Program crashes here.
            szName = nullptr;  // to avoid deleting already deleted location(!)
        }
        if (szJobTitle)
            delete[] szJobTitle;
    }
};

int main()
{
    Person you ("your_name", "your_jobTitle");
    return 0;
}

上述程序在 ~Person 中的第一个删除语句处崩溃(此时 szName 包含有效的内存位置,为什么?)。

析构函数的正确实现是什么?

同样,如果我的类包含联合成员 (how to wrtie destructor for class including a Union),如何销毁类对象?

【问题讨论】:

    标签: c++ destructor unions


    【解决方案1】:

    您一次只能使用工会的一个成员,因为他们共享相同的内存。然而,在构造函数中,你初始化了两个成员,它们相互覆盖,然后在析构函数中你最终释放了它两次。您正在尝试将其用作结构(基于您需要使用结构的字段的名称)。

    尽管如此,如果您需要一个联合,那么您可能需要一个结构体作为一种信封,它有一些 id 表示正在使用的成员,以及一个构造函数和一个处理资源的析构函数。

    另外 - 你的数组太小了。 size() 返回字符数,但如果您使用 char* 作为字符串类型,则需要为空字符 (\0) 留出空间来处理终止。

    如果您需要联合,请尝试使用 Boost.Variant。它比普通的 union 更容易使用。

    【讨论】:

    • “一个结构体作为一种信封,它有一些 id 代表正在使用的成员”又名。一个标记的联合
    【解决方案2】:

    您使用的是delete,而您应该使用delete [],因为您使用的是new [],而不是new

    改变这些:

    delete szName;
    delete szJobTitle;
    

    对这些:

    delete [] szName;
    delete [] szJobTitle;
    

    顺便说一句,析构函数中的if 条件是没有意义的。我的意思是,如果一个指针是nullptr,那么写delete ptr;是安全的,也就是

    A *ptr = nullptr;
    delete ptr; //Okay! No need to ensure ptr is non-null
    

    除此之外,您还违反了三规则(或 C++11 中的五规则):

    实施它们。

    【讨论】:

      【解决方案3】:

      您不尊重新删除配对:newdelete 配对,new[]delete[] 配对。你在做new[],但打电话给delete;这是不兼容的。

      附带说明一下,构造函数存在内存泄漏:分配给szName 的内存一旦被分配给szJobTitle 的指针覆盖,就永远不会释放。

      由于这是 C++,您通常应该使用 std::string 而不是 char* 来表示字符串。

      【讨论】:

        【解决方案4】:

        上述程序在 ~Person 中的第一个删除语句处崩溃(此时 szName 包含有效的内存位置,为什么?)。

        我没有编译器(或没有时间编译您的代码)但是(除了the problems addressed by Nawaz)我猜这是因为您将工会成员视为类成员。在你的 union 中,szName 和 szJobTitle 应该看起来像两个具有相同地址的变量:

        Person (const string& strName, const string& strJob)
        {
            szName = new char[strName.size()];
            strcpy (szName, strName.c_str());
        
            szJobTitle = new char [strJob.size()]; // this creates memory leak (1)
            strcpy (szJobTitle, strJob.c_str());
        }
        

        由于分配了新内存并将其放置在 szJobTitle 中,因此会发生内存泄漏。 &szJobTitle 使用与 &szName 相同的内存位置,因此使用第 (1) 行中的分配,您失去了在 szName 中分配的地址。如果 szName 和 szJobTitle 属于不同类型(内存占用不匹配),设置 szJobTitle 也会损坏(或仅部分覆盖 szTitle)。

        析构函数的正确实现是什么?

        我认为你没有足够的细节来实现析构函数。查看discriminated unions in C++ 的概念,了解如何正确实现它。通常你的联合成员应该管理他们自己的内存(使用 std::string,而不是 char*),然后你的析构函数只会删除分配的内容(但你必须显式调用它)。

        同样,如果我的类包含联合成员,如何破坏类对象(如何为包含联合的类编写析构函数)?

        再次,看看受歧视的工会。它基本上是一个联合和一个枚举的关联,其中枚举映射到联合的成员,并设置为指定联合的哪些成员被设置。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-01-11
          • 2010-09-06
          • 2012-07-28
          • 1970-01-01
          • 1970-01-01
          • 2014-05-27
          • 2011-04-03
          • 2010-12-16
          相关资源
          最近更新 更多