I have simple question about class design in C++.
Lets assume we have the following class:
class DataBase
{
public:
DataBase();
void addEntry(const std::string& key, double value);
double getEntry(const std::string& key);
protected:
std::map<std::string, double> table;
};
There is another class which holds a pointer to an instance of DataBase
class:
class SomeClass
{
protected:
DataBase *someDataBase;
};
Here I get confused, as two options come to my mind:
SomeClass
will have a database of its own. In the sense that only the data added by this instance will be present in this database (dedicated databases). SomeClass
will be referring to a central database. Data added by any of the instances of SomeClass
will be in one single database (a global database). Question:
With Composition you can just have the DataBase
as a member:
class SomeClass
{
protected:
DataBase someDataBase;
};
With Dependency injection you basically give SomeClass
a pointer to your shared DataBase
and SomeClass
saves a pointer to it. Be careful if you have a multithreaded application, you need to protect writing to the database and maybe reading as well.
class SomeClass
{
public:
SomeClass(DataBase* db) : someDataBase(db) {}
protected:
DataBase* someDataBase;
};
How you crate and where you store the shared DataBase
is up to you.
What you are looking for is the topic of ownership in C++. When I say ownership, I mean who is responsible for managing the memory that holds the object.
In your first example each SomeClass
could own its own DataBase
.
class SomeClass
{
private DataBase *db;
public SomeClass();
public SomeClass(DataBase* db);
public ~SomeClass();
}
SomeClass::SomeClass()
{
this.db = new DataBase();
}
SomeClass::SomeClass(DataBase* db)
{
this.db = db;
}
SomeClass::~SomeClass()
{
delete this.db;
}
This SomeClass
either takes ownership of the DataBase given to it or creates its own (practically you usually do one or the other). This means you can pass in a DataBase
object (using a concept known as dependency injection):
DataBase *db = new DataBase();
SomeClass sc(db);
sc.doSomeStuffWithDB();
or just let the class create the DataBase
object:
SomeClass sc();
sc.doSomeStuffWithDB();
In the above example you don't have to worry about disposal of the DataBase
objects, knowing that SomeClass
should take care of disposal in its destructor.
In the other scenario you could share a DataBase
without having it be disposed of by your SomeClass
(whether it's global or not is irrelevant).
class SomeClass
{
private DataBase *db;
public SomeClass(DataBase* db);
}
SomeClass::SomeClass(DataBase* db)
{
this.db = db;
}
Here we could pass multiple SomeClass
objects the same DataBase
and not have to worry about them being disposed of by any of the objects.
DataBase *db = new DataBase();
SomeClass *sc1 = new SomeClass(db);
SomeClass *sc2 = new SomeClass(db);
sc1.doSomeStuffWithDB();
delete sc1;
sc2.doSomeStuffWithDB();
delete sc2;
delete db;
In this scenario we were able to reuse the DataBase
object before disposing of it external to our SomeClass
objects. Practically speaking, this disposal could be managed by another class like DataBaseStore
, allowing you to have a reliable way to handle and reuse DataBase
objects.
Concept n°1 is composition. The Database
is part of the SomeClass
.
Concept n°2 doesn't have a name as far as I know.
This is actually pretty straightworward : give SomeClass
a member of type Database
.
class SomeClass
{
protected:
DataBase someDataBase;
};
If you need pointers (eg for polymorphism), use a std::unique_ptr
:
class SomeClass
{
protected:
std::unique_ptr<DataBase> someDataBase;
};
This depends on the rest of the program. If you can, the simplest way is to have a static Database
member inside SomeClass
:
class SomeClass
{
protected:
static DataBase someDataBase;
// or static std::unique_ptr<DataBase> someDataBase;
};
If Database
can't be statically initialized, or if you don't want all of the SomeClass
es to share the same Database
, you can make use of the object factory pattern :
class SomeClassFactory {
// Constructors, etc
SomeClass createSomeClass(/* args */) {
return SomeClass(_database, /* args */);
}
private:
Database _database;
// or std::unique_ptr<Database> _database;
};
class SomeClass {
friend class SomeClassFactory;
// Private, only the factory can create SomeClass'es
SomeClass(Database &database, /* args */)
: database(database) {}
protected:
Database &database;
};
Then all SomeClass
es created by the same factory will share the same Database
.
1 is object composition.
2 needs another Database* declaration in SomeClass declaration, and both pointers must be initialized.
I don't know whether the concept itself has a name, but the members are referred to as static or non-static .
Your 1. would be non-static and your 2. would be static .
As for how to implement this, you seem to know how to go about the non-static variant, and for the static one, just use the static
keyword in the member declaration:
class SomeClass
{
protected:
static DataBase *someDataBase;
};
Static members can be accessed with ::
, like SomeClass::someDataBase
.
Initializing static members in C++ is not that straightforward though, see this question .
I will address both of your options:
You have that with your current setup. Each instance of SomeClass
will have a pointer to a DataBase
class.
In order to achieve this, you have to take DataBase
out of your SomeClass
, since SomeClass
no longer owns the database. You will utilize the singleton design pattern for your DataBase
class to say "there is only one instance of this class at any one time".
To do that, you will write your database class like so:
class DataBase
{
public:
DataBase();
static Database * instance(); // This is the function that is used to get the global database for use.
void addEntry(const std::string& key, double value);
double getEntry(const std::string& key);
protected:
std::map<std::string, double> table;
private:
static DataBase * pDataBase;
};
To implement the instance()
method:
static DataBase * DataBase::instance()
{
if (!pDataBase)
pDataBase = new DataBase();
return pDataBase;
}
If you would like more information on singletons, read to your heart's content here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.