Как избежать создания копий базового класса при наследовании? С++

У меня есть класс «База данных», в котором есть карта, в которой хранятся пользователи. Также есть класс «Auth», который наследуется от «Database» и получает карту пользователей, чтобы найти нужного пользователя при авторизации. Также есть класс «Администратор», который также наследуется от «Базы данных» и может добавлять новых пользователей. Но проблема в том, что при наследовании «Auth» и «Admin» от «Database» создаются два экземпляра «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();
};

Тогда наследование здесь не подходит. Спросите себя: является ли Admin/Auth базой данных? Разве не было бы больше смысла, если бы они содержались в базе данных?

churill 09.12.2020 20:01

Должен ли Admin действительно наследоваться от Database? Помните «есть-а» против «имеет-а». Является ли AdminDatabase? Или у Database есть Admin?

scohe001 09.12.2020 20:01

У вас проблемы, потому что вы наследуете несвязанные типы друг от друга. Решение состоит в том, чтобы не использовать наследование для этой задачи.

john 09.12.2020 20:03

«Администратор» должен иметь доступ к методам класса «База данных». Что можно сделать в этом случае?

yaromchikV 09.12.2020 20:05

@insania37 Просто передайте объект базы данных методам администратора. Спросите себя, как бы это работало, если бы у вас было две базы данных и вы хотели бы, чтобы один и тот же администратор работал с обеими базами данных. Простой ответ — передать объект базы данных тем методам администратора, которые в нем нуждаются.

john 09.12.2020 20:06

Не используйте наследование для этой задачи. Вместо этого вам, вероятно, следует добавить ссылку в качестве параметра для функций администратора и аутентификации. Если Auth и Admin нуждаются в доступе к функциям базы данных, объявите их как дружественные классы.

Gary Strivin' 09.12.2020 20:06

погуглите "виртуальное наследование".

Peter - Reinstate Monica 09.12.2020 20:08

Я не думаю, что виртуальное наследование является ответом на этот вопрос.

Gary Strivin' 09.12.2020 20:09

@churille e.a: конечно, это не база данных, а, может быть, «DatabaseObtainable»? Возникает та же проблема.

Peter - Reinstate Monica 09.12.2020 20:10

@GaryNLOL Это, вероятно, не ответ на что-то особенное в современных проектах, но он отвечает на вопрос о том, как избежать нескольких подобъектов (и использовать одну и ту же базу данных, в чем, я полагаю, и заключается проблема).

Peter - Reinstate Monica 09.12.2020 20:12

Класс «Auth» и класс «Admin» должны иметь доступ к методам класса «Database». Извините, но я не понимаю, как в этом случае создать один и тот же объект класса "База данных" в классах "Авторизация" и "Админ"...

yaromchikV 09.12.2020 20:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
11
86
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема:

Авторизации и администратору требуется доступ к членам базы данных, но они не должны создавать новый экземпляр базы данных.

Решение:

Трудно помочь без Минимально воспроизводимого примера, но из того, что видно, вам, вероятно, следует использовать дружественные классы вместо наследования и передавать базы данных в классы по ссылке.

Что-то вроде этого:

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);
};

А потом что-то вроде этого:

    //...
    Database db;
    Admin someone;
    someone.addNewUser(db,"someUser","somePassword");
    //...

Спасибо, ваш совет мне помог.

yaromchikV 09.12.2020 21:46

Как отмечают комментарии, Admin и Auth сами по себе не являются базами данных. Я не совсем уверен, что вы делаете, но правдоподобная модель:

Database (который содержит или имеет доступ ко всем данным). Database также имеет переменную-член, которая идентифицирует всех пользователей. Затем класс Session, который идентифицирует аутентифицированного пользователя как подключенного к базе данных. У него будет указатель (или даже ссылка) на Database и один на User.

если вы хотите представить, что доступ к Database должен быть доступен только через сеанс (за исключением некоторой начальной загрузки для создания Database, используйте friend.

Но будьте осторожны friend очень мощный. Еще один класс, называемый SessionChannel, может быть friend и использоваться для ограничения доступа к Database, а сам иметь `Session как друга. Ему даже не нужны никакие переменные-члены.

Лучшей практикой является использование композиции с большим предпочтением наследованию. Используйте его только там, где вам строго нужен полиморфизм, потому что строка кода должна динамически переключаться между типами. С templates вы должны найти это относительно редко.

Наследование, объектно-ориентированный подход и C++, в частности, получили дурную славу, потому что наследованием чрезмерно злоупотребляли, и когда это происходит, на самом деле это приводит именно к той негибкости, о которой люди заявляли в первую очередь для объектно-ориентированного программирования.

Не верьте никому, кто говорит, что С++ — это все о наследовании и полиморфизме. К сожалению, так до сих пор учат, хотя это не было правдой около десяти лет, возможно, даже больше.

Если вы хотите смоделировать «настоящие» базы данных, вы можете создать объект DatabaseServer, который имеет набор баз данных и набор пользователей, возможно, с доступом, предоставленным к нулю или более базам данных, а затем авторизации в каждой базе данных.

Другие вопросы по теме