c++ - 当一个对象保证比其包含的对象长寿时,应该如何存储该对象?

标签 c++ c++11 memory-management smart-pointers

在处理一个项目时,我遇到了一个有趣的问题,当通过其构造函数将一个对象传递给另一个对象时,传入的对象保证比接收者对象长寿(就内存生存期而言)。请记住,我仍在学习 C++11/C++14 的来龙去脉,因此我正在寻找建设性的讨论,以帮助我理解 C++11/的内存管理和生命周期C++14 风格的语义。

本题的设置如下:

class TopLevelClass {
public:
    void someMethod (int someValue) {
        // Do some work
    }

    std::unique_ptr<Context> getContext () {
        return std::make_unique<Context>(this);
    }
};

class Context {
public:
    Context (TopLevelClass* tlc) : _tlc(tlc) {}

    void call (int value) {
        // Perform some work and then call the top level class...
        _tlc->someMethod(value);
    }

protected:
    TopLevelClass* _tlc;
};

尽管此设置的有效替代方法是传递 TopLevelClass作为 call 的参数Context 的方法类,这在我正在说明的场景中是不可能的:可以访问 Context 的客户端代码对象可能无法访问 TopLevelClass目的。

虽然上面说明的代码在功能上符合我的需要,但我觉得好像存在代码味道。即,存储 TopLevelClass 的句柄作为原始指针的对象并没有传达 Context 的事实类不负责管理此指针的生命周期(因为在这种情况下,TopLevelClass 保证比任何 Context 对象都长寿)。此外,在使用 C++11 时,我对使用原始指针而不是智能指针犹豫不决(根据 Scott Meyer 在 Effective Modern C++ 中的建议)。

我探索的一种替代方法是将句柄传递给 TopLevelClass使用共享指针并将此句柄存储在 Context 中类作为共享指针。这需要 TopLevelClass继承自 std::enabled_shared_from_this 通过以下方式:

class TopLevelClass : public std::enable_shared_from_this<TopLevelClass> {
public:
    // Same "someMethod(int)" as before...

    std::unique_ptr<Context> getContext () {
        return std::make_unique<Context>(shared_from_this());
    }
};

class Context {
public:
    Context (std::shared_ptr<TopLevelClass> tlc) : _tlc(tlc) {}

    // Same "call(int)" as before...

protected:
    std::shared_ptr<TopLevelClass> _tlc;
};

这种方法的缺点是,除非 std::shared_ptr存在 TopLevelClass 先验,然后是 std::bad_weak_ptr将抛出异常(有关更多信息,请参阅 this post )。因为,就我而言,没有 std::shared_ptr<TopLevelClass>在代码中创建,我无法使用 std::enable_shared_from_this<T>方法:我仅限于返回 TopLevelClass 的单个实例使用 static原始指针,按照我项目的要求,如下

static TopLevelClass* getTopLevelClass () {
    return new TopLevelClass();
}

有没有一种方法可以表达 Context不负责管理其对 TopLevelClass 的句柄例如,自 TopLevelClass将保证活得比任何 Context目的?我也乐于接受有关更改设计的建议,这些设计将完全解决问题,只要设计更改不会使上述设计的简单性过于复杂(即,创建许多不同的类以绕过简单地传递一个指向 Context 的构造函数的单个指针。

感谢您的帮助。

最佳答案

以您的方式传递原始指针绝对应该意味着没有所有权被转移。

如果你听到有人说“不要使用原始指针”,你可能错过了这句话的一部分 - 它应该是“不要使用拥有原始指针”,即不应该某个地方有一个原始指针,您需要对其调用 delete。除了可能在一些低级代码中。如果您知道被指向的对象比获取指针的对象存在时间更长,那么只传递指针绝对没有错。

您是说“也就是说,将 TopLevelClass 对象的句柄存储为原始指针并不能传达 Context 类不负责管理该指针的生命周期这一事实”。 相反,存储原始指针的意思恰恰是——“这个对象不管理这个指针指向的对象的生命周期”。在 C++98 风格的代码中,它并不一定意味着。

使用指针的另一种方法是使用引用。但是有一些注意事项,因为您必须在构造函数中初始化它,并且它不能像指针一样设置为 nullptr(这也可能是一件好事)。即:

class TopLevelClass {
public:
    void someMethod (int someValue) {
        // Do some work
    }

    std::unique_ptr<Context> getContext () {
        return std::make_unique<Context>(*this);
    }
};

class Context {
public:
  Context(TopLevelClass &tlc) : _tlc(tlc) {}

  void call (int value) {
    // Perform some work and then call the top level class...
    _tlc.someMethod(value);
  }

private:
  TopLevelClass &_tlc;
};

这里有一些关于这个主题的文章:

C++ 核心指南:

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-ptr

Herb Sutter 的一些早期文章:

http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/

http://herbsutter.com/2013/05/30/gotw-90-solution-factories/

http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/

http://herbsutter.com/elements-of-modern-c-style/

可能还有很多来自 CppCon 以及 Cpp 和 Beyond 的视频,但我有点懒得用 google 搜索合适的视频。

关于c++ - 当一个对象保证比其包含的对象长寿时,应该如何存储该对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33575557/

相关文章:

c++ - 创建子类变量的语法

c++ - Botan编译错误VS2015

iphone - 如果我不创建自动释放对象,是否需要自动释放池?

java - 在一次采访中有人问我如何检测 Java 中的内存泄漏?

c++ - 重载==递归比较两个链表

c++ - C++ stderr 输出在 OSX 中的哪里?

c++ - 缓存谷歌地图以供离线使用

c++ - C++11 中的别名结构

c++ - 普通拷贝可分配与普通拷贝

c - 什么是C中的固定地址变量