Вот мой сценарий:
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 был закрыт. В этой форме кажется, что это может быть небезопасный подход, поскольку, если я копирую объект, и я его уничтожаю, то скопированный объект находится в недопустимом состоянии.
На ваш взгляд, как лучше всего действовать? Я думал об одном классе, но я не уверен в этом.
Сделайте свой класс не подлежащим копированию. В С++ 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;
database
с помощью std::unique_ptr
- я думаю, что это неявно сделает Database
не копируемой, но перемещаемой.
Вы можете использовать общий указатель [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;
Если нет веских оснований для этого, отключите конструктор копирования и оператор присваивания и не разрешите копирование. Если у вас есть веская причина разрешить копии, то изучение того, что это значит, поможет вам понять, какую семантику вы действительно хотите, когда есть копия, и это должно привести к дизайну.
Database
должна быть не копируемой (но перемещаемой).