[英]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_ptr
和shared_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
不拥有String
和short
对象(不会在其析构函数中删除它们)。 这个类将保留指针,就像HashMap
。 您需要定义谁在创建和谁在销毁这些对象。 例如,您可以创建它们,将它们的地址传递给NumberedString
和HashMap
,然后委托HashMap
通过调用HashMap::RemoveAll(true)
来删除它们。 或者,您可以自己删除它们,然后调用HashMap::RemoveAll(false)
。
结论是:对这个愚蠢的 API 非常小心,并注意您的对象是何时以及由谁创建和删除的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.