【问题标题】:About ownership of heap objects and C++ & pass-by-reference parameters关于堆对象的所有权以及 C++ 和传递引用参数
【发布时间】:2012-02-09 13:16:35
【问题描述】:

我希望我的班级是:

class NumberedString : public Object {
public:
    String newName;
    short nameID;
    NumberedString(String &newName, short nameID) : newName(newName), nameID(nameID) {}
};

HashMap uniqueStrs;//For later.

此实例化将传递给HashMap,该HashMap 接管其堆分配的所有权:

在 HashMap.h 中(比如说):

virtual result Add(const Object& key, const Object& value);

现在这就是我感到困惑的地方。我在调用Add 的行中分配了String

uniqueStrs.Add(*(new String(L"XYX_say")), *pNewLoc);

HashMap 然后会为我释放这个内存,尽管只接受对它的引用。也许我在新千年中失去了 C 十年,但我认为这是不可能的?

如果不是,那么我应该能够写出类似的东西:

~NumberedString() {
    delete &newName;
}

对于我的班级,但我永远不会猜到,除非我看到这个库 HashMap::RemoveAll() 做同样的事情。 This question 表示这是不可能的,但要依赖 auto_ptrshared_ptr 但我的“平台仅支持 STL(标准模板库(http://www.sgi.com/tech/stl/))。” (在整个“标准 C++ 库”中)。请所有答案都不要这样引用。

谢谢。

cmets 提示的链接

我无法将链接发布为 cmets,因此请查看 Add 方法及其建议使用示例:here 而 Benj,String 不是 std::string 不,抱歉。

我知道尝试删除堆栈对象可能会导致崩溃,但我不明白HashMap 如何声称能够删除堆对象。我已经编写了上面的类来尝试重新创建这种行为,但我无法完成这一壮举,因此提出了问题。

回应“无用”

@Useless:可能不能将变量*pBar 传递给foo(int &bar),声明为int pBar = new int(1);,然后foo 拥有所有权

foo(int &bar) {
    int *__pBar = &bar;
    delete __pBar;
}

?我打算尝试,但我开始谨慎,不要太相信文档所说的内容。虽然它是从标题中生成的

class _EXPORT_BASE_ HashMap :
    public IMap,
    public Object
    {
    virtual result Add(const Object& key, const Object& value);
        //other stuff
    };

【问题讨论】:

  • 那么你的问题是什么?你的代码可以编译吗? Add() 参数在我看来不正确。
  • @BojanKomazec:我刚刚链接到具有Add(const Object &key...) 的类,并且仍然声称能够为您删除它们! developer.bada.com/help/topic/com.osp.cppapireference.help/…
  • “String”实际上是 std::string 吗?
  • uniqueStrs.Add(*(new String(L"XYX_say"), *pNewLoc)) 几乎可以肯定没有做你想做的事,它立即尊重指针并泄漏内存......你按值传递到哈希映射中。
  • 回复。最后一次 (foo/bar) 编辑:你完全可以做到这一点,事实上确实如此。我只是说他们选择了一种愚蠢的误导方式来表达所有权的转移,因为您通常为此使用指针。

标签: c++ memory-leaks pass-by-reference bada ownership


【解决方案1】:

嗯,它在语法上肯定没有任何问题。唯一的 delete 的语法规则是它的操作数必须是一个指针。 语义上:指针必须是从new 返回的值,那就是 这个成语很臭;如果我看到一个函数采用 const 引用, 我通常有理由假设我可以将它传递给一个局部变量, 或暂时的,或类似的。在这种情况下,delete 将导致 真正的大问题。

更一般地说,查看了库文档:我会 像瘟疫一样避免这个图书馆。它让我想起了一个图书馆 NHS,在 C++ 的早期很普遍:它要求 一切都来自Object,容器包含Object*。 当时(1980 年代后期)使用这个库的经验导致了 结论是它不起作用,并且是动机的一部分 将模板添加到语言中,这样我们就可以编写完成的东西 工作。使用这个库基本上可以追溯到 25 年前,并且 扔掉我们从那时起学到的一切。 (Java 遵循 大约 10 年后类似的路线,所以它不是特定于 C++。基本上,提出的解决方案是为 具有完整动态类型检查的语言,例如 Lisp、Smalltalk 或更多 最近使用 Python,并且不适用于静态类型的语言 检查,如 C++ 或 Java。)

【讨论】:

  • 我已经修改了我的问题,以描述我认为我可以如何模仿这个库的 atrocious 行为。我已经采纳了大家的建议并采用了 Bojan 的 String *pNewName; 实现,但我仍然对库的实现方式感到好奇。
【解决方案2】:
uniqueStrs.Add(*new String(L"XYX_say"), *pNewLoc);

去掉了多余的括号,这是错误的;我猜你不想问他们。

尽管只接受对它的引用,但 HashMap 将为我释放此内存。也许我在新千年中失去了 C 十年,但我认为这是不可能的?

这是可能的,并且delete &newName; 是合法的,鉴于 newName 实际上是*new ... 的结果。但是,它是单调的,尤其是在声明时

virtual result Add(const Object& key, const Object& value);

由于它将其参数作为 const 引用,它也可以将右值隐式转换为 const 引用:

uniqueStrs.Add(String(L"XYX_say"), something)

这会导致崩溃(因为调用后右值不再存在,因为delete会删除一个非堆分配的对象等)但是界面没有清楚地显示它并且习惯上通过使用 const 引用的函数的右值。

【讨论】:

  • 我看到我打错了添加调用。请查看更新的(但仍然非常括号)问题。
  • 我知道尝试删除堆栈对象会导致崩溃,但我不明白它如何声称能够删除堆对象。我已经编写了上面的类来尝试重新创建这种行为,但我无法完成这一壮举,因此提出了问题。
  • 没有魔法。只需int& x = *new int; delete &x; 即可。如果您遇到具体问题,您应该发布具体代码。
  • 您的回答/评论没有描述如何删除首先分配然后通过引用传递给新所有者的参数。
  • 那个 bada 代码是 atrocious。看起来三星从 Java 中翻译得很糟糕。 @John,您只需要接受 API 真的使用指针并将所有权转移给容器,使用愚蠢的误导性语法。
【解决方案3】:

如果你有:

class NumberedString : public Object {
    String newName;
    ...
};

编译器生成NumberedString 的析构函数,它会自动调用所有成员对象的析构函数,包括newName。因此,您不需要做这样的事情(无论如何这都没有意义):

~NumberedString() {
    delete &newName;
}

我查看了用于HashMap::Add(const Object& key, const Object& value) 的 Bada API 说:

此方法执行浅拷贝。它只添加指针;不是 元素本身。

这个接口有点误导和潜在的危险 - jpalacek 解释了如果你传递不在堆上的对象会发生什么。在我看来,这个函数应该有指针类型作为参数,这样会更清楚。

对于 HashMap::Remove(const Object& key, bool deallocate = false)HashMap::RemoveAll(bool deallocate = false) 文档说:

删除集合中的所有对象指针。如果解除分配 param 为真,它还会删除所有对象。

因此,默认情况下,这些函数只会删除指针,但您的对象仍然存在。

在您当前的实现中,NumberedString 负责其成员的生命周期。当该类的实例被销毁时,其成员将被销毁。如果您将其成员传递给HashMap,请删除您的对象/将其从堆栈中删除,然后调用HashMap::RemoveAll(false)HashMap 不会尝试两次释放对象。请注意,删除对象后HashMap 将持有指向已释放内存的指针(悬空指针),这是危险的。如果你调用HashMap::RemoveAll(true)HashMap 会尝试释放已经释放的内存,这也是很危险的。

更好的设计可能是这样的:

class NumberedString : public Object {
    String* pNewName;
    short* pNameID;
    ...
}

您确保NumberedString 不拥有Stringshort 对象(不在其析构函数中删除它们)。这个类将保留指针,就像HashMap。您将需要定义谁在创建和谁在销毁这些对象。例如。您可以创建它们,将它们的地址传递给NumberedStringHashMap,然后委托HashMap 通过调用HashMap::RemoveAll(true) 删除它们。或者,您可以自己删除它们,然后致电HashMap::RemoveAll(false)

结论是:要非常小心这个愚蠢的 API,并注意何时以及由谁创建和删除您的对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-20
    • 2014-05-07
    • 1970-01-01
    • 2016-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-06
    相关资源
    最近更新 更多