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. MyBatis中typealiases的使用

    转自:http://blog.csdn.net/lelewenzibin/article/details/42713585 问题描述 Mybatis有个代码生成工具,生成的代码里面有mapper.xm ...

  2. RMAN概述及其体系结构

    1 Recovery Manager(RMAN)特性 是一种用于集备份(backup).还原(restore)和恢复(recover)数据库于一体的Oracle 工具,支持命令行及图形界面操作 能够备 ...

  3. [MongoDB] 安装MongoDB配置Replica Set

    MongoDB的环境主要包括StandAlone,Replication和Sharding. StandAlone:单机环境,一般开发测试的时候用. Replication:主从结构,一个Primar ...

  4. Jmeter性能测试实践之java请求

     前言 Apache Jmeter是开源.易用的性能测试工具,之前工作中用过几次对http请求进行性能测试,对jmeter的基本操作有一些了解.最近接到开发的对java请求进行性能测试的需求,所以需要 ...

  5. 阿里云ecs禁止ping,禁止telnet

    现在的中小型企业服务器大多是云比较多,因此,可能会面临着服务器ping不通,或者是端口telnet不通的情况,但是服务器上的服务仍然是正常的情况,这个时候我们就要考虑是不是云上配置了访问规则了.废话不 ...

  6. asp.net 下载文件几种方式

    protected void Button1_Click(object sender, EventArgs e) { /* 微软为Response对象提供了一个新的方法TransmitFile来解决使 ...

  7. C#操作word之插入图片

    假如我们导出一份简历到word文档,那势必可能要同时导出我们包含的简历,下面就来试一下如何和通过C#代码,将图片插入到word文档中. 为了简便起见,就简单一点.类似下面这样的 姓名 张三 照片   ...

  8. 字符串函数---atof()函数详解

    atof()函数 atof(),是C 语言标准库中的一个字符串处理函数,功能是把字符串转换成浮点数,所使用的头文件为<stdlib.h>.该函数名是 “ascii to floating ...

  9. scrapy爬虫系列之七--scrapy_redis的使用

    功能点:如何发送携带cookie访问登录后的页面,如何发送post请求登录 简单介绍: 安装:pip3 install scrapy_redis 在scrapy的基础上实现了更多的功能:如reques ...

  10. java爬取网页内容 简单例子(2)——附jsoup的select用法详解

    [背景] 在上一篇博文java爬取网页内容 简单例子(1)——使用正则表达式 里面,介绍了如何使用正则表达式去解析网页的内容,虽然该正则表达式比较通用,但繁琐,代码量多,现实中想要想出一条简单的正则表 ...