简体   繁体   English

这是Item28中“更有效的C ++”中的错误吗?

[英]Is this an error in “More Effective C++” in Item28?

I encountered a question when I was reading the item28 in More Effective C++ . 在阅读更有效的C ++中的item28时遇到一个问题。 In this item, the author shows to us that we can use member template in SmartPtr such that the SmartPtr<Cassette> can be converted to SmartPtr<MusicProduct> . 在此项目中,作者向我们展示了我们可以在SmartPtr使用成员模板,以便将SmartPtr<Cassette>转换为SmartPtr<MusicProduct>
The following code is not the same as in the book, but has the same effect. 以下代码与书中的代码不同,但是具有相同的效果。

#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;
}

It indeed can be compiled without compilation error. 它确实可以编译而没有编译错误。 But when I ran the executable file, I got a core dump. 但是当我运行可执行文件时,我得到了一个核心转储。 I think that's because the member function of the conversion operator makes a temporary smart, which has a pointer to the same ptr in sd (its type is smart<Derived> ). 我认为这是因为转换运算符的成员函数使一个临时智能,它具有指向sd相同ptr的指针(其类型为smart<Derived> )。 So the delete directive operates twice. 因此,删除指令会运行两次。 What's more, after calling test, we can never use sd any more, since ptr in sd has already been delete. 而且,调用test之后,我们将再也无法使用sd了,因为sd中的ptr已经被删除了。
Now my questions are : 现在我的问题是:

  • Is my thought right? 我的想法对吗? Or my code is not the same as the original code in the book? 还是我的代码与书中的原始代码不同?
  • If my thought is right, is there any method to do this? 如果我的想法是正确的,有什么方法可以做到这一点?

Thanks very much for your help. 非常感谢您的帮助。

Yes, you've described the problem with your code fairly accurately. 是的,您已经用代码相当准确地描述了问题。

As far as how to make it work: just about like the usual when you run into problems from a shallow copy: do a deep copy instead. 至于如何使它起作用:就像从浅表副本遇到问题时的通常情况一样:进行深表副本。 That is, instead of just creating another pointer to the same data, you'd need to clone the data, and have the second object point to the clone of the data instead of the original data. 也就是说,您不仅需要克隆指向相同数据的指针,还需要克隆数据,并使第二个对象指向数据的克隆,而不是原始数据。

Alternatively, use a reference counted pointer, and increment the reference count when you do a copy, and decrement it when a copy is destroyed. 或者,使用引用计数的指针,并在执行副本时增加引用计数,在销毁副本时减少引用计数。 When the count reaches zero (and not before) delete the pointee data. 当计数达到零(而不是之前)时,删除pointee数据。

Generally speaking: avoid doing all of this. 一般来说:避免做所有这一切。 Assuming you're using a relatively up-to-date compiler, the standard library should already contain a shared_ptr and a unique_ptr that can handle a lot of your smart pointer needs. 假设您使用的是相对最新的编译器,则标准库应该已经包含一个shared_ptrunique_ptr ,它们可以处理您的许多智能指针需求。

Your interpretation is correct, the conversion operator will create a different object that holds a pointer to the same underlying object. 您的解释是正确的,转换运算符将创建一个不同的对象,该对象持有指向同一基础对象的指针。 Once it goes out of scope it will be destroyed, and it will in turn call delete . 一旦超出范围,它将被销毁,并依次调用delete

Not sure I understand the last question, if what you ask is whether this can be useful or not, it can be useful if implemented correctly. 不确定我是否理解最后一个问题,如果您要问的是这是否有用,那么如果正确实现,它将很有用。 For example, if instead of a raw pointer and manually allocating/deleting the memory you were using a std::shared_ptr then it would work just fine. 例如,如果您使用的是std::shared_ptr而不是原始指针并手动分配/删除内存,那么它将正常工作。 In other cases there might not even be a dynamically allocated object... This is just a tool, use it where it makes sense. 在其他情况下,甚至可能没有动态分配的对象……这只是一种工具,请在有意义的地方使用它。

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

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