我试图理解类处理 * 另一个 * 类的对象的有效性(引用)的首选方法。
在这里,C
有一个存储D
对象引用的向量。如果D
和C
是库的一部分,C
应该如何处理D
对象超出调用者范围的情况?
我有几种方法,虽然不确定可行性:
D
知道_list
存储了什么,并且一旦所述D
超出范围,~D()
就运行,其将自身从_list
中移除。_list
存储weak_ptr
,而不是raw,并且在访问D
时,在访问之前调用weak_ptr::lock()
,尽管这将需要示例化shared_ptr
,而这在生产中似乎并不常见。
struct D
{
~D()
{
printf ("~D()\n");
}
};
class C
{
vector<D*> _list;
public:
void add(D* dObject)
{
_list.push_back(dObject);
printf ("Adding D => size = %ld\n", _list.size());
}
~C()
{
printf ("~C()\n");
}
};
int main()
{
C c1;
{
D d1;
c1.add(&d1);
}
/**
_list[0] is garbage now. How to avoid accessing it i.e
C being aware to not access it?
*/
printf ("----out of scope---\n");
D d2;
c1.add(&d2);
}
3条答案
按热度按时间ltqd579y1#
备选项为
1.使用scope_exit(也称为作用域保护):
你应该在
D
中使用std::set
而不是std::vector
来更有效地查找和删除条目。当然D
应该公开提供erase
或delete
函数。scope_exit
是即将发布的C++库基础v3技术规范(https://en.cppreference.com/w/cpp/experimental/scope_exit)的一部分。是的。1.创建一个自定义类,用于管理从两个类外部添加和自动删除(也在范围退出时)指向列表的指针和从列表中删除指针:
您也可以将
D
储存在E
中,而不是透过储存的指涉来指涉它。顺便说一句:本地范围的销毁顺序与施工顺序相反。
这两个解决方案保持
C
和D
不变。您必须自己决定,C
是否应该以入侵方式了解其D
对象,还是像这两个解决方案那样以非入侵方式处理其在D
中的添加和删除。对于对象存储在本地并且销毁时间明确的情况(在结束时或由于异常或
return
语句而离开作用域时),我不会使用shared_ptr
/weak_ptr
。omhiaaxx2#
基本上在c++中,你不应该保留栈示例对某些容器的引用。悬空内存访问是危险的,你应该把在堆上创建的示例添加到你喜欢的容器中。在这种情况下,shared_ptr对于管理示例的生存期是有用和安全的。
输出:
附加:按块范围删除对象
9nvpjoqh3#
从本质上讲,最初的问题是问:“如何知道局部变量何时超出范围?”
答案是:当调用其析构函数时。
对于您问题中的代码,您可以让
~D()
从c1
中删除自身。这很尴尬,问题下面的讨论会提到std::shared_ptr
,但示例中的代码使用了局部变量。您不能在局部变量上使用std::shared_ptr
,只能在堆变量上使用。这在讨论中没有明确说明。以下是与shared_ptr和局部变量特别相关的问题的两个参考:
Create shared_ptr to stack object
和
Set shared_ptr to point existing object
更好的解决方案是不使用局部变量,而使用堆变量。
局部变量和堆变量之间的区别似乎会引起一些混乱。
C++有几个变量存储类:
C++对
new
和delete
有一些技巧,在这些技巧中,您可以使用自定义堆,即您自己管理的内存,但该内存将从通用堆中分配,或者可能存在于其他变量中。下面是三个不同的示例:
局部变量:
当这个函数退出时,包含d1的堆栈帧消失,变量也不再存在。
~D()
将被调用,如果你添加了一个机制,比如让d1
持有一个指向c1
的指针,你可以从c1中删除d1,等等。std::shared_ptr
对局部变量没有帮助。使用new分配的堆变量:
当这个函数退出时,
d1p
指向的D
变量仍然存在。它将继续存在,直到它被显式删除。在这个上下文中,c1
变量“拥有”这个指针,并且它负责在使用完它后删除它。在这种情况下,添加智能指针可以简化引用管理,如下所示:
通过make_shared创建的堆变量和shared_ptr:
此示例在堆上创建一个
D
变量,并创建一个本地shared_ptr
,该本地shared_ptr
通过一个小计数器对象(也在堆上)保存对该D
变量的引用。当shared_ptr
传递给C::add
方法时,计数器对象中的引用计数将递增。当shared_ptr
超出范围时,计数器对象中的引用计数递减。c1
对象中的shared_ptr
现在保存对该shared_ptr
的唯一引用,当c1
对象被销毁时,它所包含的shared_ptr
也将被销毁,释放其引用--如果计数变为零,它所指向的对象也将被销毁。除了这种自动销毁之外,这种方法的另一个优点是,您可以从
c1
获取对D
变量的引用,并在其他地方使用它们,这些引用(以及D
变量本身)可以比c1
更持久。C
类必须针对这种情况进行修改,例如和
以下是共享指针工作方式的参考:
How do shared pointers work?
和一些关于make_shared文档:
https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared