为类成员使用智能指针 [英] Using smart pointers for class members

查看:76
本文介绍了为类成员使用智能指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解C ++ 11中智能指针作为类成员的用法。我已经阅读了很多有关智能指针的文章,我想我确实理解 unique_ptr shared_ptr / weak_ptr 通常可以正常工作。我不了解实际用途。似乎每个人都建议使用 unique_ptr 作为几乎所有时间的方式。但是我将如何实现这样的东西:

  class Device {
};

类别设置{
Device * device;
public:
设置(设备*设备){
this-> device = device;
}

Device * getDevice(){
return device;
}
};

int main(){
Device * device = new Device();
设置设置(设备);
// ...
设备* myDevice = settings.getDevice();
//使用myDevice进行操作...
}

让我说想要用智能指针替换指针。由于 getDevice() unique_ptr 不起作用,对吗?那是我使用 shared_ptr weak_ptr 的时候吗?无法使用 unique_ptr 吗?在我看来,大多数情况下 shared_ptr 更有意义,除非我在很小的范围内使用指针?

 类设备{
};

类设置{
std :: shared_ptr< Device>设备;
public:
设置(std :: shared_ptr< Device>设备){
this-> device = device;
}

std :: weak_ptr< Device> getDevice(){
返回设备;
}
};

int main(){
std :: shared_ptr< Device>设备(新设备());
设置设置(设备);
// ...
std :: weak_ptr< Device> myDevice = settings.getDevice();
//使用myDevice做某事...
}

要走的路?非常感谢!

解决方案


A unique_ptr 由于 getDevice()而无法使用,对吗?


否,不必要。这里重要的是为您的 Device 对象确定适当的所有权策略,即谁将成为由指向的对象的所有者您的(智能)指针。



是否将成为 Settings 对象 的实例?当 Settings 对象被销毁时,是否必须自动销毁 Device 对象,还是应该使该对象失效?



在第一种情况下,您需要 std :: unique_ptr ,因为它使 Settings 指向对象的唯一(唯一)所有者,也是唯一负责其销毁的对象。



在此假设下, getDevice()应该返回一个简单的 observing 指针(指针是不会使指向对象保持活动状态的指针)。最简单的观察指针是原始指针:

  #include< memory> 

类设备{
};

类设置{
std :: unique_ptr< Device>设备;
public:
Settings(std :: unique_ptr< Device> d){
device = std :: move(d);
}

Device * getDevice(){
return device.get();
}
};

int main(){
std :: unique_ptr< Device>设备(新设备());
设置设置(std :: move(device));
// ...
设备* myDevice = settings.getDevice();
//使用myDevice做某事...
}

[注意1: 您可能想知道为什么我在这里使用原始指针,而每个人都在不断告诉原始指针不好,不安全和危险。实际上,这是一个宝贵的警告,但是将其放在正确的上下文中很重要:当用于执行手动内存管理时,原始指针很糟糕,即通过新建和删除。当纯粹用作实现引用语义并绕过非所有观察指针的一种手段时,原始指针没有内在的危险,除了可能要注意的是,不要小心取消悬空指针的引用。 - END注1 ]



[注意2: 评论中出现的内容在特定情况下所有权是唯一的并且总是保证拥有的对象存在(即内部数据成员 device 永远不会 nullptr ),函数 getDevice()可以(也许应该)返回引用而不是指针。虽然这是事实,但我决定在此处返回一个原始指针,因为这是一个简短的答案,可以推广到 device 可能是<$ c $的情况c> nullptr ,并表明只要原始指针不使用它们进行手动内存管理就可以。- END NOTE 2 ] - END NOTE 2 ] p>




当然,如果您的 Settings 对象的情况则完全不同应拥有该设备的专有所有权。例如,如果 Settings 对象的破坏不意味着所指向的 Device 对象。



这是只有程序设计人员才能知道的事情;从您提供的示例中,我很难判断是否是这种情况。



为帮助您解决问题,您可能会问自己是否有除 Settings 外的其他任何有权保持 Device 对象的对象,只要它们持有指向该对象的指针即可,而不仅仅是被动的观察者。如果确实如此,那么您需要一个共享所有权政策,这是 std :: shared_ptr 提供的内容:

  #include< memory> 

类设备{
};

类设置{
std :: shared_ptr< Device>设备;
public:
Settings(std :: shared_ptr< Device> const& d){
device = d;
}

std :: shared_ptr< Device> getDevice(){
返回设备;
}
};

int main(){
std :: shared_ptr< Device>设备= std :: make_shared< Device>();
设置设置(设备);
// ...
std :: shared_ptr< Device> myDevice = settings.getDevice();
//使用myDevice进行操作...
}

注意, weak_ptr 是一个观察指针,不是一个拥有的指针-换句话说,如果指向该对象的所有其他拥有的指针,它不会使指向的对象保持活动状态尖的对象超出范围。



与常规原始指针相比, weak_ptr 的优势在于,您可以安全地判断 weak_ptr 是否悬空(即是否指向有效对象,或者最初指向的对象是否已被破坏)。这可以通过调用 weak_ptr 对象上的 expired()成员函数来完成。


I'm having trouble understanding the usage of smart pointers as class members in C++11. I have read a lot about smart pointers and I think I do understand how unique_ptr and shared_ptr/weak_ptr work in general. What I don't understand is the real usage. It seems like everybody recommends using unique_ptr as the way to go almost all the time. But how would I implement something like this:

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...
}

Let's say I would like to replace the pointers with smart pointers. A unique_ptr would not work because of getDevice(), right? So that's the time when I use shared_ptr and weak_ptr? No way of using unique_ptr? Seems to me like for most cases shared_ptr makes more sense unless I'm using a pointer in a really small scope?

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...
}

Is that the way to go? Thanks very much!

解决方案

A unique_ptr would not work because of getDevice(), right?

No, not necessarily. What is important here is to determine the appropriate ownership policy for your Device object, i.e. who is going to be the owner of the object pointed to by your (smart) pointer.

Is it going to be the instance of the Settings object alone? Will the Device object have to be destroyed automatically when the Settings object gets destroyed, or should it outlive that object?

In the first case, std::unique_ptr is what you need, since it makes Settings the only (unique) owner of the pointed object, and the only object which is responsible for its destruction.

Under this assumption, getDevice() should return a simple observing pointer (observing pointers are pointers which do not keep the pointed object alive). The simplest kind of observing pointer is a raw pointer:

#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...
}

[NOTE 1: You may be wondering why I am using raw pointers here, when everybody keeps telling that raw pointers are bad, unsafe, and dangerous. Actually, that is a precious warning, but it is important to put it in the correct context: raw pointers are bad when used for performing manual memory management, i.e. allocating and deallocating objects through new and delete. When used purely as a means to achieve reference semantics and pass around non-owning, observing pointers, there is nothing intrinsically dangerous in raw pointers, except maybe for the fact that one should take care not to dereference a dangling pointer. - END NOTE 1]

[NOTE 2: As it emerged in the comments, in this particular case where the ownership is unique and the owned object is always guaranteed to be present (i.e. the internal data member device is never going to be nullptr), function getDevice() could (and maybe should) return a reference rather than a pointer. While this is true, I decided to return a raw pointer here because I meant this to be a short answer that one could generalize to the case where device could be nullptr, and to show that raw pointers are OK as long as one does not use them for manual memory management. - END NOTE 2]


The situation is radically different, of course, if your Settings object should not have the exclusive ownership of the device. This could be the case, for instance, if the destruction of the Settings object should not imply the destruction of the pointed Device object as well.

This is something that only you as a designer of your program can tell; from the example you provide, it is hard for me to tell whether this is the case or not.

To help you figure it out, you may ask yourself whether there are any other objects apart from Settings that are entitled to keep the Device object alive as long as they hold a pointer to it, instead of being just passive observers. If that is indeed the case, then you need a shared ownership policy, which is what std::shared_ptr offers:

#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...
}

Notice, that weak_ptr is an observing pointer, not an owning pointer - in other words, it does not keep the pointed object alive if all other owning pointers to the pointed object go out of scope.

The advantage of weak_ptr over a regular raw pointer is that you can safely tell whether weak_ptr is dangling or not (i.e. whether it is pointing to a valid object, or if the object originally pointed to has been destroyed). This can be done by calling the expired() member function on the weak_ptr object.

这篇关于为类成员使用智能指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆