2

我在一个项目中使用 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在这里有所作为,为什么编译器不给出警告/错误呢?

4

2 回答 2

5

存在public很重要,因为shared_ptr系统需要访问enable_shared_from_this给定类型的基类。public如果不能从给定类型访问它,它就不能这样做。

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

即使是私有的,使用shared_ptr可以“启用”的构造函数在概念上是可以的。为什么?考虑以下:shared_from_thisenable_shared_from_this

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

class D : private B {...};

现在,B期望能够做shared_from_this体操。但D私下继承自它。SoDB(因此与B::shared_from_this)的关系是私有的。也许D正在以B不会触发任何使用的方式shared_from_this使用。

所以 ifD不依赖于B::shared_from_this, ifB只是一个实现细节,如果有人把 a放在a 中D,为什么会出错?Dshared_ptr

没有任何测试你能想出不会导致这样的误报。因此,如果enable_shared_from_this基类不可访问,那么shared_ptr可以尝试使用它的构造函数就不会尝试使用它。

于 2019-06-10T16:11:02.110 回答
3

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

的一个常见实现enable_shared_from_this是持有对 的弱引用(例如std::weak_ptrthis。构造函数std::shared_ptr检测是否存在明确且可访问的(C++17 起)基,如果还没有被活体拥有(C++17 起) enable_shared_from_this,则将新创建的基分配给内部存储的弱引用。std::shared_ptrstd::shared_ptr

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

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

于 2019-06-10T16:10:01.380 回答