1

我在阅读More Effective C++中的 item28 时遇到了一个问题。在这个项目中,作者向我们展示了我们可以使用成员模板,SmartPtr以便SmartPtr<Cassette>可以将其转换为SmartPtr<MusicProduct>.
下面的代码和书中的代码不一样,但是效果是一样的。

#include <iostream>

class Base {};
class Derived : public Base {};

template<typename T>
class smart {
public:
    smart(T* ptr)
        : ptr(ptr)
    {}

    template<typename U>
    operator smart<U>()
    {
        return smart<U>(ptr);
    }

    ~smart()
    {
        delete ptr;
    }
private:
    T* ptr;
};

void test(const smart<Base>& ) {}

int main()
{
    smart<Derived> sd(new Derived);
    test(sd);
    return 0;
}

它确实可以编译而没有编译错误。但是当我运行可执行文件时,我得到了一个核心转储。我认为这是因为转换运算符的成员函数做了一个临时智能,它有一个指向相同 ptr 的指针sd(它的类型是smart<Derived>)。所以 delete 指令运行了两次。更重要的是,在调用 test 之后,我们再也不能使用sd了,因为ptrinsd已经被删除了。
现在我的问题是:

  • 我的想法对吗?还是我的代码和书中的原代码不一样?
  • 如果我的想法是正确的,有什么方法可以做到这一点吗?

非常感谢您的帮助。

4

2 回答 2

0

是的,您已经相当准确地描述了代码的问题。

至于如何使它工作:就像你从浅拷贝遇到问题时一样:做一个深拷贝。也就是说,您需要克隆数据,并让第二个对象指向数据的克隆而不是原始数据,而不是仅仅创建另一个指向相同数据的指针。

或者,使用引用计数指针,并在执行复制时增加引用计数,并在销毁副本时减少引用计数。当计数达到零(而不是之前)时,删除指针数据。

一般来说:避免做所有这些。假设您使用的是相对最新的编译器,标准库应该已经包含 ashared_ptr和 aunique_ptr可以处理您的许多智能指针需求。

于 2013-11-05T03:56:18.293 回答
0

您的解释是正确的,转换运算符将创建一个不同的对象,该对象包含指向同一基础对象的指针。一旦超出范围,它将被销毁,并依次调用delete.

不确定我是否理解最后一个问题,如果你问的是这是否有用,如果正确实施它会很有用。例如,如果不是原始指针并手动分配/删除您正在使用的内存,std::shared_ptr那么它就可以正常工作。在其他情况下,甚至可能没有动态分配的对象......这只是一个工具,在有意义的地方使用它。

于 2013-11-05T03:57:13.127 回答