【问题标题】:How to correctly use smart pointers in OOP如何在 OOP 中正确使用智能指针
【发布时间】:2021-02-24 03:03:23
【问题描述】:

我想知道在面向对象编程中使用std::unique_ptr 的正确方法是什么。假设我有一个类,其中包含一个必须可以通过指针传递的成员对象

class B
{/*Some code here*/};

class A
{
public:
    A();
    B* getMember();
private:
    std::unique_ptr<B> member
};

首先:成员应该是std::unique_ptr 还是std::shared_ptr 以避免内存泄漏?

其次,如果我想的话,我的 getter 的实现会是什么

  1. 允许修改成员?
  2. 不允许修改成员?

我最初是这样做的:

B* A::getMember()
{
    return member.get();
}

但我正在考虑让成员成为 std::shared_ptr 并调整 getter:

class A
{
public:
    A();
    std::shared_ptr getMember();
private:
    std::shared_ptr<B> member
};

std::shared_ptr A::getMember()
{
    return member;
}

什么是标准和最广泛接受的实现?

【问题讨论】:

  • 提示:在 C++ 中,没有必要提及“面向对象编程”。据了解,这是您可能正在做的事情之一。这不是 1970 年代,“OOP”是一些需要解释的新奇事物,因此无需提请注意这一事实。也许您在这里真正谈论的是封装的概念,这是“OOP”的一个非常具体的方面。

标签: c++ shared-ptr unique-ptr


【解决方案1】:

shared_ptr 暗示了一个复杂的所有权规则,其中所讨论的对象由多个平等方拥有,并且其生命周期不容易确定。决定使用shared_ptr,因为它更简单或更容易,就像使用火箭筒杀死困扰您的果蝇一样,因为它比苍蝇拍更强大。

在 C++ 中,没有简单的方法可以避免考虑所有权。

unique_ptr 提供了一个简单的拥有关系来管理生命周期。值类型(或具有值语义的类型)提供更简单的生命周期管理。没有人会对int x=7; 的生命周期感到困惑。

C++ 可以支持运行时多态值类型;见std::function。有疑问时使用值。

class A {
public:
  A();
  B b;
};

更容易思考。

现在,如果您正在做类似 UI 框架的事情,那么您的 UI 元素将归您的框架所有。您可能需要一个命名系统、ASL 中的配置文件,以及一种将您的 UI 连接到操作并使用代码修改您的 UI 的方法。

接下来没有一个将涉及一个类,其成员子对象通过访问器返回指向它的指针。

也许您的 UI 在写入共享 ptr 数据时是不可变副本,附加到您可以从框架获取/设置的名称。在这里,不可变数据是真正共享的;它就像一个整数。

无论如何,当您共享 B 指针时,您必须考虑 B 与 A 分开存在意味着什么,无论是哑、唯一、共享、不可变还是写入时复制。要么 B 成为 A 状态某些部分的接口,在这种情况下,它的生命周期在逻辑上是绑定的,要么它独立存在,在这种情况下,您可能需要值语义。

做出这个决定后,共享 ptr 可能是错误的生命周期规则。如果它是 A 状态的一部分的接口,那么共享所有权所暗示的它比 A 寿命更长是一个错误,如果它是您设置/获取的值,则获取它并修改状态隐式地充当 A 状态的接口! Sithout A 甚至知道!

A 正在从 B 传递状态更改通知?带上能拿到提供状态变化通知的B吗?明智的;在这里,B 可以是 UI 主题颜色,而 A 可以是控件。但是这里 A 可能有一个指向 const 主题颜色集的共享 ptr 以及一个主题更改消息的订阅令牌。 (这里的颜色集是合法共享的)

简而言之,默认共享 ptr 是不好的。当您无法使用值时,请使用唯一 ptr。所有权遵循生命周期,生命周期遵循语义。默认为值,C++ 是少数具有真正可靠的值支持的 OO 语言之一。

【讨论】:

    【解决方案2】:

    如果您正在分发指向 unique_ptr 的指针,那么您并没有真正将其视为“唯一”。

    您真正需要的是shared_ptr。您的第二种方法是正确的方法。这些旨在根据需要复制并保留引用计数。这意味着只要您持有 shared_ptr 对象的副本,就可以访问它。

    在前一种情况下,您完全不知道您的指针在收到它的那一刻之后是否仍然有效。您必须手动跟踪它,这有点违背了使用智能指针的意义。

    【讨论】:

    • 很公平。谢谢您的回答。有没有办法让返回“const”来检查编译时是否会有修改,就像你通过 const 引用返回时一样?
    • 通常您首先提供const 版本,如果情况允许,则提供非const 版本。完全视情况而定。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 2012-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多