草庐IT

c++ - 对类成员使用智能指针

coder 2023-04-24 原文

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

假设我想用智能指针替换指针。 unique_ptr 不会因为 getDevice() 而工作,对吧?那么这就是我使用 shared_ptrweak_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...
}

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

最佳答案

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

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

它会是Settings 对象单独 的实例吗?当 Settings 对象被销毁时,Device 对象是否必须自动销毁,还是应该比该对象生命周期更长?

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

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

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

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

[注意 2: 正如评论中出现的那样,在这种特殊情况下,所有权是唯一的并且始终保证拥有的对象存在(即内部数据成员 device 永远不会是 nullptr),函数 getDevice() 可以(并且可能应该)返回一个引用而不是比指针。虽然这是真的,但我决定在这里返回一个原始指针,因为我的意思是这是一个简短的答案,可以概括为 device 可能是 nullptr 的情况,并且表明只要不将原始指针用于手动内存管理就可以。 - END NOTE 2]


情况完全不同,当然,如果您的 Settings 对象应该拥有设备的独占所有权。例如,如果 Settings 对象的破坏不应暗示指向的 Device 对象的破坏,则可能是这种情况。

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

为了帮助您弄清楚,您可能会问自己,除了 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...
}

注意,weak_ptr 是一个 observing 指针,而不是拥有指针 - 换句话说,如果所有其他拥有指针指向指向的对象超出范围。

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

关于c++ - 对类成员使用智能指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15648844/

有关c++ - 对类成员使用智能指针的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐