繁体   English   中英

关于堆对象和 C++ 和传递引用参数的所有权

[英]About ownership of heap objects and C++ & pass-by-reference parameters

我希望我的班级是:

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

HashMap uniqueStrs;//For later.

这个实例将被传递给一个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()做等效的事情,否则我永远不会猜到。 这个问题表明这是不可能的,但又依赖于auto_ptrshared_ptr但我的“平台仅支持 STL(标准模板库( http://www.sgi.com/tech/stl/ ))”。 (在整个“标准 C++ 库”之外)。 所有的答案都可以避免此类引用。

谢谢你。

评论提示的链接

我无法将链接作为评论发布,因此请参阅Add方法及其建议使用示例: 此处And Benj, String is not std::string no,抱歉。

我知道尝试删除堆栈对象会导致崩溃,但我不明白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
    };

好吧,它在语法上肯定没有错误。 delete 的唯一语法规则是其操作数必须是指针。 语义上:指针必须是从new返回的值,这就是这个习语的臭味所在; 如果我看到一个函数采用 const 引用,我通常有理由假设我可以将一个局部变量或临时变量传递给它。 在这种情况下, delete将导致非常大的问题。

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

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

删除了额外的括号,这是错误的; 我猜你不想问他们。

尽管只接受对它的引用,但 HashMap 会为我释放这些内存。 也许我在新千年里输给了 C 语言,但我认为这是不可能的?

这是可能的,并delete &newName; 是合法的,因为newName实际上是*new ...的结果。 然而,它是单语的,尤其是 decaration

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

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

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

这会导致崩溃(因为调用后右值不再存在,因为delete会删除非堆分配的对象等)但接口没有清楚地显示它并且习惯上将右值传递给采用 const 的函数-参考。

如果你有:

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

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

~NumberedString() {
    delete &newName;
}

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

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

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

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

移除集合中的所有对象指针。 如果 deallocate 参数为真,它还会删除所有对象。

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

在您当前的实现中, 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 非常小心,并注意您的对象是何时以及由谁创建和删除的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM