移动构造函数和 const 成员变量

发布于 2024-11-14 21:53:08 字数 961 浏览 3 评论 0原文

我喜欢 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;
};

I like the idea of const member variables especially when I wrap C functions into classes. The constructor takes a resource handle (e.g. a file descriptor) that stays valid during the whole object life time and the destructor finally closes it. (That is the idea behind RAII, right?)

But with the C++0x move constructor i run into a problem. Since the destructor is also called on the "unloaded" object i need to prevent the cleanup of the resource handle. Since the member variable is const i have no way to assign the value -1 or INVALID_HANDLE (or equivalent values) to indicate to the destructor that it should not do anything.

Is there a way that the destructor is not called if the state of an object was moved to another object?

Example:

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;
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

游魂 2024-11-21 21:53:08

这就是为什么你不应该声明所述成员变量constconst 成员变量通常没有任何作用。如果您不希望用户更改 FILE*,则不要向他们提供执行此操作的函数,如果您想阻止自己意外更改它,则将您的函数标记为 <代码>常量。但是,不要将成员变量本身设置为 const - 因为当您开始使用移动或复制语义时,您会遇到乐趣

This is why you should not declare said member variables const. const member variables usually serve no purpose. If you don't want users to mutate the FILE*, then don't provide them with functions to do that, and if you want to stop yourself from mutating it by accident, then mark your functions const. However, do not make member variables themselves const - because then you run into fun when you start to use move or copy semantics.

深者入戏 2024-11-21 21:53:08

不,没有办法做到这一点。我建议,如果您确实附加到 const 的 handle 变量,您应该有一个非常量标志成员变量,指示销毁是否应该执行任何操作。

No, there is no way to do this. I would suggest that if you're really attached to the handle variable being const you should have a non-const flag member variable that indicates whether or not destruction should do anything.

吻风 2024-11-21 21:53:08

其实我今天也遇到了这个问题。不愿意接受“做不到”& '使用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。请注意,我继承了 protected,因此用户不会意外地将 A 强制转换为资源(或者非常愿意破解它),但 A 仍然不是最终的,因此您仍然可以从中继承(从资源继承的一个有效原因是例如,具有单独的读取和读写访问权限)。这是极少数情况之一,受保护的继承并不自动意味着您的设计有缺陷;但是,如果您觉得难以理解,您可以使用公共继承。

然后,假设您有一个 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。我们的移动构造函数是常量。鉴于您的类型具有适当的移动构造函数,任何堆分配的内存都可以在对象之间共享。

Actually, I've run into this problem myself as well today. Not willing to accept 'can't be done' & 'use shared_ptr / reference counting', googling more, I came up with this base class:

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;
    }
};

All methods and constructors are protected, you need to inherit from it to use it. Notice the mutable field: this means that descendant can be a const member in a class. E.g.,

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;
        }
    }
};

Field(s) of A can be const now. Note that I've inherited protected, so that user cannot accidentally cast A to Resource (or very willingly to hack it), but A is still not final, so you can still inherit from this (a valid reason to inherit from a Resource is e.g. to have separate read and read-write access). This is one of the extremely rare cases when protected inheritance doesn't automatically mean that your design is faulty; however, if you find it difficult to understand, you might just use public inheritance.

Then, assuming you have a 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;
    }
};

Note that fields of B can also be const. Our move constructors are const. Given your types have appropriate move constructors, any heap-allocated memory can be shared amongst objects.

不爱素颜 2024-11-21 21:53:08

实现移动构造函数的典型方法是将要移动的实例的成员归零或使其无效(请参阅 MSDN 一个简单的例子)。因此我想说不要在这里使用 const,因为它与移动语义的目标不兼容。

The typical way to implement a move constructor is to zero out or otherwise invalidate the members of the instance being moved (see MSDN for a simple example). Therefore I would say just don't use const here as it is incompatible with the goals of move semantics.

太阳哥哥 2024-11-21 21:53:08

引用计数是解决您的问题的标准方法。考虑在你的类中添加引用计数;手动或使用现有工具(如 boost shared_ptr)。

Reference counting is standard approach that solves your problem. Consider adding reference counting to your class; either manually, or using existing tools like boost shared_ptr.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文