为类成员使用智能指针

在 C + + 11中,我很难理解智能指针作为类成员的用法。我读了很多关于智能指针的文章,我认为我理解 unique_ptrshared_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...
}

假设我想用智能指针替换指针。因为 getDevice()unique_ptr不会工作,对吗?这就是我使用 shared_ptrweak_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...
}

是这样吗? 非常感谢!

107473 次浏览
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_ptrs,1个用于所有指针(shared_ptrweak_ptr)。删除所有 shared_ptr时,指针将被删除。当需要从 weak_ptr获取指针时,如果指针存在,则应使用 lock获取指针。

因为 getDevice()unique_ptr不会工作,对吗?

不,不一定。这里重要的是为 Device对象确定合适的 所有权政策,也就是说,谁将成为(智能)指针指向的对象的所有者。

它是 Settings对象 一个人的实例吗?当 Settings对象被破坏时,Device对象是否必须被自动破坏,或者它是否应该比该对象存活得更久?

在第一种情况下,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: 你可能想知道我为什么要在这里使用原始指针,因为每个人都一直在说原始指针是不好的、不安全的和危险的。实际上,这是一个宝贵的警告,但是把它放在正确的上下文中是很重要的: 当用于执行手动内存管理时,原始指针是坏的 < strong > ,即通过 ABC0和 delete分配和释放对象。当纯粹作为实现引用语义和传递非拥有、观察指针的手段时,原始指针本质上并没有什么危险,也许除了一个事实,那就是人们应该注意不要取消引用一个迷途指针。-结束注1]

[ 注2: 正如在评论中出现的那样,在所有权是唯一的 < strong > 和 的特殊情况下,所有权对象总是保证存在(即内部数据成员 ABC0永远不会是 nullptr) ,函数 ABC2可以(也许应该)返回引用而不是指针。虽然这是真的,但我决定在这里返回一个原始指针,因为我的意思是这是一个简短的答案,可以推广到 ABC0可以是 nullptr的情况,并表明原始指针是可以的,只要不使用它们进行手动内存管理。-结束注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是否为 晃来晃去(即它是否指向一个有效的对象,或者最初指向的对象是否已被销毁)。这可以通过调用 weak_ptr对象上的 expired()成员函数来完成。