30

我喜欢 const 成员变量的想法,尤其是当我将 C 函数包装到类中时。构造函数采用在整个对象生命周期内保持有效的资源句柄(例如文件描述符),而析构函数最终将其关闭。(这就是 RAII 背后的想法,对吧?)

但是使用 C++0x 移动构造函数我遇到了问题。由于在“卸载”对象上也调用了析构函数,因此我需要防止清理资源句柄。由于成员变量是 const 我无法分配值 -1 或 INVALID_HANDLE (或等效值)来指示析构函数它不应该做任何事情。

如果一个对象的状态被移动到另一个对象,有没有办法不调用析构函数?

例子:

class File
{
public:
    // Kind of "named constructor" or "static factory method"
    static File open(const char *fileName, const char *modes)
    {
        FILE *handle = fopen(fileName, modes);
        return File(handle);
    }

private:
    FILE * const handle;

public:
    File(FILE *handle) : handle(handle)
    {
    }

    ~File()
    {
        fclose(handle);
    }

    File(File &&other) : handle(other.handle)
    {
        // The compiler should not call the destructor of the "other"
        // object.
    }

    File(const File &other) = delete;
    File &operator =(const File &other) = delete;
};
4

5 回答 5

25

这就是为什么你不应该声明所说的成员变量constconst成员变量通常没有任何作用。如果您不希望用户改变FILE*,那么不要为他们提供功能来做到这一点,如果您想阻止自己意外地改变它,那么标记您的功能const。但是,不要自己创建成员变量- 因为当您开始使用移动或复制语义时,您const会遇到乐趣。

于 2011-06-11T17:31:25.103 回答
9

不,没有办法做到这一点。我建议如果您真的附加到handleconst 变量,您应该有一个非常量标志成员变量,指示破坏是否应该做任何事情。

于 2011-06-11T17:29:43.183 回答
4

实现移动构造函数的典型方法是将被移动实例的成员清零或以其他方式无效(有关简单示例,请参见MSDN )。因此我想说不要const在这里使用,因为它与移动语义的目标不兼容。

于 2011-06-11T17:30:16.940 回答
4

实际上,我今天自己也遇到了这个问题。不愿意接受“无法完成”和“使用 shared_ptr / 引用计数”,谷歌搜索更多,我想出了这个基类:

class Resource
{
private:
     mutable bool m_mine;

protected:
    Resource()
    : m_mine( true )
    {
    }

    Resource(const Resource&)       = delete;
    void operator=(const Resource&) = delete;

    Resource(const Resource&& other)
    : m_mine( other.m_mine )
    {
        other.m_mine = false;
    }

    bool isMine() const
    {
        return m_mine;
    }
};

所有方法和构造函数都受到保护,您需要从它继承才能使用它。注意可变字段:这意味着后代可以是类中的 const 成员。例如,

class A : protected Resource
{
private:
    const int m_i;

public:
    A()
    : m_i( 0 )
    {
    }

    A( const int i )
    : m_i( i )
    {
    }

    A(const A&& a)
    : Resource( std::move( a     ) )
    , m_i     ( std::move( a.m_i ) ) // this is a move iff member has const move constructor, copy otherwise
    {
    }

    ~A()
    {
        if ( isMine() )
        {
            // Free up resources. Executed only for non-moved objects
            cout << "A destructed" << endl;
        }
    }
};

A 的字段现在可以是 const 。请注意,我继承了受保护的,因此用户不会意外地将 A 转换为 Resource(或非常愿意破解它),但 A 仍然不是最终的,因此您仍然可以从中继承(从 Resource 继承的正当理由是例如具有单独的读写访问权限)。当受保护的继承并不自动意味着您的设计有错误时,这是​​极为罕见的情况之一。但是,如果您觉得难以理解,您可以只使用公共继承。

然后,假设你有一个struct X

struct B
{
    const A m_a;
    const X m_x;

    B(const A&& a, const X& x) // implement this way only if X has copy constructor; otherwise do for 'x' like we do for 'a'
    : m_a( std::move( a ) )
    , m_x(            x   )
    {
    }

    B( const B&& b )
    : m_a( std::move( b.m_a ) )
    , m_x( std::move( b.m_x ) ) // this is a move iff X has move constructor, copy otherwise
    {
    }

    ~B()
    {
        cout << "B destructed" << endl;
    }
};

请注意,B 的字段也可以是 const。我们的移动构造函数是 const。鉴于您的类型具有适当的移动构造函数,任何堆分配的内存都可以在对象之间共享。

于 2016-06-10T19:46:51.463 回答
-1

引用计数是解决您的问题的标准方法。考虑为您的课程添加引用计数;手动或使用现有工具,如 boost shared_ptr。

于 2011-06-11T17:33:03.977 回答