【问题标题】:Mysterious crash with shared_ptrshared_ptr 的神秘崩溃
【发布时间】:2014-09-20 23:27:11
【问题描述】:

有人可以解释为什么退出内部范围时 main() 中的以下崩溃?我正在使用 Visual Studio 2013。虽然 GCC 4.8.1 一切都很好,但我怀疑代码中有问题。我就是不明白。

#include <iostream>
#include <memory>

class Person;  class PersonProxy;

class PersonInterface {
    public:
        virtual ~PersonInterface() = default;
        virtual PersonProxy* getProxy() const = 0;
        virtual void createProxy (Person*) = 0;
    };

class Person : public PersonInterface {
    private:
        std::string name;
        std::shared_ptr<PersonProxy> proxy;
    public:
        Person() = default;
        explicit Person (const std::string& n) : name(n) {}
    public:
        virtual PersonProxy* getProxy() const override {return proxy.get();}
        inline void createProxy (Person* p);
};

class PersonProxy : public PersonInterface {
    private:
        std::shared_ptr<Person> actual;
    public:
        explicit PersonProxy (Person* p) : actual (std::shared_ptr<Person>(p)) {}
        explicit PersonProxy (std::shared_ptr<Person> p) : actual (p) {}
        void rebind (std::shared_ptr<Person> p) {actual = p;}
        virtual PersonProxy* getProxy() const override {return actual->getProxy();}
        virtual void createProxy (Person* p) override {actual->createProxy(p);}
};

class Girl : public Person {
    public:
        Girl (const std::string& name) : Person (name) {createProxy (this);}
};

inline void Person::createProxy (Person* p) {
    proxy = std::shared_ptr<PersonProxy>(new PersonProxy(p));
}

int main() {
    {
        Girl* a = new Girl("a");
//      std::shared_ptr<Girl> a = std::make_shared<Girl>("a");  // Using this crashes with Visual Studio 2013 on the line 'a->getProxy()->rebind(b);'
        std::shared_ptr<Girl> b = std::make_shared<Girl>("b");
        a->getProxy()->rebind(b);
        std::cout << "rebind succeeded." << std::endl;
    }
    std::cout << "Exited scope." << std::endl;  // Exiting scope crashes with VS 2013.
}

我用 VS2013 得到的错误信息是:

断言失败

_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

【问题讨论】:

  • 代码很小,为什么不用调试器单步调试呢?
  • @CaptainObvlious 真的有用吗?
  • @n.m.可能不会,但我是一个受虐狂,直到我早上吃了一碗果味鹅卵石。
  • 在同一个作用域内声明两个同名变量合法吗?
  • @Ginger。我现在解决了混乱,尽管它与问题无关。这是实际合法的。

标签: c++ shared-ptr


【解决方案1】:

你有两个shared_ptrs 试图拥有同一个指针(他们不知道彼此)。这导致他们俩都试图释放同一个地址。

a 正试图获得this 的全部所有权。但随后您将this 传递给CreateProxy(),这会创建一个 shared_ptr,它试图获得this 的全部所有权。新的shared_ptr 不知道a,所以没有人分享他们的引用计数。 shared_ptrs 需要共享他们的引用计数,而不仅仅是指针本身。

如果你想在两个shared_ptrs 之间共享一个指针,它们需要相互了解(这样它们就可以更新它们的引用计数)。当Girl 调用createProxy() 时,需要将shared_ptr 传递给this

也许现在是使用std::enable_shared_from_this() 的好时机。

【讨论】:

  • 所以 Girl 必须派生自 std::enable_shared_from_this 并在其构造函数中调用 'createProxy(shared_from_this());' ?这意味着 Person 必须有一个 createProxy (std::shared_ptr) 重载?
  • @prestokeys:你为什么要使用所有这些代理的东西。
  • @Cheers。这是一个很长的解释。我已经与一些专业程序员确认我的程序需要它。但是我仍然没有使用 std::enable_shared_from_this 解决上述问题(仍然在挠头)。
  • @prestokeys:你可以,但老实说,这种设计越来越难以遵循,因此重新设计或使用工厂可能会简化逻辑和对象生命周期(从消费者的角度来看)。或者你可以使用弱指针而不是共享指针。
【解决方案2】:

您正在为同一个指针创建多个引用计数实例。
从指针创建新的 shred_ptr 会启动一个新的引用计数器。当引用计数器达到 0 时,shared_ptr 的默认删除器对该指针调用 delete。

因为该指针有多个引用计数器,所以多次调用删除。

【讨论】: