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 instances SomeClass

    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 ++
+3


source to share


6 answers


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

.

+2


source


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.

+2


source


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

.

+1


source


1 is the composition of the object.

2 requires a different Database * declaration in the SomeClass declaration, and both pointers must be initialized.

0


source


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 .

0


source


I'll consider both options:

  • You have this with your current setup. Each instance SomeClass

    will have a pointer to the class DataBase

    .

  • To achieve this, you must take DataBase

    from yours SomeClass

    , as it SomeClass

    no longer owns the database. You use the singleton design pattern for your class DataBase

    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 .

0


source







All Articles