Правило трёх C ++ для объектов-энкупсалаторов

0

Вот мой сценарий:

class Database {

    public:
        Database();
        ~Database();

        void close();
                ...

    private:

        sqlite3 *database; //SQLITE3 OBJECT
        bool isOpenDb;
        ...

};

Database::Database() {

    database = 0;
    filename = "";
    isOpenDb = false;
}

Database::~Database() {
    close();
}

void Database::close() {
    sqlite3_close(database); 
    isOpenDb = false;
}

Когда объект базы данных уничтожен, я хочу, чтобы объект sqlite3 был закрыт. В этой форме кажется, что это может быть небезопасный подход, поскольку, если я копирую объект, и я его уничтожаю, то скопированный объект находится в недопустимом состоянии.

На ваш взгляд, как лучше всего действовать? Я думал об одном классе, но я не уверен в этом.

  • 2
    Похоже, Database должна быть не копируемой (но перемещаемой).
  • 0
    @ sftrabbit спасибо! Вы можете аргументировать свои аргументы?
Показать ещё 3 комментария
Теги:
object
encapsulation

3 ответа

5
Лучший ответ

Сделайте свой класс не подлежащим копированию. В С++ 11:

class Database {
public:
  Database(const Database&) = delete;
  Database& operator=(const Database&) = delete;
};

В С++ 03:

class Database {
private: // inaccessible 
  Database(const Database&);
  Database& operator=(const Database&);
};

Или с Boost:

#include <boost/noncopyable.hpp>
class Database : private boost::noncopyable {
};

В С++ 11 я бы также сделал объект Movable, предоставив ему операторы MoveConstructor и Move присваивания. Там вы должны назначить дескриптор (или что бы вы ему дали) для нового объекта и использовать некоторый флаг в старом объекте, чтобы указать, что ничего не нужно закрывать.

Не забудьте также реализовать swap !

class Database {
  // This might look odd, but is the standard way to do it without a namespace.
  // If you have a namespace surrounding Database, put it there.
  friend void swap(Database& a, Database& b) { /* code to swap a and b */ }
};

Также: установка значения false в деструктор не влияет. Ничто никогда не сможет увидеть изменения.

Или используя unique_ptr/shared_ptr с пользовательским удалением:

struct CloseDatabase {
  void operator()(sqlite* x) { sqlite3_close(x); }
};

typedef std::unique_ptr<sqlite3, CloseDatabase> Database;
  • 1
    Как @Andrey предложил в комментариях к вопросу, вы также можете управлять членом database с помощью std::unique_ptr - я думаю, что это неявно сделает Database не копируемой, но перемещаемой.
  • 0
    @sftrabbit Очень хороший момент. Я предположил, что объект используется для инкапсуляции, потому что некоторые высокоуровневые параметры, относящиеся к доступу к базе данных, также должны быть там.
Показать ещё 2 комментария
2

Вы можете использовать общий указатель [0], учитывая, что ваше приложение однопоточно.

Затем конструктор-копир будет тривиальным, и объект будет уничтожен, когда последний экземпляр вашего класса будет уничтожен.

private:
  std::shared_ptr<sqlite3> database;

EDIT: Как указано в pmr, для закрытия базы данных требуется специальный дебетер. Например, простая лямбда:

database = std::shared_ptr<sqlite3>(new database,
                                    [](sqlite3 *db)
                                    { db->close; delete db; });

Или вы можете использовать функтор в качестве делетера.

Синглтон сделает ваш класс не скопированным, если это то, что вы хотите, вы можете сделать это.

Или просто отключите конструктор копирования и оператор копирования (и переместите эквиваленты). С++ 11 предоставляет ключевое слово delete для этого.

Database(const Database &) = delete;
Database & operator =(const Database &) = delete;
Database(Database &&) = delete;
Database & operator =(Database &&) = delete;

[0] http://ru.cppreference.com/w/cpp/memory/shared_ptr

  • 0
    Вам все еще нужно пользовательское средство удаления.
  • 0
    @pmr Я полагаю, что средство удаления класса sqlite3 закрывает саму базу данных.
Показать ещё 2 комментария
1

Если нет веских оснований для этого, отключите конструктор копирования и оператор присваивания и не разрешите копирование. Если у вас есть веская причина разрешить копии, то изучение того, что это значит, поможет вам понять, какую семантику вы действительно хотите, когда есть копия, и это должно привести к дизайну.

  • 0
    благодарю вас! Можете ли вы объяснить мне что-то еще?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню