为了说明我的问题,我举了一个例子:
#include <iostream>
using namespace std;
struct A
{
void doSomething (){
cout << "Somethingn";
}
};
struct B
{
A a;
A *getA ()
{
return &a;
}
};
int
main ()
{
B *b = new B ();
A *a = b->getA ();
// POINT 1
if (nullptr != a)
{
a->doSomething ();
}
delete b;
b = nullptr;
// POINT 2
if (nullptr != a)
{
a->doSomething ();
}
return 0;
}
这在我的机器上编译和运行没有错误,但是如果您检查代码,确实存在标记为"POINT 2"的注释后面的行上悬空指针的问题。
由于b
被删除,那么a
现在无效(因为它已被 b
的 dtor 删除)。
所以我可以使用共享指针来解决这个问题,但这会在删除b
后保留a
实例,而且我也无法在堆栈上分配a
。这是我想避免的两件事。相反,我只是想知道a
是否仍然有效。
我也可以使用一个唯一的指针,但是我只能有一个a
实例,这也不是我想要的,我想要许多指向a
的指针副本。
那么是否有一些现有的指针/引用类型可以让我这样做?有什么理由说明这是一个好/坏主意吗?
您刚刚发现了所有权语义的奇迹:)
如何解决这个问题取决于应用程序的设计:你需要什么以及你想要实现什么。
在这种情况下,如果您确实想共享对象的所有权,请使用 std::shared_ptr
它保留剩余指针数的引用计数,以便最后一个删除该对象;如果您只需要检查对象是否仍处于活动状态但不想使其处于活动状态的时间超过需要的时间,则可能会std::weak_ptr
。
但是,请注意(滥用)使用共享指针可能是设计不佳的标志。
顺便说一下,您的A a;
成员没有在堆栈中分配(即标题错误)。
使用标准库的唯一可行解决方案是使用 std::weak_ptr() - 它将允许在不持有其所有权的情况下检查对象的有效性。这是有代价的 - 你必须保持对它的所有权 std::shared_ptr
.尽管可以使用自动存储持续时间和noop删除器创建对象的std::shared_ptr
,但只有在我确实需要时才这样做,因为这种方法容易出错并且违背了智能指针的目的。
最好的方法是不暴露a
。
您的B
是界面。赋予它您需要执行的功能。让它继续调用它需要在a
上调用的任何内容,以便实现这一目标。
然后删除getA()
,并将a
设为私有。
现在它完全封装了,调用范围不能像这样四处乱窜!
不需要指针或动态分配;只是好的,老式的OOP。