0

我希望我的班级是:

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

HashMap uniqueStrs;//For later.

它的实例将传递给HashMap接管其堆分配所有权的 a:

在 HashMap.h 中(比如说):

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

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

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方法及其建议使用示例:这里还有 Benj,String is not std::string no,抱歉。

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

回应“无用”

@Useless:可能不能传递给foo(int &bar)变量*pBar,声明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
    };
4

3 回答 3

5

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

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

于 2012-02-09T14:01:04.580 回答
1
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- 的函数参考。

于 2012-02-09T13:35:41.953 回答
0

如果你有:

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. 您将需要定义谁在创建和谁在销毁这些对象。例如,您可以创建它们,将它们的地址传递给NumberedString然后HashMap委托HashMap通过调用删除它们HashMap::RemoveAll(true)。或者,您可以自己删除它们,然后调用HashMap::RemoveAll(false).

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

于 2012-02-09T14:31:56.693 回答