防止对同一对象进行多次shared_ptr的最佳保护
Best way to protect from multiple shared_ptr to the same object
同一指针发送到两个不同的shared_ptr
是不好的,它会导致双重释放,如下所示:
int* p = new int;
std::shared_ptr<int> p1(p);
std::shared_ptr<int> p2(p); // BAD
您可以使用std::enable_shared_from_this
实现相同的目的:
class Good: public std::enable_shared_from_this<Good>
{
public:
std::shared_ptr<Good> getptr() {
return shared_from_this();
}
};
int main()
{
std::shared_ptr<Good> gp1(new Good);
std::shared_ptr<Good> gp2 = gp1->getptr();
}
但这仍然不能防止:
class Good: public std::enable_shared_from_this<Good>
{
public:
std::shared_ptr<Good> getptr() {
return shared_from_this();
}
};
int main()
{
Good* p = new Good;
std::shared_ptr<Good> gp3(p);
std::shared_ptr<Good> gp4(p); // BAD
}
如果您有这样的代码,这可能会成为一个问题:
void Function(std::shared_ptr<Good> p)
{
std::cout << p.use_count() << 'n';
}
int main()
{
Good* p = new Good;
std::shared_ptr<Good> p1(p);
Function(p); // BAD
}
当有智能指针时,为什么我会使用常规指针?因为在性能关键型代码中(或为方便起见),shared_ptr或weak_ptr的开销是不可取的。
为了防止这个错误,我做了:
class CResource : public shared_ptr<Good>
{
public:
CResource()
{
}
CResource(std::shared_ptr<CBaseControl> c)
: CResource(c)
{
}
private:
CResource(CBaseControl* p)
{
}
};
void Function(CResource p)
{
std::cout << p.use_count() << 'n';
}
int main()
{
Good* p = new Good;
CResource p1(std::shared_ptr<Good>(p));
Function(p); // Error
}
如果有人尝试使用指针而不是shared_ptr
调用Function
,这将导致编译器出错。但这并不能阻止某人宣布void Function(std::shared_ptr p)
,但我认为这不太可能。
这还不好吗?有没有更好的方法
解决方案很简单:永远不要让原始指针拥有自己的内存。此模式:
int* p = new int;
std::shared_ptr<int> p1(p);
std::shared_ptr<int> p2(p); // BAD
根本不应该存在。从代码库中根除它。new
在 C++11 中唯一合法的地方是作为构造函数调用智能指针的参数(或在非常低级的东西中)。
即有这样的代码:
std::shared_ptr<int> p1(new int);
或者更好的是(不再涉及裸new
):
auto p1 = std::make_shared<int>();
请注意,在代码中使用原始指针是可以的(但我甚至在我见过的大多数C++代码中都会质疑这一点)。但是,如果您使用原始指针,请不要让它们拥有内存。要么指向自动存储(无需资源管理),要么使用 unique_ptr
并通过其 get
成员函数访问原始指针。
这个例子甚至没有编译:
void Function(std::shared_ptr<Good> p)
{
std::cout << p.use_count() << 'n';
}
int main()
{
Good* p = new Good;
std::shared_ptr<Good> p1(p);
Function(p); // BAD
}
shared_ptr
有一个explicit
构造函数来阻止这种情况的发生。
要进行编译,您需要编写:
Function( std::shared_ptr<Good>(p) );
这显然是错误的,如果有人要犯这个错误,他们很可能会这样做:
Function( CResource(std::shared_ptr<Good>(p)) );
那你为什么要费心写CResource
呢?它增加了什么?
为了扩展康拉德·鲁道夫的出色答案:
您关于如何避免问题的问题的答案是遵循 RAII 习语,但要完全遵循它。
我们将忽略甚至无法编译的示例,并查看上面的示例:
Good* p = new Good;
std::shared_ptr<Good> gp3(p);
std::shared_ptr<Good> gp4(p); // BAD
该代码无法遵循 RAII 惯用法。 您获取的资源:
Good* p = new Good;
但不要初始化 RAII 类型。巴德。
然后,使用一些现有资源初始化对象:
std::shared_ptr<Good> gp3(p);
这也是 BAD。您应该在获取资源的同时初始化 RAII 类型,而不是单独初始化(甚至不能只用一行分隔)。
然后重复相同的错误:
std::shared_ptr<Good> gp4(p); // BAD
您已将此行标记为"BAD",但实际上前两行同样糟糕。 第三行将导致未定义的行为,但前两行允许错误蔓延,而本应使其更加困难。 如果你从来没有Good*
挂在身边,那么你就不能用它来初始化gp4
,你需要说shared_ptr<Good> gp4(gp3.get())
这显然是错误的,没有人会这样做。
规则很简单:不要拿一个原始指针把它放在shared_ptr
里。未分配的原始指针不是资源获取,因此不要将其用于初始化。这同样适用于Function
,它不应该获取原始指针并使用它来初始化获得类型所有权的shared_ptr
。
这是C++,所以不可能防止所有错误的代码编写方式,你不能阻止有足够动机的白痴搬起石头砸自己的脚,但所有的指导方针和工具都在那里防止它,如果你遵循指导方针。
如果你有一个接口,通过传递一个原始指针来强制你转移所有权,尝试替换接口,以便它使用unique_ptr
进行所有权转移,或者如果完全不可能做到这一点,那么尝试使用unique_ptr
将接口包装在更安全的版本中进行所有权转让,并且只是作为最后的手段, 使用危险界面,但非常明确地记录所有权已转移。
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- dopen():不以 root 身份运行时"failed to map segment from shared object"
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 无法使用 libtool 将 -shared 参数传递给 g++
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- shared-ptr-C++shared_ptr与unique_ptr用于资源管理