1

I have a "Database" class that has a map that stores users. There is also an "Auth" class that inherits from "Database" and gets a map of users in order to find the right user during authorization. There is also the "Admin" class, which also inherits from "Database" and can add new users. But the problem is that when inheriting "Auth" and "Admin" from "Database", two instances of "Database" are created. Accordingly, if "Admin" adds a new user, "Auth" will not know about it. Что можно сделать, чтобы не создавать две копии "Database"?

class User;

class Database
{
private:
    map<int, User> users;

protected:
    Database() {}
    void readFileOfUsers();
    auto& getUsers() { return users; }
};

class Auth : public Database
{
private:
    int userID;

public:
    Admin itsAdmin;
    User* itsUser;

    Auth() : userID(0) { readFileOfUsers(); }
    void logIn(std::string login, std::string password);
};

class Admin : public Database
{
public:
    void getDatabase() { readFileOfUsers(); }// ?
    void addNewUser(std::string login, std::string password); 
};

class User
{
private:
    std::string login;
    std::string password;

public:
    void userActions();
};
yaromchikV
  • 79
  • 10
  • 2
    Then inheritance isn't the right tool here. Ask yourself: _Is_ an Admin/Auth a database? Wouldn't it make more sense if they were contained within the database? – Lukas-T Dec 09 '20 at 19:01
  • 2
    Should `Admin` really inherit from `Database`? Remember ["is-a" vs. "has-a"](https://stackoverflow.com/a/36162730/2602718). Is an `Admin` a `Database`? Or does a `Database` have an `Admin`? – scohe001 Dec 09 '20 at 19:01
  • You are getting into trouble because you are inheriting unrelated types from each other. The solution is not to use inheritance for this task. – john Dec 09 '20 at 19:03
  • "Admin" must have access to the methods of the "Database" class. What can be done in this case? – yaromchikV Dec 09 '20 at 19:05
  • @insania37 Just pass a database object to the admin methods. Ask yourself how it would work if you had two databases and you wanted the same administrator to work on both databases. The simple answer is to pass the database object to the administrator methods that need it, – john Dec 09 '20 at 19:06
  • Do not use inheritance for this task. Instead you should probably add a reference as parameter to Admin and Auth functions. If Auth and Admin need acces to Database's functions declare them as friend classes. – Gary Strivin' Dec 09 '20 at 19:06
  • google "virtual inheritance". – Peter - Reinstate Monica Dec 09 '20 at 19:08
  • I don't think virtual inheritance is the answer for this. – Gary Strivin' Dec 09 '20 at 19:09
  • @churille e.a: sure, it is not a database but maybe a "DatabaseObtainable"? The same problem arises. – Peter - Reinstate Monica Dec 09 '20 at 19:10
  • @GaryNLOL It's probably not the answer to anything much at all in modern designs, but it answers the question of how to avoid multiple subobjects (and use the same database which is what the issue is here, I believe). – Peter - Reinstate Monica Dec 09 '20 at 19:12
  • The "Auth" class and the "Admin" class must have access to the methods of the "Database" class. Sorry, but I don't understand how to create the same object of the "Database" class in the "Auth" and "Admin" classes in this case... – yaromchikV Dec 09 '20 at 19:18

2 Answers2

1

Problem:

Auth and Admin need access to Database members but they shouldn't create a new instance of Database.

Solution:

Is hard to help without a Minimal Reproducible Example, but from what it can be seen you should probably use friend classes instead of inheritance and pass the databases to the classes by reference.

Something like this:

class Database
{
    //...
    Database() {}
    friend class Auth;
    friend class Admin;
};

class Auth
{
    //...
    void logIn(Database&, std::string login, std::string password);
};

class Admin
{
    //...
    void addNewUser(Database&, std::string login, std::string password);
};

And then something like this:

    //...
    Database db;
    Admin someone;
    someone.addNewUser(db,"someUser","somePassword");
    //...
Gary Strivin'
  • 908
  • 7
  • 20
1

As comments point out Admin and Auth aren't themselves Databases. I'm not entirely sure what your quite doing but a plausible model is:

Database (that holds or holds access to all the data). Database also has-a member variable that is identifies all the users. Then a Session class that identifies an authenticated user as connected to the database. That will have a pointer (or even reference) to a Database and one to the User.

if you want to represent that a Database should only be accessed through a Session (except for some bootstrap to create the Database use friend.

But be careful friend is very powerful. Yet another class called SessionChannel could be the friend and used to restrict access to Database and itself have `Session as a friend. It doesn't even need any member variables.

Best practice is to use composition in strong preference to inheritance. Only use it where you strictly need polymorphism because the line of code must dynamically switch between types. With templates you should find that relatively rare.

Inheritance, OO and C++ in particular have got a bad name because inheritance has been wildly overused and when that happens in fact leads to exactly the sort of inflexibility people claimed for OO in the first place.

Don't believe anyone who says C++ is all about Inheritance and Polymorphism. It's sadly still taught that way even though it hasn't been true for about a decade possibly more.

If you want to model 'real' databases you might create a DatabaseServer object that has a collection of Databases and a collection of Users possibly with access granted to zero or more databases and then authorisations in each database.

Persixty
  • 8,165
  • 2
  • 13
  • 35