Designing an OOP class in C ++
I have a simple question about class design in C ++.
Let's 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 that contains a pointer to an instance of the class DataBase
:
class SomeClass
{
protected:
DataBase *someDataBase;
};
This is where I'm confused as two options come to me:
- Each instance
SomeClass
will have its own database. In the sense that only the data added by this instance will be present in this database (dedicated databases). - Each instance
SomeClass
will link to a central database. The data added by any of the instancesSomeClass
will reside in one single database (global database).
Question:
- What is the name of the above concepts in OOP?
- How each of the above approaches can be achieved in C ++
source to share
What you are looking for is the topic of proficiency in C ++. When I talk about ownership, I mean who is responsible for managing the memory that holds the object.
In your first example, each SomeClass
can have their 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 one SomeClass
either takes responsibility for the Database provided to it, or creates it (in practice, you usually do this or that). This means that you can pass an object DataBase
(using a concept known as dependency injection):
DataBase *db = new DataBase();
SomeClass sc(db);
sc.doSomeStuffWithDB();
or just let the class create an object DataBase
:
SomeClass sc();
sc.doSomeStuffWithDB();
In the above example, you don't have to worry about disposing of objects DataBase
, knowing that you SomeClass
have to take care of disposal in your destructor.
In another scenario, you can share DataBase
without deleting it with SomeClass
(no matter if globally or not).
class SomeClass
{
private DataBase *db;
public SomeClass(DataBase* db);
}
SomeClass::SomeClass(DataBase* db)
{
this.db = db;
}
Here we could pass several objects SomeClass
the same DataBase
and don't have to worry about them being deleted 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 case, we were able to reuse the object DataBase
before removing it from external objects SomeClass
. In practical terms, this deletion can be managed by another class, for example DataBaseStore
, allowing objects to be managed and reused reliably DataBase
.
source to share
With composition, you can simply have DataBase
as a member:
class SomeClass
{
protected:
DataBase someDataBase;
};
With dependency injection, you basically point to a SomeClass
pointer to your shared DataBase
and SomeClass
store a pointer to it. Be careful if you have a multithreaded application, you need to protect your database write and possibly read as well.
class SomeClass
{
public:
SomeClass(DataBase* db) : someDataBase(db) {}
protected:
DataBase* someDataBase;
};
How you store and where you store the shared DataBase
is up to you.
source to share
Concept n ° 1 - composition. Database
is part of SomeClass
.
Concept # 2 has no name as far as I know.
Concept implementation n ° 1:
It's actually pretty straight forward: give a SomeClass
member a type Database
.
class SomeClass
{
protected:
DataBase someDataBase;
};
If you need pointers (e.g. for polymorphism) use std::unique_ptr
:
class SomeClass
{
protected:
std::unique_ptr<DataBase> someDataBase;
};
Implementation of Concept No. 2:
It depends on the rest of the program. If possible, the simplest way is to have a static member Database
inside SomeClass
:
class SomeClass
{
protected:
static DataBase someDataBase;
// or static std::unique_ptr<DataBase> someDataBase;
};
If Database
cannot be statically initialized or if you don't want all SomeClass
es to use the same Database
, you can use the 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 have the same Database
.
source to share
I don't know if the concept itself has a name, but the members are called static or non-static .
Your 1. will be non-static and your 2. will be static .
As for how to implement this, you seem to know how to go about the non-stationary option, and for the static one, just use the keyword static
in the member declaration:
class SomeClass
{
protected:
static DataBase *someDataBase;
};
Static members can be accessed with ::
eg SomeClass::someDataBase
.
Initializing static members in C ++ is not that easy, see this question .
source to share
I'll consider both options:
-
You have this with your current setup. Each instance
SomeClass
will have a pointer to the classDataBase
. -
To achieve this, you must take
DataBase
from yoursSomeClass
, as itSomeClass
no longer owns the database. You use the singleton design pattern for your classDataBase
to say "there is only one instance of this class at any given time."
To do this, you write your database class like this:
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 method instance()
:
static DataBase * DataBase::instance()
{
if (!pDataBase)
pDataBase = new DataBase();
return pDataBase;
}
If you need more information on singlons read your heartfelt content here .
source to share