简体   繁体   English

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

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

I would like my class to be:我希望我的班级是:

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

HashMap uniqueStrs;//For later.

An instantiation of this will be passed to a HashMap which takes over ownership of its the heap allocation:这个实例将被传递给一个HashMap ,它接管其堆分配的所有权:

In HashMap.h (say):在 HashMap.h 中(比如说):

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

Now this is where I get confused.现在这是我感到困惑的地方。 I was allocating the String in the line that called Add :我在名为Add的行中分配String

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

The HashMap would then free this memory for me despite only accepting a reference to it.尽管只接受对它的引用,但HashMap会为我释放这些内存。 Maybe I lost a decade to C over the new millenium but I thought this was impossible?也许我在新千年里输给了C ,但我认为这是不可能的?

If it's not then I should be able to write something like:如果不是,那么我应该能够写出类似的东西:

~NumberedString() {
    delete &newName;
}

for my class, but I'd never have guessed unless I saw this library HashMap::RemoveAll() doing the equivalent.对于我的班级,但除非我看到这个库HashMap::RemoveAll()做等效的事情,否则我永远不会猜到。 This question states that this is impossible but falls back to reliance on auto_ptr and shared_ptr but my "platform supports only STL(Standard Template Library ( http://www.sgi.com/tech/stl/ ))." 这个问题表明这是不可能的,但又依赖于auto_ptrshared_ptr但我的“平台仅支持 STL(标准模板库( http://www.sgi.com/tech/stl/ ))”。 (out of the entire "Standard C++ Library"). (在整个“标准 C++ 库”之外)。 Could all answers please refrain from such references.所有的答案都可以避免此类引用。

Thank you.谢谢你。

LINKS prompted by comments评论提示的链接

I can't post the links as comments so please see the Add method and an example of its suggested use: here And Benj, String is not std::string no, sorry.我无法将链接作为评论发布,因此请参阅Add方法及其建议使用示例: 此处And Benj, String is not std::string no,抱歉。

ALSO

I know it can cause crashes trying to delete stack objects but I don't get how HashMap can claim to be able to delete heap objects.我知道尝试删除堆栈对象会导致崩溃,但我不明白HashMap如何声称能够删除堆对象。 I have coded up the above class to try and recreate this behaviour but I cannot accomplish the feat, hence the question.我已经编写了上面的类来尝试重新创建这种行为,但我无法完成这一壮举,因此问题。

In response to "Useless"回复“没用”

@Useless: Mightn't it be possible to pass to foo(int &bar) the variable *pBar , declared int pBar = new int(1); @Useless:可能无法将变量*pBar传递给foo(int &bar) ,声明为int pBar = new int(1); and then foo assumes ownership with然后foo承担所有权

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

? ? I was going to try, but I am beginning to be cautious not to believe too much of what the documentation says.我打算尝试,但我开始谨慎,不要太相信文档所说的内容。 Though it was generated from the header which is saying虽然它是从标题中生成的

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

Well, there's certainly nothing syntactically wrong with it.好吧,它在语法上肯定没有错误。 The only syntax rule for delete is that its operand has to be a pointer. delete 的唯一语法规则是其操作数必须是指针。 Semantically: the pointer must be value returned from new , and that's where this idiom stinks;语义上:指针必须是从new返回的值,这就是这个习语的臭味所在; if I see a function taking a const reference, I'm normally justified in supposing that I can pass it a local variable, or a temporary, or such.如果我看到一个函数采用 const 引用,我通常有理由假设我可以将一个局部变量或临时变量传递给它。 In which case, the delete is going to cause really big problems.在这种情况下, delete将导致非常大的问题。

More generally, having looked at the library documentation: I would avoid this library like the plague.更一般地说,查看了库文档:我会像躲避瘟疫一样避开这个库。 It reminds me of a library from the NHS, which was widespread in the early days of C++: it requires that everything derive from Object , and containers contain Object* .它让我想起了 NHS 的一个库,它在 C++ 的早期很流行:它要求所有东西都从Object派生,并且容器包含Object* Experience with this library back then (late 1980's) led to the conclusion that it didn't work, and were part of the motivation for adding templates to the language, so that we could write things that did work.当时(1980 年代后期)使用这个库的经验得出的结论是它不起作用,并且是向该语言添加模板的动机的一部分,以便我们可以编写有效的东西。 Using this library is basically going back 25 years in time, and throwing out everything we've learned since then.使用这个库基本上可以追溯到 25 年前,并扔掉我们从那时起学到的一切。 (Java followed a similar route about 10 years later, so it's not something specific to C++. Basically, the solution proposed is one that was developed for languages with full dynamic type checking, like Lisp, Smalltalk or more recently Python, and doesn't work in languages with static type checking, like C++ or Java.) (Java 大约在 10 年后遵循了类似的路线,所以它不是 C++ 特有的。基本上,提出的解决方案是为具有完整动态类型检查的语言开发的,如 Lisp、Smalltalk 或最近的 Python,并且不使用具有静态类型检查的语言,如 C++ 或 Java。)

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

Removed the extra parentheses, which were wrong;删除了额外的括号,这是错误的; I guess you didn't want to ask about them.我猜你不想问他们。

The HashMap would then free this memory for me despite only accepting a reference to it.尽管只接受对它的引用,但 HashMap 会为我释放这些内存。 Maybe I lost a decade to C over the new millenium but I thought this was impossible?也许我在新千年里输给了 C 语言,但我认为这是不可能的?

It is possible, and delete &newName;这是可能的,并delete &newName; is legal, given that newName is actually a result of *new ... .是合法的,因为newName实际上是*new ...的结果。 However, it is unidiomatic especially with the decaration然而,它是单语的,尤其是 decaration

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

Since it takes its arguments as const-references, it can also take rvalues implicitly converted to const references:由于它将其参数作为常量引用,它还可以将右值隐式转换为常量引用:

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

This will lead to crashes (because the rvalue ceases to exist after the call, because the delete will delete a non-heap allocated object etc.) but the interface doesn't clearly show it and it is customary to pass rvalues to functions taking const-references.这会导致崩溃(因为调用后右值不再存在,因为delete会删除非堆分配的对象等)但接口没有清楚地显示它并且习惯上将右值传递给采用 const 的函数-参考。

If you have:如果你有:

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

compiler generates NumberedString 's destructor which automatically calls destructors of all member objects, including newName .编译器生成NumberedString的析构函数,它自动调用所有成员对象的析构函数,包括newName Therefore you don't need to do something like this (which doesn't make sense anyway):因此你不需要做这样的事情(无论如何这没有意义):

~NumberedString() {
    delete &newName;
}

I had a look at Bada API which for HashMap::Add(const Object& key, const Object& value) says:我查看了 Bada API,对于HashMap::Add(const Object& key, const Object& value)说:

This method performs a shallow copy.此方法执行浅拷贝。 It adds only the pointer;它只添加指针; not the element itself.不是元素本身。

This interface is a bit misleading and potentially dangerous - jpalacek explained what can happen if you pass object which is not on the heap.这个接口有点误导和潜在的危险——jpalacek 解释了如果你传递不在堆上的对象会发生什么。 In my opinion, this function should have pointer types as arguments, that would be much clearer.在我看来,这个函数应该有指针类型作为参数,这样会更清楚。

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

Removes all the object pointers in the collection.移除集合中的所有对象指针。 If the deallocate param is true, it also removes all of the objects.如果 deallocate 参数为真,它还会删除所有对象。

So, by default these functions will just remove pointers but your objects will still be alive.因此,默认情况下,这些函数只会删除指针,但您的对象仍然存在。

In your current implementation NumberedString is responsible for the lifetime of its members.在您当前的实现中, NumberedString负责其成员的生命周期。 When instance of that class is destroyed, its members will be destroyed.当该类的实例被销毁时,其成员也将被销毁。 If you pass its members to HashMap , delete your object/remove it from the stack and then call HashMap::RemoveAll(false) , HashMap won't try do deallocate objects twice.如果您将其成员传递给HashMap ,删除您的对象/从堆栈中删除它,然后调用HashMap::RemoveAll(false)HashMap不会尝试执行两次解除分配对象。 Be aware that after deleting your object HashMap will be holding pointers to deallocated memory ( dangling pointers ) which is dangerous.请注意,删除对象后HashMap将持有指向释放内存的指针悬空指针),这是危险的。 If you call HashMap::RemoveAll(true) , HashMap will try to deallocate memory that has already been deallocated, which is dangerous as well.如果你调用HashMap::RemoveAll(true)HashMap将尝试释放已经释放的内存,这也是危险的。

Better design could be to have something like this:更好的设计可能是这样的:

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

where you make sure that NumberedString doesn't own String and short objects (doesn't delete them in its destructor).确保NumberedString不拥有Stringshort对象(不会在其析构函数中删除它们)。 This class would be keeping pointers, just as HashMap .这个类将保留指针,就像HashMap You will need to define who's creating and who's destroying these objects.您需要定义谁在创建和谁在销毁这些对象。 Eg you could create them, pass their addresses to NumberedString and HashMap and then delegate HashMap to delete them by calling HashMap::RemoveAll(true) .例如,您可以创建它们,将它们的地址传递给NumberedStringHashMap ,然后委托HashMap通过调用HashMap::RemoveAll(true)来删除它们。 Or, you can delete them yourself and then call HashMap::RemoveAll(false) .或者,您可以自己删除它们,然后调用HashMap::RemoveAll(false)

The conclusion is: be very careful with this silly API and pay attention to when and by whom are your objects created and deleted.结论是:对这个愚蠢的 API 非常小心,并注意您的对象是何时以及由谁创建和删除的。

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

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