2013-12-17 2 views
0

Вот мой сценарий:C++ правило трех для encupsalator объектов

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

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

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

+2

Похоже, 'Database' должен быть не копируемыми (пока подвижны). –

+0

@sftrabbit спасибо! можете ли вы аргументировать свои аргументы? –

+0

Вы также можете использовать интеллектуальный указатель для объекта 'sqlite3'. –

ответ

5

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

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

В C++ 03:

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

Или с НЧ:

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

В C++ 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 с настраиваемой Deleter:

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

typedef std::unique_ptr<sqlite3, CloseDatabase> Database; 
+1

Как предложил @Andrey в комментариях к вопросу, вы также можете управлять членом 'database' с помощью' std :: unique_ptr' - я думаю, что это косвенно сделало бы «базу данных» не скопируемой, а переносимой. –

+0

@sftrabbit Очень хорошая точка. Я предположил, что объект используется для инкапсуляции, потому что некоторые высокоуровневые параметры доступа к базе данных также должны идти туда. – pmr

+0

@pmr это отлично! можете ли вы лучше объяснить, что вы имеете в виду в заключительной части (от «Не забудьте также выполнить обмен»?) –

1

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

+0

спасибо! можете ли вы объяснить мне что-то еще? –

2

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

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

private: 
    std::shared_ptr<sqlite3> database; 

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

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

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

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

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

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

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

+0

Вам все равно понадобится пользовательский деактивировать. – pmr

+0

@pmr Я использую класс sqlite3, который закрывает базу данных. – Pluc

+1

Это не так, если вы посмотрите на пример кода OP. должен быть вызван. Я думаю, что часть вашего ответа не содержит важной информации, которая будет полезной. – pmr

Смежные вопросы