【问题标题】:RAII - Class Pointers and ScopeRAII - 类指针和范围
【发布时间】:2012-03-13 15:54:16
【问题描述】:

我想通过一个示例更好地了解如何在我的类中实现 RAII 习语:什么推荐的方法是确保指针在我的类中正确释放()?

我有一个课程应该在课程期间存在。本着 RAII 的精神,并且因为我需要将这个类的引用传递给其他类,所以我将它保存在 shared_ptr 中(不确定它是否真的需要保存在 shared_ptr 中,但为了好玩,确实如此)。

在类 ctor 中,我使用 2 个缓冲区(指针),然后循环多次 malloc()'ing,使用缓冲区然后 free()'ing。 dtor 应包含故障安全代码以在发生意外时释放缓冲区。

dtor 可以看到缓冲区的唯一方法是,如果我将它们声明为类变量,但它们仅在类 ctor 中使用。

例子:

class Input
{
private:
    PSOMETYPE buffer1;
public:
    Input();
    ~Input();
}

Input::Input() : buffer1(NULL)
{
    for(blahblah)
    {
        buffer1 = (PSOMETYPE)malloc(sizeof(SOMETYPE));
        // Do work w/buffer1
        if(buffer1 != NULL) { free(buffer1); buffer1 = NULL }
    }
}

Input::~Input()
{
    if(buffer1 != NULL) { free(buffer1); buffer1 = NULL }
}

考虑到我只使用 ctor 中的缓冲区,将其声明为私有类变量是否有意义?如果我在 ctor 的范围内声明它,则 dtor 将不知道什么是释放。

我知道这是一个微不足道的例子,老实说,我可以很容易地忘记使用智能指针来引用我的类并拥有一个空白 dtor,就像我在循环中所做的那样,只需 free()'ing。我没有导师也没有受过教育,我不确定何时应该遵循 RAII 习语。

【问题讨论】:

  • 这是 C 和 C++ 思想的一种奇怪组合……您使用 malloc 而不是新的,甚至更好的 STL 模板化容器有什么原因吗?那些更适合 RAII 的概念
  • @tmpearce:这是由于我缺乏经验并且使用 MSDN 作为学习资源。我感谢替代/改进的参考。

标签: c++ design-patterns raii


【解决方案1】:

RAII 的精神是使用本地对象来管理本地分配的对象,而不是人为地将其生命周期与正在构造的对象绑定:

class Input
{
    // no pointer at all, if it's only needed in the constructor
public:
    Input();
    // no explicit destructor, since there's nothing to explicitly destroy
};

Input::Input()
{
    for(blahblah)
    {
        std::unique_ptr<SOMETYPE> buffer1(new SOMETYPE);

        // or, unless SOMETYPE is huge, create a local object instead:
        SOMETYPE buffer1;

        // Do work w/buffer1
    }   // memory released automatically here
}

如果您正在编写一个旨在管理该资源的类,那么您应该只需要自己使用delete(或free,或其他) - 通常已经有一个标准类(例如 smart指针或容器)做你想做的事。

当您确实需要编写自己的管理类时,请始终记住Rule of Three:如果您的析构函数删除了某些内容,那么该类的默认复制行为几乎肯定会导致双重删除,因此您需要声明一个副本构造函数和复制赋值运算符来防止这种情况。例如,在您的班级中,我可能会编写以下错误代码:

{
    Input i1;     // allocates a buffer, holds a pointer to it
    Input i2(i1); // copies the pointer to the same buffer
}                 // BOOM! destroys both objects, freeing the buffer twice

防止这种情况的最简单方法是删除复制操作,这样这样的代码将无法编译:

class Input {
    Input(Input const&) = delete;    // no copy constructor
    void operator=(Input) = delete;  // no copy assignment
};

较旧的编译器可能不支持= delete;在这种情况下,您可以通过在没有= delete 的情况下私​​下声明它们并且不实现它们来获得几乎相同的效果。

【讨论】:

  • 谢谢。这对我来说很有意义。你的第一句话完美地总结了一切。在您的错误代码示例中,是否会由于复制 unique_ptr 而引发异常?我肯定需要实现复制 ctor 和复制赋值运算符。再次感谢!
  • @Lokked:如果该类包含unique_ptr,那么如果您尝试复制它,则会出现编译器错误。您可以改为使用shared_ptr 来获取安全的指针复制,或者嵌入一个对象而不是指针来获取对象复制。在我的示例中,unique_ptr 根本不是成员,因为它只在构造函数中需要。
猜你喜欢
  • 2013-06-17
  • 2011-07-22
  • 2016-11-15
  • 1970-01-01
  • 1970-01-01
  • 2018-10-07
  • 1970-01-01
  • 2010-09-28
  • 2011-02-26
相关资源
最近更新 更多