【问题标题】:Operator overloading with memory allocation?内存分配的运算符重载?
【发布时间】:2009-03-25 05:12:50
【问题描述】:

以下句子来自 Bruce Eckel 的 The Positive Legacy of C++ and Java,关于 C++ 中的运算符重载:

C++ 既有栈分配又有堆 分配,你必须超载你的 操作员处理所有情况和 不会导致内存泄漏。难的 确实。

我不明白运算符重载与内存分配有何关系。谁能解释一下它们是如何相关的?

【问题讨论】:

  • 我没有使用过 C++。但是,如果您重载一个运算符,它可能需要返回一个新值(对于简单值,它可能在堆栈上,对于复杂类型,它可能在堆上),例如人* a;人* b;人* c = a + b;

标签: c++ memory-leaks operator-overloading


【解决方案1】:

我可以想象几种可能的解释:

首先,在 C++ 中,newdelete 实际上都是运算符;如果您选择通过重载这些运算符来为对象提供自定义分配行为,则必须非常小心,以确保不会引入泄漏。

其次,某些类型的对象要求您重载operator= 以避免内存管理错误。例如,如果您有一个引用计数智能指针对象(如 Boost shared_ptr),则必须实现operator=,并且必须确保正确执行。考虑这个破碎的例子:

template <class T>
class RefCountedPtr {
public:
    RefCountedPtr(T *data) : mData(data) { mData->incrRefCount(); }
    ~RefCountedPtr() { mData->decrRefCount(); }
    RefCountedPtr<T>& operator=(const RefCountedPtr<T>& other) {
        mData = other.mData;
        return *this;
    }
    ...
protected:
    T *mData;
};

这里的operator= 实现被破坏了,因为它不管理mDataother.mData 上的引用计数:它不会减少mData 上的引用计数,从而导致泄漏;并且它不会增加 other.mData 上的引用计数,从而导致可能的内存故障,因为在所有实际引用消失之前,可能会删除指向的对象。

请注意,如果您没有为您的类显式声明您自己的operator=,编译器将提供一个默认实现,其行为与此处显示的实现相同——也就是说,对于这种特殊情况完全不适用。

正如文章所说——在某些情况下,您必须重载运算符,并且必须小心正确处理所有情况。

编辑:抱歉,我没有意识到参考文献是在线文章,而不是一本书。即使在阅读了整篇文章后,也不清楚其意图是什么,但我认为 Eckel 可能指的是我上面描述的第二种情况。

【讨论】:

  • 那是一篇文章...点击链接
  • 这篇文章并没有真正提供太多背景信息 - 我怀疑 Eric 是在谈论文章中提到的书
【解决方案2】:

new 和 delete 实际上是 C++ 中的运算符,您可以覆盖它们以提供您自己的自定义内存管理。看看这里的example

【讨论】:

    【解决方案3】:

    运算符是函数。仅仅因为它们添加了句法糖并不意味着您不必小心记忆。您必须像使用任何其他成员/全局/朋友函数一样管理内存。

    这在实现包装指针类时重载时尤为重要。

    然后通过重载operator+operator+= 进行字符串连接。查看basic_string 模板了解更多信息。

    【讨论】:

      【解决方案4】:

      如果您在比较 Java 和 C++ 之间的运算符重载,您就不会谈论 newdelete - Java 没有为 new 公开足够的内存管理细节,也不需要删除。

      你不能为指针类型重载其他运算符——至少一个参数必须是类或枚举类型,所以他不能谈论为指针提供不同的运算符。

      因此,C++ 中的运算符对值或对值的 const 引用进行操作。

      对于值或对值的 const 引用进行操作的运算符返回值以外的任何内容是非常不寻常的。

      除了所有 C++ 函数常见的明显错误 - 返回对堆栈分配对象的引用(这与内存泄漏相反),或返回对使用 new 而不是值创建的对象的引用(这通常在被学习之前在一个职业生涯中不超过一次),很难想出一个普通运算符有记忆问题的场景。

      因此,无需根据操作数是根据正常使用模式分配的堆栈还是堆来创建多个版本。

      运算符的参数是作为值或引用传递的对象。 C++ 中没有可移植的机制来测试对象是分配在堆上还是栈上。如果对象是按值传递的,它将始终在堆栈上。因此,如果需要在这两种情况下更改运算符的行为,那么在 C++ 中就无法进行可移植的操作。 (在许多操作系统上,您可以测试指向对象的指针是否位于通常用于堆栈的空间或通常用于堆的空间中,但这既不便携也不完全可靠。)(此外,即使您可以拥有运算符它将两个指针作为参数,没有理由相信这些对象只是因为它们是指针而被堆分配的。这些信息在 C++ 中根本不存在)

      你得到的唯一重复是在 operator[] 这样的情况下,同一个运算符同时用作访问器和修改器。那么有 const 和 non-const 版本是正常的,所以如果接收者不是 const 可以设置值。这是一件好事 - 无法改变已标记为常量的对象(可公开访问的状态)。

      【讨论】:

        猜你喜欢
        • 2013-03-02
        • 1970-01-01
        • 2018-08-29
        • 1970-01-01
        • 1970-01-01
        • 2014-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多