Подтвердить что ты не робот

Переместить конструктор и переменные-константы

Мне нравится идея константных членских переменных, особенно когда я переношу функции C в классы. Конструктор принимает дескриптор ресурса (например, дескриптор файла), который остается действительным в течение всего времени жизни объекта, и деструктор, наконец, закрывает его. (Это идея RAII, правда?)

Но с конструктором перемещения С++ 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;
};
4b9b3361

Ответ 1

Нет, нет никакого способа сделать это. Я бы предположил, что если вы действительно привязаны к переменной handle, являющейся const, у вас должна быть переменная-член, не являющаяся константой, которая указывает, должно ли быть что-то разрушение.

Ответ 2

Вот почему вы не должны объявлять упомянутые переменные-члены const. const переменные-члены обычно не имеют никакой цели. Если вы не хотите, чтобы пользователи мутировали FILE*, то не предоставляйте им какие-либо функции для этого, и если вы хотите случайно отключить его от мутации, отметьте свои функции const. Однако не делайте сами переменные-члены const, потому что тогда вы запускаете удовольствие, когда начинаете использовать семантику перемещения или копирования.

Ответ 3

Типичный способ реализации конструктора перемещения - обнулить или иным образом аннулировать элементы перемещаемого экземпляра (см. MSDN для простой пример). Поэтому я бы сказал, что просто не используйте const здесь, поскольку он несовместим с целями семантики перемещения.

Ответ 4

На самом деле, я столкнулся с этой проблемой и сегодня. Не желая принимать "не может быть сделано" и "использовать shared_ptr/reference counting", больше искать в googling, я придумал этот базовый класс:

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

Все методы и конструкторы защищены, вам нужно наследовать от него, чтобы использовать его. Обратите внимание на изменяемое поле: это означает, что потомок может быть членом-константой в классе. Например.

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

Поле (а) А может быть теперь const. Обратите внимание, что я унаследовал защиту, так что пользователь не может случайно использовать A для ресурса (или очень охотно взломать его), но 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. Если у ваших типов есть соответствующие конструкторы перемещения, любая память, выделенная кучей, может быть разделена между объектами.

Ответ 5

Подсчет ссылок - это стандартный подход, который решает вашу проблему. Попробуйте добавить подсчет ссылок к вашему классу; либо вручную, либо используя существующие инструменты, такие как boost shared_ptr.