C++ 为类成员使用智能指针

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15648844/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:35:35  来源:igfitidea点击:

Using smart pointers for class members

c++c++11shared-ptrsmart-pointersunique-ptr

提问by michaelk

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_ptrand shared_ptr/weak_ptrwork in general. What I don't understand is the real usage. It seems like everybody recommends using unique_ptras the way to go almost all the time. But how would I implement something like this:

我无法理解智能指针在 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...
}

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

假设我想用智能指针替换指针。Aunique_ptr不会工作,因为getDevice(),对吧?所以那是我使用shared_ptrand 的时候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...
}

Is that the way to go? Thanks very much!

这是要走的路吗?非常感谢!

回答by Andy Prowl

A unique_ptrwould not work because of getDevice(), right?

Aunique_ptr不会工作,因为getDevice(),对吧?

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

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

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

它将单独成为Settings对象的实例吗?当对象被销毁时,该对象是否必须自动销毁,还是应该比该对象寿命更长?DeviceSettings

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

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

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

在这个假设下,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...
}

[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 newand 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]

[注 1:你可能想知道为什么我在这里使用原始指针,因为每个人都一直在说原始指针是坏的、不安全的和危险的。实际上,这是一个宝贵的警告,但将它放在正确的上下文中很重要:原始指针在用于执行手动内存管理时是不好的,即通过new和分配和取消分配对象delete。当纯粹用作实现引用语义并传递非拥有的观察指针时,原始指针没有本质上的危险,除非人们应该注意不要取消对悬空指针的引用。-结束注释 1]

[NOTE 2:As it emerged in the comments, in this particular case where the ownership is unique andthe owned object is always guaranteed to be present (i.e. the internal data member deviceis 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 devicecould 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]

[注2:正如在评论中出现的那样,在这种特殊情况下,所有权是唯一的,并且始终保证所拥有的对象存在(即内部数据成员device永远不会存在nullptr),函数getDevice()可以(也许应该)返回一个引用而不是一个指针。虽然这是事实,我决定在这里返回原始指针,因为我的意思是这是一个简单的答案,人们可以推广到那里的情况device可能是nullptr,并表明原始指针都行,只要一个不使用它们手动内存管理。-结束注释 2]



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

这种情况是完全不同的,当然,如果你的Settings目标应该具备该设备的独占所有权。例如,如果Settings对象的销毁不应暗示指向Device对象的销毁,则可能就是这种情况。

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 Settingsthat are entitled to keep the Deviceobject 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_ptroffers:

为了帮助您弄清楚,您可能会问自己,除了这些对象之外,是否还有任何其他对象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...
}

Notice, that weak_ptris an observingpointer, 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.

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

The advantage of weak_ptrover a regular raw pointer is that you can safely tell whether weak_ptris danglingor 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_ptrobject.

的优势weak_ptr比普通生指针,你可以放心地告诉是否weak_ptr晃来晃去与否(即它是否指向有效的对象,如果对象原来指向已被破坏)。这可以通过调用对象expired()上的成员函数来完成weak_ptr

回答by Naszta

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_ptris used only for reference loops. The dependency graph must be acyclicdirected graph. In shared pointers there are 2 reference counts: 1 for shared_ptrs, and 1 for all pointers (shared_ptrand weak_ptr). When all shared_ptrs are removed, the pointer is deleted. When pointer is needed from weak_ptr, lockshould be used to get the pointer, if it exists.

week_ptr仅用于参考循环。依赖图必须是无环有向图。在共享指针中,有 2 个引用计数:1 个用于shared_ptrs,1 个用于所有指针(shared_ptrweak_ptr)。当所有的shared_ptrs 都被删除时,指针被删除。当需要 from 的指针时weak_ptrlock应该使用它来获取指针(如果存在)。