C++智能指针作为成员变量的用法

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!

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------

 

 

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用智能指针和原子变量来实现全局静态变量的安全访问和线程安全。 具体实现方法如下: 1. 定义一个全局静态变量,使用智能指针来管理其内存。 2. 在智能指针类中使用原子变量来实现引用计数的原子操作。 3. 在全局静态变量的访问函数中,使用原子变量来保证变量的安全访问。 示例代码如下: ```cpp #include <atomic> #include <memory> class GlobalStatic { public: static int getValue() { std::atomic_int& refCount = getRefCount(); refCount.fetch_add(1, std::memory_order_relaxed); static std::shared_ptr<int> data(new int(0), [](int* p) { delete p; getRefCount().fetch_sub(1, std::memory_order_relaxed); }); int value = *data; refCount.fetch_sub(1, std::memory_order_relaxed); return value; } private: static std::atomic_int& getRefCount() { static std::atomic_int refCount(0); return refCount; } }; ``` 在上面的示例代码中,我们定义了一个名为GlobalStatic的类,其中包含了一个名为getValue的静态函数,用于获取全局静态变量的值。 在getValue函数中,我们首先获取了一个原子变量refCount,用于记录当前有多少线程正在访问全局静态变量。 然后,我们使用一个static std::shared_ptr<int> data对象来管理全局静态变量的内存。在data对象的析构函数中,我们会将refCount的值减去1,以保证引用计数的正确性。 最后,我们使用原子变量来保证对全局静态变量的安全访问,保证了线程安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值