У меня есть класс «База данных», в котором есть карта, в которой хранятся пользователи. Также есть класс «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
действительно наследоваться от Database
? Помните «есть-а» против «имеет-а». Является ли Admin
Database
? Или у Database
есть Admin
?
У вас проблемы, потому что вы наследуете несвязанные типы друг от друга. Решение состоит в том, чтобы не использовать наследование для этой задачи.
«Администратор» должен иметь доступ к методам класса «База данных». Что можно сделать в этом случае?
@insania37 Просто передайте объект базы данных методам администратора. Спросите себя, как бы это работало, если бы у вас было две базы данных и вы хотели бы, чтобы один и тот же администратор работал с обеими базами данных. Простой ответ — передать объект базы данных тем методам администратора, которые в нем нуждаются.
Не используйте наследование для этой задачи. Вместо этого вам, вероятно, следует добавить ссылку в качестве параметра для функций администратора и аутентификации. Если Auth и Admin нуждаются в доступе к функциям базы данных, объявите их как дружественные классы.
погуглите "виртуальное наследование".
Я не думаю, что виртуальное наследование является ответом на этот вопрос.
@churille e.a: конечно, это не база данных, а, может быть, «DatabaseObtainable»? Возникает та же проблема.
@GaryNLOL Это, вероятно, не ответ на что-то особенное в современных проектах, но он отвечает на вопрос о том, как избежать нескольких подобъектов (и использовать одну и ту же базу данных, в чем, я полагаю, и заключается проблема).
Класс «Auth» и класс «Admin» должны иметь доступ к методам класса «Database». Извините, но я не понимаю, как в этом случае создать один и тот же объект класса "База данных" в классах "Авторизация" и "Админ"...
Проблема:
Авторизации и администратору требуется доступ к членам базы данных, но они не должны создавать новый экземпляр базы данных.
Решение:
Трудно помочь без Минимально воспроизводимого примера, но из того, что видно, вам, вероятно, следует использовать дружественные классы вместо наследования и передавать базы данных в классы по ссылке.
Что-то вроде этого:
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");
//...
Спасибо, ваш совет мне помог.
Как отмечают комментарии, Admin
и Auth
сами по себе не являются базами данных.
Я не совсем уверен, что вы делаете, но правдоподобная модель:
Database
(который содержит или имеет доступ ко всем данным).
Database
также имеет переменную-член, которая идентифицирует всех пользователей.
Затем класс Session
, который идентифицирует аутентифицированного пользователя как подключенного к базе данных.
У него будет указатель (или даже ссылка) на Database
и один на User
.
если вы хотите представить, что доступ к Database
должен быть доступен только через сеанс (за исключением некоторой начальной загрузки для создания Database
, используйте friend
.
Но будьте осторожны friend
очень мощный. Еще один класс, называемый SessionChannel
, может быть friend
и использоваться для ограничения доступа к Database
, а сам иметь `Session как друга. Ему даже не нужны никакие переменные-члены.
Лучшей практикой является использование композиции с большим предпочтением наследованию. Используйте его только там, где вам строго нужен полиморфизм, потому что строка кода должна динамически переключаться между типами. С templates
вы должны найти это относительно редко.
Наследование, объектно-ориентированный подход и C++, в частности, получили дурную славу, потому что наследованием чрезмерно злоупотребляли, и когда это происходит, на самом деле это приводит именно к той негибкости, о которой люди заявляли в первую очередь для объектно-ориентированного программирования.
Не верьте никому, кто говорит, что С++ — это все о наследовании и полиморфизме. К сожалению, так до сих пор учат, хотя это не было правдой около десяти лет, возможно, даже больше.
Если вы хотите смоделировать «настоящие» базы данных, вы можете создать объект DatabaseServer
, который имеет набор баз данных и набор пользователей, возможно, с доступом, предоставленным к нулю или более базам данных, а затем авторизации в каждой базе данных.
Тогда наследование здесь не подходит. Спросите себя: является ли Admin/Auth базой данных? Разве не было бы больше смысла, если бы они содержались в базе данных?