我无法理解智能指针在C ++ 11中作为类成员的用法。我已经阅读了很多关于智能指针的内容,我想我确实理解unique_ptr
和shared_ptr
/ weak_ptr
的工作原理。我不明白的是真正的用法。似乎每个人都建议使用unique_ptr
作为几乎所有时间的方式。但是,我将如何实现这样的事情:
class Device {
};
class Settings {
Device *device;
public:
Settings(Device *device) {
this->device = device;
}
Device *getDevice() {
return device;
}
};
int main() {
Device *device = new Device();
Settings settings(device);
// ...
Device *myDevice = settings.getDevice();
// do something with myDevice...
}
假设我想用智能指针替换指针。由于unique_ptr
,getDevice()
无效,对吧?那是我使用shared_ptr
和weak_ptr
的时候?无法使用unique_ptr
?在我看来,大多数情况shared_ptr
更有意义,除非我在一个非常小的范围内使用指针?
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(std::shared_ptr<Device> device) {
this->device = device;
}
std::weak_ptr<Device> getDevice() {
return device;
}
};
int main() {
std::shared_ptr<Device> device(new Device());
Settings settings(device);
// ...
std::weak_ptr<Device> myDevice = settings.getDevice();
// do something with myDevice...
}
这是要走的路吗?非常感谢!
答案 0 :(得分:181)
由于
unique_ptr
,getDevice()
无效,对吗?
不,不一定。这里重要的是为您的Device
对象确定适当的所有权政策,即谁将成为您的(智能)指针指向的对象的所有者。
它将是Settings
对象单独的实例吗?当Device
对象被破坏时,Settings
对象是否必须自动销毁,还是应该比该对象更长?
在第一种情况下,std::unique_ptr
是您所需要的,因为它使Settings
成为指向对象的唯一(唯一)所有者,并且是唯一负责其销毁的对象。
在此假设下,getDevice()
应该返回一个简单的观察指针(观察指针是指针不能使指向的对象保持活动状态)。最简单的观察指针是原始指针:
#include <memory>
class Device {
};
class Settings {
std::unique_ptr<Device> device;
public:
Settings(std::unique_ptr<Device> d) {
device = std::move(d);
}
Device* getDevice() {
return device.get();
}
};
int main() {
std::unique_ptr<Device> device(new Device());
Settings settings(std::move(device));
// ...
Device *myDevice = settings.getDevice();
// do something with myDevice...
}
[注1: 你可能想知道为什么我在这里使用原始指针,当时每个人都在告诉原始指针是坏的,不安全的和危险的。实际上,这是一个宝贵的警告,但重要的是将它置于正确的上下文中:原始指针在用于执行手动内存管理时不好,即通过new
分配和释放对象和delete
。当纯粹用作实现引用语义的方法并传递非拥有,观察指针时,原始指针中没有任何本质上的危险,除非可能因为人们应该注意不要取消引用悬空指针。 - 结束注1 ]
[注2: 正如评论中出现的那样,在这种特殊情况下,所有权是唯一的和始终保证拥有的对象存在(即内部数据成员device
永远不会是nullptr
),函数getDevice()
可能(也许应该)返回引用而不是指针。虽然这是真的,但我决定在这里返回一个原始指针,因为我认为这是一个简短的答案,可以概括为device
可能是nullptr
的情况,并显示原始指针是好的,只要不使用它们进行手动内存管理。 - END NOTE 2 ]
当然,如果您的Settings
对象不拥有该设备的独占所有权,则情况完全不同。例如,如果Settings
对象的销毁不应该意味着对Device
对象的破坏,则可能就是这种情况。
只有你作为你的程序设计师才能说出来;从你提供的例子中,我很难判断是否是这种情况。
为了帮助您解决这个问题,您可能会问自己除了Settings
之外是否有任何其他对象有权保持Device
对象存活,只要它们指向它,而不仅仅是被动的观察者。如果确实如此,那么您需要共享所有权政策,这是std::shared_ptr
提供的内容:
#include <memory>
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(std::shared_ptr<Device> const& d) {
device = d;
}
std::shared_ptr<Device> getDevice() {
return device;
}
};
int main() {
std::shared_ptr<Device> device = std::make_shared<Device>();
Settings settings(device);
// ...
std::shared_ptr<Device> myDevice = settings.getDevice();
// do something with myDevice...
}
注意,weak_ptr
是观察指针,而不是拥有指针 - 换句话说,如果指向对象的所有其他拥有指针的话,它不会使指向的对象保持活动状态超出范围。
weak_ptr
优于常规原始指针的优点是,您可以安全地判断weak_ptr
是否悬空(即它是否指向有效对象,或者如果最初指向的对象已被销毁)。这可以通过调用expired()
对象上的weak_ptr
成员函数来完成。
答案 1 :(得分:0)
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(const std::shared_ptr<Device>& device) : device(device) {
}
const std::shared_ptr<Device>& getDevice() {
return device;
}
};
int main()
{
std::shared_ptr<Device> device(new Device());
Settings settings(device);
// ...
std::shared_ptr<Device> myDevice(settings.getDevice());
// do something with myDevice...
return 0;
}
week_ptr
仅用于参考循环。依赖图必须是非循环定向图。在共享指针中有2个引用计数:1表示shared_ptr
s,1表示所有指针(shared_ptr
和weak_ptr
)。删除所有shared_ptr
后,将删除指针。当weak_ptr
需要指针时,lock
应该用于获取指针(如果存在)。