理解c++共享指针
Understanding c++ shared pointers
嗨,我正在制作自己的引用计数智能指针,但在开始之前,有两个概念我不太理解。
-
我知道,当创建一个指向对象的共享指针时,我必须为一个结构/类分配内存,该结构/类将包含诸如引用计数(最初为1)之类的信息,可能还有一个用于递增和递减的互斥锁。当我使用=运算符使另一个共享指针也指向这个对象时,我也会将指向这个结构/类的指针传递给那个新指针,这样我就可以增加计数。我的问题是,如果我让第三个共享指针指向这个对象(不使用复制构造函数或=运算符),那么这个指针将不知道结构,因此引用计数将为1,如果我删除指针,则计数将达到0,并且对象将被销毁,而事实上,这个对象还有另外两个指针?
-
如果一个共享指针的引用计数为1,然后创建了多个线程,如果一个线程结束/破坏了它,那么其他可能仍在运行的线程会发生什么?
我知道,当创建一个指向对象的共享指针时,我必须为一个结构/类分配内存,该结构/类将包含诸如引用计数(最初为1)之类的信息,可能还有一个用于递增和递减的互斥锁。
是的,你需要一个柜台
只有在计划多线程时才需要互斥。我会把注意力集中在让计数工作上,首先担心锁定的后记。
当我使用=运算符使另一个共享指针也指向这个对象时,我也会将指向这个结构/类的指针传递给那个新指针,这样我就可以增加计数。
共享指针的意义在于它们拥有指针的所有权。一旦创建了共享指针,就不应该有指向同一对象的RAW指针实例。因此,当你复制或分配时,你是通过共享指针来完成的。
我的问题是,如果我让第三个共享指针指向这个对象(不使用复制构造函数或=运算符),那么这个指针将不知道该结构,因此引用计数将为1。
你的假设是正确的。这就是为什么在创建共享指针时不应该保留指针的副本。这也是引入std::make_shared()
的另一个原因,它分配内存并立即将其封装在智能指针中,因此不会向用户代码返回RAW指针。
如果我删除指针,计数将达到0,对象将被销毁,而事实上,这个对象还有另外两个指针?
这正是问题所在。这就是为什么不应该创建或传递指向已被管理对象的RAW指针。
如果一个共享指针的引用计数为1,然后创建了多个线程,如果一个线程结束/破坏了它,那么其他可能仍在运行的线程会发生什么?
如果您的引用计数正在工作,并且您只有管理RAW指针的共享指针,那么它应该按预期工作。但是如果你在一个线程中删除它,它在所有线程中都会被破坏。
我的问题是,如果我让第三个共享指针指向这个对象(不使用复制构造函数或=运算符)
由于您绕过了复制共享指针的机制,这将是未定义的行为。使用复制构造函数或赋值运算符。
如果一个共享指针的引用计数为1,然后创建了多个线程,如果一个线程结束/破坏了它,那么其他可能仍在运行的线程会发生什么?
这意味着您在其析构函数完成后使用共享指针,这是未定义的行为。确保这些线程有自己的共享指针副本以避免这种情况,或者确保指针在其他线程使用时不会被破坏。
我的问题是,如果我让第三个共享指针指向这个对象(不使用复制构造函数或=运算符),那么这个指针将不知道结构,因此引用计数将为1,如果我删除指针,则计数将达到0,并且对象将被销毁,而事实上,这个对象还有另外两个指针?
对此你真的无能为力。如果你有一个指针,并且你把它交给两个单独的共享指针,那么你会得到一个双重删除。
int * foo = new int(10); // set value to 10
{
std::shared_ptr<int> a(foo);
std::shared_ptr<int> b(foo)
} // uh-oh. we call delete twice
无法将指针标记为已拥有。这取决于编写代码的人不这样做。我们解决这种问题的方法是直接在构造函数中创建指针,这样就不能将其提供给另一个实例
std::shared_ptr<int> a(new int(10));
或者您可以使用std::make_shared
auto b = std::make_shared<int>(10);
现在,我们不再需要担心将原始指针提供给另一个共享指针,并将其从我们下面删除
如果一个共享指针的引用计数为1,然后创建了多个线程,如果一个线程结束/破坏了它,那么其他可能仍在运行的线程会发生什么?
如果将共享指针传递给每个线程,那么所有线程都共享所有权。在所有线程结束并且原始共享指针超出范围之前,指针不会被删除。
-
答案是肯定的。如果从指向对象的普通指针创建共享指针,当该对象已由另一个共享指针拥有时,则当一个引用计数达到0时,该对象将被销毁,其余共享指针将保持悬空状态-它们一直指向对象曾经存在的位置。
这就是为什么智能指针的用户永远不能从本身没有所有权的代码中授予指针的所有权。
-
存在检查和递减引用计数器的竞争条件。如果共享指针可以在多个线程中使用,那么对引用计数器的访问必须跨线程同步。
在一个典型的实现中,如果您为一个对象创建一个shared_pointer
,并为同一对象创建第二个shared_pointer
而不引用已经创建的shared_pointer
,则它们的引用计数都为1,当其中一个超出范围时删除对象,当另一个试图删除对象时会导致未定义的行为。
这是STL具有std::make_shared
和std::make_unique
等工厂对象的主要原因之一,因为确保程序员从不管理原始对象指针有助于防止此类错误。
当然,一个更聪明的实现可能会尝试在静态级别上跟踪已经创建的所有指针&维护,并可能尝试记忆对象。但这也有其自身的风险和问题,所以我不建议这样做。
至于多线程问题:这取决于情况。如果所有线程都是基于原始shared_pointer
对象创建shared_pointer
的,并且您正确地使用mutex
来维护引用计数,那么它们都应该正确地增加引用计数,而不会出现问题,并且对象不应该仅仅因为一个线程停止使用它而被删除。
- 如何从具有移动语义的类对象中生成共享指针
- 使用共享指针的函数调用,其对象应为 const
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- 如何访问由共享指针保存的类方法?
- 从矢量或地图中删除共享指针
- 使用共享指针时,从共享指针本身释放内存的机制是什么
- 使用共享指针实现复制 c'tor?
- C++/QT:使用指向私有成员的常量指针作为只读数据共享
- 共享指针继承,而不先显式强制转换
- 如何检查类中共享指针的有效性?
- 共享 C++ 的数据成员指针
- std::排序在共享指针的向量上
- 将相同共享指针的副本存储在不同的向量中是否是一种好的做法?
- 在什么情况下,需要共享智能指针而无法使用唯一指针?
- 嵌套类、继承和C++中的共享指针
- <Base> <Derived> 具有相同原始指针共享引用的 shared_ptr 和 shared_ptr 实例是否计数?
- 重载流<<运算符,用于指针/共享指针和其他类型的
- RAW指针共享_ptr
- 指针共享习语
- 将memmove函数与临时指针共享相同的地址