http://stackoverflow.com/questions/15648844/using-smart-pointers-for-class-members

Question:

I'm havingtrouble understanding the usage of smart pointers as class members in C++11. Ihave read a lot about smart pointers and I think I do understand howunique_ptrandshared_ptr/weak_ptrwork in general. What I don't understand is the real usage. It seems likeeverybody recommends usingunique_ptr as the way to go almostall the time. But how would I implement something like this:

我无法理解该如何将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...
}

Let's say Iwould like to replace the pointers with smart pointers. A unique_ptr would not workbecause of getDevice(), right? So that's the time when I useshared_ptrandweak_ptr?No way of usingunique_ptr? Seems to me like for most casesshared_ptrmakes more sense unless I'm using a pointer in a really small scope?

我想将原始指针换成智能指针。因为有getDevice(),所以不能用unique_ptr(因为所有权不再唯一了),对吗?所以这时候我该用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...
}

Is that the wayto go? Thanks very much!

Answer:

A unique_ptr would not workbecause of getDevice(), right?

No, notnecessarily. What is important here is to determine the appropriate ownershippolicy for your Device object, i.e. who is going to be the owner of the objectpointed to by your (smart) pointer.

因为有getDevice(),所以不能用unique_ptr,对吗?

不,没那么绝对。现在重要的是针对你的Device对象确定一个适合的所有权策略,即,在指向这个对象的指针中,哪个拥有它的所有权(有责任决定对象的生命期)?

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

只由Settings对象拥有它吗?当Settings对象析构时Device对象也要自动被析构吗?还是Device对象的生命期可以比Settings对象更长?

In the firstcase, 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 responsiblefor its destruction.

第一种情况下,std::unique_ptr就是你需要的,Settings对象独自拥有被指向的Device对象的所有权,因此析构Device对象就是它的责任。

Under thisassumption, getDevice() should return a simple observing pointer (observingpointers are pointers which do not keep the pointed object alive). The simplestkind 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 amusing raw pointers here, when everybody keeps telling that raw pointers arebad, unsafe, and dangerous. Actually, that is a precious warning, but it isimportant to put it in the correct context: raw pointers are bad when used for performing manual memorymanagement, i.e. allocating and deallocating objects through new anddelete. When used purely as a means to achieve reference semantics and passaround non-owning, observing pointers, there is nothing intrinsically dangerousin raw pointers, except maybe for the fact that one should take care not todereference a dangling pointer. -ENDNOTE 1]

[注意1:你可能纳闷为什么我在这用了原始指针,因为所有人都在告诉你原始指针不好、不安全、很危险。事实上这个警告很宝贵,但重要的是要将它放在正确的上下文中:当用于执行手动的内存管理时用原始指针并不合适,即通过new和delete分配和释放对象时。但如果只是用于实现引用语义,不涉及所有权,作为观察指针时,原始指针本质上并不危险,除了可能需要牢记不要解引用一个悬挂指针。——注意1结束]

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

[注意2:正如评论中所说,在这种特殊情况下——所有权唯一,而且,拥有的对象保证一直存在(内部数据成员device永远不会变为nullptr),getDevice()可以(或者是应该)返回一个引用而不是指针。虽然这是事实,我决定返回一个原始指针是因为这个答案很短,还可以推广到device可以为nullptr的情况,此时用原始指针也没有问题,只要没有人将它们用于手动内存管理就可以。——注意2结束]

The situation isradically different, of course, if your Settings object shouldnot have the exclusive ownership of thedevice. This could be the case, for instance, if the destruction of theSettings object should not imply the destruction of the pointed Device objectas well.

如果你的Settings对象不应该拥有device的唯一所有权,情况当然就很不一样了。这就意味着,Settings对象的析构不应该隐含着device指向的Device对象也要析构。

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

作为你的程序的设计者,只有你能告诉我们到底是什么情况;从你提供的例子中,我很难看出是哪种。

To help youfigure it out, you may ask yourself whether there are any other objects apartfrom Settings that are entitled to keep the Device object alive as long as theyhold a pointer to it, instead of being just passive observers. If that isindeed the case, then you need a sharedownership policy, which is what std::shared_ptr offers:

为了帮助你解决,你应该问自己,有没有Settings以外的对象有资格在持有Device指针期间保持Device对象存在,而不仅仅是被动的观察者。如果有这样的对象,你需要的是分享所有权策略,这就是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 observing pointer, not an owning pointer - in other words, it does notkeep the pointed object alive if all other owning pointers to the pointedobject go out of scope.

注意,weak_ptr是观察指针,而不是所有权指针——也就是说,如果离开了所有其它拥有这个对象的指针所在的作用域,它没办法保持对象存在。

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

weak_ptr相比于普通的原始指针的优势在于你可以很安全的分辨出weak_ptr是不是悬挂指针(它是指向有效的对象,还是它本来指向的对象已经被摧毁了)。这可以通过对weak_ptr对象调用expired()成员函数来实现。

@LKK: Yes,correct. A weak_ptr is always an alternative to raw observing pointers. Itis safer in a sense, because you could check if it is dangling beforedereferencing it, but it also comes with some overhead. If you can easilyguarantee that you are not going to dereference a dangling pointer, then youshould be fine with observing raw pointers

@LKK:对,weak_ptr总是原始指针的另一种选择。某种意义上它更安全,因为你能在解引用前检查它是不是悬挂状态,但这也带来了一些开销。如果能轻松保证不会解引用一个悬挂指针,你可以放心将原始指针当作观察用。

@vobject: Also,returning a reference would make it possible for the user to erroneously writesomething like: Device myDevice = settings.getDevice(); when they actuallymeant Device& myDevice = settings.getDevice(); (forgetting the &),which won't happen when returning a pointer. I also feel like checking againstnullptr is not needed here, because the function is always guaranteed to returna valid pointer.

@vobject:同样的,返回一个引用也可能会导致使用者写下这样的错误代码:

Device myDevice =settings.getDevice();

实际上他们想写的是:

Device&myDevice = settings.getDevice(); //忘了写&

如果返回指针的话就不会发生这种事了。我还感觉不需要检查nullptr,因为这个函数保证了始终会返回一个有效指针。

@vobject: Inthis case it could return a reference, yes, or a reference wrapper. However,this can't be generalized to the case where a returned pointer could be null,so I thought I'd just put a pointer in there (my main goal in this answer wasto explain the meaning of observing pointers vs owning pointers in ageneralized context, and to point out that raw pointers are not that bad whenobserving pointers are needed).

@vobject:这种情况下可以返回一个指针,或是一个包装器引用。但不能将它推广到可能返回null指针的情况,所以我想我只需要在那放个指针(答案里我的主要目的是解释观察指针相比于所有权指针在一个一般化的上下文中的含义,并指出在需要观察指针时,原始指针并没有那么糟糕)。

@chico: Not surewhat you mean. auto myDevice = settings.getDevice() will create a new instanceof type Device called myDevice and copy-construct it from the one referenced bythe reference that getDevice() returns. If you want myDevice to be a reference,you need to do auto& myDevice = settings.getDevice(). So unless I ammissing something, we're back in the same situation we had without using auto.

@chico:不清楚你想说什么。auto myDevice =settings.getDevice()会创建一个名叫myDevice的Device类型新对象,并且会通过getDevice()返回的引用来进行复制构造。如果你想要让myDevice是一个引用,你应该写auto& myDevice =settings.getDevice()。所以如果我没有错过什么的话,我们还是回到没有用auto的情况吧。

@BretKuhns: As Imentioned in a previous comment, it is true that in this case we could return areference, but my goal in the answer was just to provide a simple solution that1) could be generalized (applies also when the allowed pointer could be null),and 2) showed that raw pointers are bad only when used for manual memorymanagement. But I agree that in this particular case we could/should return areference.

@BretKuhns:正如我在前面的评论中提到的,这种情况下返回一个引用很正确,但我答案的目的只是提供一个简单的解决方案1)能被一般化(也能应用于允许返回null指针的情况),2)表明原始指针只有在用于手动内存管理时才很糟糕。但我同意在这种特殊情况下我们可以/应该返回一个引用。

[翻译]将智能指针用于C++的类成员的更多相关文章

  1. C++ 11 智能指针(shared_ptr)类成员函数详解

    C++ 11 模板库的 <memory> 头文件中定义的智能指针,即 shared_ptr 模板类,用来管理指针的存储,提供有限的内存回收函数,可同时与其他对象共享该管理功能. share ...

  2. C++解析(27):数组、智能指针与单例类模板

    0.目录 1.数组类模板 1.1 类模板高效率求和 1.2 数组类模板 1.3 堆数组类模板 2.智能指针类模板 2.1 使用智能指针 2.2 智能指针类模板 3.单例类模板 3.1 实现单例模式 3 ...

  3. Binder学习笔记(十一)—— 智能指针

    轻量级指针 Binder的学习历程爬到驱动的半山腰明显感觉越来越陡峭,停下业务层的学习,补补基础层知识吧,这首当其冲的就是智能指针了,智能指针的影子在Android源码中随处可见.打开framewor ...

  4. C++ 引用计数技术及智能指针的简单实现

    一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...

  5. C++11智能指针读书笔记;

    智能指针是一个类对象,而非一个指针对象. 原始指针:通过new建立的*指针 智能指针:通过智能指针关键字(unique_ptr, shared_ptr ,weak_ptr)建立的指针 它的一种通用实现 ...

  6. 【C++】动态内存与智能指针

    C++常见的内存分配方式有三种: 从静态存储区分配,这里主要是存储局部static对象,类的static成员以及定义在函数之外的变量: 从栈内存分配,这里主要是存储函数内的非static对象: 从堆内 ...

  7. 智能指针(一):STL auto_ptr实现原理

    智能指针实际上是一个类(class),里面封装了一个指针.它的用处是啥呢? 指针与内存 说到指针自然涉及到内存.我们如果是在堆栈(stack)中分配了内存,用完后由系统去负责释放.如果是自定义类型,就 ...

  8. STL 智能指针

    转自: https://blog.csdn.net/k346k346/article/details/81478223 STL一共给我们提供了四种智能指针:auto_ptr.unique_ptr.sh ...

  9. 智能指针unique_ptr

    转自:https://www.cnblogs.com/DswCnblog/p/5628195.html 成员函数 (1) get 获得内部对象的指针, 由于已经重载了()方法, 因此和直接使用对象是一 ...

随机推荐

  1. Thrift Expected protocol id ffffff82 but got 0

    如果服务端配的也是noblock=false;客户端不能改成noblock=true;

  2. Oracle预定义的21个系统异常类型

    命名的系统异常 产生原因 ACCESS_INTO_NULL 未定义对象 CASE_NOT_FOUND CASE 中若未包含相应的 WHEN ,并且没有设置 ELSE 时 COLLECTION_IS_N ...

  3. Linux系统下 MongoDB安装搭建

    1.下载linux的mongodb 2.在目录usr/local下创建文件夹mongodb,把安装包解压到该文件夹中 # mkdir mongodb # tar -zxvf mongodb-3.4.2 ...

  4. js获取元素的外链样式

    一般给元素设置行内样式,如<div id="div1" style="width:500px;"></div>.如要获取它的样式,即可d ...

  5. IIS与ASP.NET中的线程池

    1. W3 Thread Pool(W3TP) 当处于内核模式的http.sys接收到来自用户的请求之后,会将请求放入队列中.那处于用户模式的w3wp进程如何从内核模式的队列中取出请求呢?I/O完成端 ...

  6. sql_q.format

    field_q_insert = 'id, title, number, created,content'sql_q = 'INSERT INTO testquestion ({}) VALUES ( ...

  7. 解决SpringMVC中文乱码

    第一种:表单提交后controller获得中文参数后乱码解决方案 注意: 1: form表单提交方式为必须为post,get方式下面spring编码过滤器不起效果 2: jsp页面编码设置为UTF-8 ...

  8. 转载:Why using Single Root I/O Virtualization (SR-IOV) can help improve I/O performance and Reduce Costs

    Introduction While server virtualization is being widely deployed in an effort to reduce costs and o ...

  9. 【我的Android进阶之旅】解决Android Studio 运行gradle命令时报错: 错误: 编码GBK的不可映射字符

    1.问题描述 最近在负责公司基础业务和移动基础设施的开发工作,正在负责Lint代码静态检查工作.因此编写了自定义的Lint规则,在调试过程中,编译的时候出现了如下所示的错误: 部分输出日志如下所示: ...

  10. 商铺项目(Logback配置与使用)

    <?xml version="1.0" encoding="utf-8"?> <configuration debug="false ...