3

在我的代码中,我有一个动态分配的普通指针

class Texture
{
    //code...
    SDL_Texture* m_texture;
};

我从 Texture 类分配了 SDL_Texture* 的 Init() 函数,并且析构函数释放了内存。

然而,这被证明是一个问题。我必须使用 shared_ptr,因为我对不同的对象使用相同的纹理,这将被破坏,因此不允许我正确显示它们(在向量中的第一个对象被破坏后,纹理(为所有相同类型的对象共享) ) 离开了)。

所以我决定使用 shared_ptr,但我有一个问题 - 我不知道如何为 shared_ptr's 分配一些东西:

//Old code:
m_texture = loadTexture();

//New code:

class Texture
{
   std::shared_ptr<SDL_Texture> m_texture;
};
//code...

m_texture = loadTexture(); // Error! No assignment operator

我试过 .get() 函数,但它是右值,所以我不能给它分配任何东西。有任何想法吗?

提前致谢。

4

2 回答 2

4

std::shared_ptr不允许您直接为其分配一个普通指针。当您考虑时,有很多令人信服的理由来禁止它隐含地发生。首先,shared_ptr需要分配一个(通常是外部的)引用计数器。例如,考虑以下(假设的)代码:

std::shared_ptr<A> p1 = obj;
std::shared_ptr<A> p2 = obj;

当指针超出范围时会发生什么?每个赋值都会创建自己的引用计数器,因为p2无法知道由p1. 实际上,obj被删除了两次 - UB。

首选的解决方案是使用库函数std::make_shared。与包装外部创建的对象相比,它提供了许多好处:

  • 计数器与对象在同一个堆块中创建,因此减少了内存分配的开销,提高了内存的一致性
  • 它提供了更高的异常安全性 -在此处查找有关它避免的问题的良好参考

另一种选择是shared_ptrstd::enable_shared_from_this. 此类对象维护必要的结构作为数据成员,因此make_shared保留了 的第一个好处。此外,shared_ptr以第一个示例中描述的方式初始化 's 变得有效,因为构造函数/复制赋值运算符可以识别std::enable_shared_from_this子类并使用内部结构进行簿记,因此两者都p1p2使用存储在内部的相同引用计数器obj

但是,有时,例如在这种情况下,不可能使用上述任何策略。如果真的需要shared_ptr直接设置a,有一种方法可以做到—— reset(). 但是,请谨慎使用它,并确保尽快将指针包装在 ashared_ptr中,然后让它管理它。特别是我认为将此类资源的创建封装在一个方法中并且从不将包装的指针暴露在它之外是一种很好的做法。

于 2013-09-08T11:13:21.233 回答
4

您还需要一个自定义删除器:

#include <memory>

// Fake SDL library

typedef void SDL_Texture;

SDL_Texture* SDL_CreateTextureFromSurface(/*SDL_Renderer* , SDL_Surface* */) {
    return 0;
}

void SDL_DestroyTexture(SDL_Texture* texture)
{}

// Shared

typedef std::shared_ptr<SDL_Texture> SharedTexture;

inline SharedTexture make_shared(SDL_Texture* texture) {
    return SharedTexture(texture, SDL_DestroyTexture);
}

int main() {
    SharedTexture texture;
    texture = make_shared(SDL_CreateTextureFromSurface());
}

请注意,C 库:

  • 可以使用不透明类型
  • 有自己的删除功能或正在免费使用
于 2013-09-08T11:34:25.573 回答