【问题标题】:Operators overloading : memory leaks运算符重载:内存泄漏
【发布时间】:2012-01-29 17:54:13
【问题描述】:

这是我无法理解的。 假设我重载了运算符 & 以在两个对象之间创建 and ,并使用运算符 = 进行分配(创建对象的副本)。 假设我有一个名为 A 的类,代码如下:

A a1,a2,a3;
// initialize a1,a2,a3
a1=a2&a3;

如果a1已经分配,​​当我将a1分配给a2&a3时,有内存泄漏? 是在不删除旧对象的情况下创建了新对象吗?

PS:运算符重载使用两个 A 类型的参数(我还没有定义 A 类,这是一个示例),返回一个 A 类型的参数,定义为类的朋友。

代码:

这是一个很长的代码,所以我使用了 pastebin: http://pastebin.com/42TnThfC 不过由于篇幅较长,我也贴出最有意义的部分:

template<class T>
List<T>& List<T>::operator= (List<T>& l)
{
    List<T>* ptr=l.next;
    if(this!=&l)
    {
        resize(0);
        while(!ptr->end)
        {
            push(ptr->info);
            ptr=ptr->next;
        }
    }
    return *this;
}

template <class T>
List<T>& operator& (List<T>& l1, List<T>& l2) throw()
{
    List<T>* temp,*ptr=l1.next;
    temp=new List<T>();
    try
    {
        if( (l1.end)^(l2.end) )
            throw excp1;
    }
    catch (char *s)
    {
        cout << "Exception: "<<s<<endl;
    }
    if(l1.end)
    {
        while(!ptr->end)
        {
            if(l2.in(ptr->info))
                temp->push(ptr->info);
            ptr=ptr->next;
        }
    }
    else
    {
        if(l1.info==l2.info)
            temp->push(l1.info);
    }
    return *temp;
}

它是一个列表,push 推送一个项目,main 的一个例子:

int main(int arcg, char **argv)
{
    List<T>l1,l2,l3;
    for(int i=0;i<10;i++)
    {
        l1.push(i);
        l2.push(i);
        l3.push(i);
    }
    l3=l1&l2;
}

这个case l3已经是l1&l2了,但是这样会不会导致内存泄露?

【问题讨论】:

  • 我们需要更多代码。像赋值运算符和 & 运算符一样能够帮助你。

标签: c++


【解决方案1】:

不,应该没问题。原来的a1 将被销毁并替换为一个新的(这是a2 &amp; a3 的结果)。

然而您应该确保,如果A 包含指针,则为该类型正确定义了复制构造函数、赋值运算符和析构函数,否则“原始a1 将被破坏”可能还不够!

【讨论】:

    【解决方案2】:

    在A类中,分配你必须删除原始数据的操作员

    class A {
        A &operator=(const A &orig){
            if( &orig == this){
                return *this;
            }
    
            // here
            if(data){
               delete []data;
            }
    
           // copy original data
        }
    }
    

    【讨论】:

    • 缺少分配给自我的检查,应该在删除之前复制。
    • @EthanSteinberg 谢谢,错过了。这样可以吗?还是你有更好的主意?
    • @Als 对不起,我不明白这个问题
    • 请注意,实际上需要自分配检查以确保语义正确,这表明存在异常安全问题!我没有正式的证明,但据我所知,异常安全的赋值运算符不需要防止自赋值,并且那些依赖于检查的赋值运算符实现不是异常安全的。上面的代码就是一个例子:如果删除数据后抛出异常,则对象进入坏状态。
    【解决方案3】:

    我相信,如果您执行以下操作,只会出现内存泄漏:

    A *a1 = new A(1);
    a1 = a2 & a3; // Don't do this, it doesn't work 
    

    只要有A a1, a2, a3;如果您像这样声明它们,就不可能泄漏它们。当函数被调用时,函数会为这些对象静态分配空间,当函数执行完成时(从堆栈弹出期间),它们会自动释放。

    【讨论】:

    • 那不会编译。你会做*a1 = a2&amp;a3; 这是合法的。
    【解决方案4】:

    赋值运算符的职责是使左侧的对象与右侧的对象相同。如果它分配了内存并准备用从右侧构造的东西替换它,那么它最好释放这个内存。一般来说,我发现用复制构造函数和swap() 成员函数来实现赋值运算符是最简单的:

    T& operator= (T const& other) {
        T(other).swap(*this);
        return *this;
    }
    

    swap() 成员函数只是进行成员交换。

    【讨论】:

    • 我认为 other 应该通过副本传递,以便复制和交换。
    • @Als:不;他明确地创建了一个副本。 T(other)
    • 应该做的是将other传递给const&amp;!我会解决的。按值传递应该可以工作,并且避免需要明确的副本和副手我不记得为什么我通常不这样做(也许这只是我应该纠正的错误习惯)。
    猜你喜欢
    • 1970-01-01
    • 2015-11-17
    • 2014-10-16
    • 1970-01-01
    • 2013-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-08
    相关资源
    最近更新 更多