在 C++ 中在析构函数中删除指针

4
我正在使用一个库中的类。假设它叫作A,并且有一个字符指针“token”。
我的代码:
void someFunction()
{
    A a;
    cout<<a.token;
    anotherFunction(a);
    cout<<a.token;  //  line 4: now token became invalid [1]
}

void anotherFunction(A copyOfA);
{
   //  doing something
}  //  on exit destructor of copyofA will be called

为什么它变成了无效的:A类如下所示:
class A
{
    char *token;
    public:
    A()
    {
        token = GetRandomToken();   // GetRandomToken will return a 'new Char' array
    }
    ~A()
    {
        if(token != NULL)
        {
            delete[] token;    // it is A's responsibility to delete the memory it created
            token = NULL;
        }
    }
};

anotherFunction中,当copyOfA的析构函数被调用时,token被删除了。因此,在第4行,token是无效的,因为a.token和copyOfA.token都指向同一个地址。
在以下情况下,有什么解决方案:
情况1:类A在给定库中:因此我不能修改它。
情况2:如果我可以修改类A:处理这种情况的好方法是什么?
我知道,如果通过引用调用anotherFunction,我就不会遇到这个问题。但是如果我必须在某个时候保留对象的副本怎么办?
请查看示例代码:https://ideone.com/yZa4k4

9
"类A"没有遵循3/5法则,因此您不能安全地复制它。 - UnholySheep
3
唯一的解决办法是永远不要创建副本。你只能有一个实例并传递引用或指针。 - UnholySheep
2
你可以使用适当的实现将类A包装在另一个类B中,从而不直接使用A。问题是:你打算复制A的副本来做什么?复制由token指出的内容?还是两个实例都应该共享token - Holt
7
抛弃这个图书馆,因为它太糟糕了。如果您可以修改它,只需给它适当的复制语义即可。 - Passer By
1
回应@路人甲所言,把这个库扔掉吧,因为如果它的作者在这方面犯错了,那么他们还会有什么其他的错误呢? - Caleth
显示剩余4条评论
2个回答

3

如果您无法修改class A,那么应该避免复制它。我认为最安全的方法是动态分配class A的对象:

void anotherFunction(std::shared_ptr<A> aPtr)
{
    // please also note that in your case token is PRIVATE
    std::cout << aPtr->token << std::endl;
}

std::shared_ptr<A> aPtr(new A);
std::cout << aPtr->token << std::endl;
anotherFunction(aPtr);

如果您坚持使用栈分配,您需要更改anotherFunction的签名:
void anotherFunction(const A& a)
{
    std::cout << a.token << std::endl;
}

通过const引用传递参数(避免复制构造函数)。

如果您可以修改您的A类,则应该应用三/五/零法则,因为您有非平凡的析构函数。懒惰的方法是仅声明其他构造函数为删除的(然后,就像在您的示例中一样,您无法复制A对象,但也保证没有人会尝试这样做):

class A
{
    public:
    // for this example purpose I made token PUBLIC, but it is a bad idea in general
    char *token;
    A()
    {
        token = GetRandomToken();   // GetRandomToken will return a 'new Char' array
    }
    ~A()
    {
        if(token != NULL)
        {
            delete[] token;    // it is A's responsibility to delete the memory it created
            token = NULL;
        }
    }
    A(const A& other) = delete;
    A(A&& other) = delete;
};

或者,如果你不懒的话,你可以考虑如何将一个对象中token指针所指向的内存复制到另一个对象中——具体实现方式由你决定。这取决于GetRandomToken的要求和实现。


-1
如果你的例子是准确的,那么 class A 没有一个适当的复制构造函数,因此会删除两个实例的令牌。这导致了双重删除,因为第一个实例中的指针没有改变。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接