如果继承不是公开的,而不是错误的,为什么enable_shared_from_this崩溃

时间:2019-06-10 15:59:18

标签: c++ shared-ptr private-inheritance enable-shared-from-this

我在一个项目中使用了shared_ptr。有一次,我不得不将原始指针存储为void,然后在传递了void *的回调中将其转换回其shared_ptr形式。但是由于某种原因,代码不断崩溃。我不明白为什么,因为我没有收到任何编译器错误或警告。但是我注意到,当我从std::enable_shared_from_this继承时,我并未将其指定为公共继承。这就是导致崩溃的原因。

我写了一个示例代码,我只是想知道为什么会发生这种情况。

#include <memory>
#include <iostream>

class TestShared : public std::enable_shared_from_this<TestShared>{
private:
    int32_t id;
public:
    TestShared(int32_t id){
        this->id = id;
    }
    std::shared_ptr<TestShared> getshared(){
        return shared_from_this();
    }
    int32_t getid(){
        return id;
    }
};

int main(){
    std::shared_ptr<TestShared> ts(new TestShared(0xFF));
    void* tsp = ts.get();
    std::shared_ptr<TestShared> tsn = ((TestShared*)tsp)->getshared();
    std::cout << std::hex << tsn->getid();
    return 0;
}

这样,代码将可以正常执行并运行,我得到了预期的结果。

但是当我从继承中删除public:

#include <memory>
#include <iostream>

class TestShared : std::enable_shared_from_this<TestShared>{
private:
    int32_t id;
public:
    TestShared(int32_t id){
        this->id = id;
    }
    std::shared_ptr<TestShared> getshared(){
        return shared_from_this();
    }
    int32_t getid(){
        return id;
    }
};

int main(){
    std::shared_ptr<TestShared> ts(new TestShared(0xFF));
    void* tsp = ts.get();
    std::shared_ptr<TestShared> tsn = ((TestShared*)tsp)->getshared();
    std::cout << std::hex << tsn->getid();
    return 0;
}

然后导致崩溃。那么为什么public在这里有所作为,为什么编译器没有给出警告/错误?

2 个答案:

答案 0 :(得分:3)

根据https://en.cppreference.com/w/cpp/memory/enable_shared_from_this

  

enable_shared_from_this的常见实现是保留对std::weak_ptr的弱引用(例如this)。 std::shared_ptr的构造函数检测到存在明确且可访问的(自C ++ 17起)enable_shared_from_this基,并将新创建的std::shared_ptr分配给内部存储的弱引用(如果尚未由其拥有)实况std::shared_ptr(自C ++ 17起)。

如果继承是公共的,则在初始化ts时,它将在enable_shared_from_this基础子对象中“记录”它是TestShared对象的所有者。以后调用getshared时,将查询基础子对象,并创建一个与shared_ptr共享所有权的新ts对象。

如果继承不是公共的,则初始化ts时,它不会“知道”有一个enable_shared_from_this子对象需要写入。因此,在调用getshared时,enable_shared_from_this子对象不包含有关谁当前拥有该对象的任何信息。在C ++ 17中,这会导致异常。在C ++ 17之前,结果是不确定的。

答案 1 :(得分:3)

成为public很重要,因为shared_ptr系统需要访问给定类型的enable_shared_from_this基类。如果无法从给定类型public进行访问,则无法做到这一点。

对于不可访问的基类没有警告/错误,因为系统无法知道您的代码是错误的。

从概念上讲,即使shared_ptr是私有的,也可以使用可以“启用shared_from_this”的enable_shared_from_this构造函数。为什么?请考虑以下内容:

class B : public enable_shared_from_this<B> {...};

class D : private B {...};

现在,B希望能够进行shared_from_this体操。但是D是从那里秘密继承的。因此,DB(并因此与B::shared_from_this)的关系是私有的。也许D使用B的方式不会触发任何shared_from_this的使用。

因此,如果D不依赖B::shared_from_this,并且如果B只是D的实现细节,那么如果有人放置{{ 1}}中的D中?

您无法进行测试,不会导致像这样的误报。因此,如果无法访问shared_ptr基类,那么可以尝试使用它的enable_shared_from_this构造函数将不会尝试使用它。