【问题标题】:How do I avoid creating copies of the base class when inheriting? C++继承时如何避免创建基类的副本? C++
【发布时间】:2020-12-09 18:58:46
【问题描述】:

我有一个“数据库”类,它有一个存储用户的地图。还有一个“Auth”类继承自“Database”并获取用户映射,以便在授权期间找到正确的用户。还有“Admin”类,同样继承自“Database”,可以添加新用户。但问题是当从“Database”继承“Auth”和“Admin”时,会创建两个“Database”实例。因此,如果“Admin”添加了一个新用户,“Auth”将不知道它。 Что можно сделать, чтобы не создавать две копии “数据库”?

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 数据库吗?如果它们包含在数据库中不是更有意义吗?
  • Admin 真的应该继承自Database 吗?记住"is-a" vs. "has-a"AdminDatabase 吗?还是DatabaseAdmin
  • 你遇到了麻烦,因为你从彼此继承了不相关的类型。解决方案是不使用继承来完成此任务。
  • “Admin”必须有权访问“Database”类的方法。在这种情况下可以做什么?
  • @insania37 只需将数据库对象传递给管理方法。问问自己,如果您有两个数据库并且您希望同一个管理员在这两个数据库上工作,那么它将如何工作。简单的答案是将数据库对象传递给需要它的管理员方法,

标签: c++ inheritance


【解决方案1】:

问题:

Auth 和 Admin 需要访问数据库成员,但他们不应创建新的数据库实例。

解决方案:

没有Minimal Reproducible Example 很难提供帮助,但据此可以看出,您可能应该使用友元类而不是继承,并通过引用将数据库传递给类。

类似这样的:

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");
    //...

【讨论】:

  • 谢谢你,你的建议帮助了我。
【解决方案2】:

正如 cmets 指出的那样,AdminAuth 本身并不是数据库。 我不完全确定你在做什么,但一个合理的模型是:

Database(持有或持有对所有数据的访问权)。 Database 还有has-a 成员变量,用于标识所有用户。 然后是一个Session 类,它将经过身份验证的用户标识为连接到数据库。 这将有一个指向Database 的指针(甚至是引用)和一个指向User 的指针。

如果您想表示 Database 只能通过 Session 访问(除了一些创建 Database 的引导程序,请使用 friend

但要小心friend 非常强大。另一个名为SessionChannel 的类可能是friend,用于限制对Database 的访问,并且它本身具有`Session 作为朋友。它甚至不需要任何成员变量。

最佳实践是使用组合优先于继承。仅在您严格需要多态性的地方使用它,因为代码行必须在类型之间动态切换。使用templates,您应该会发现这种情况比较少见。

继承,特别是 OO 和 C++ 的名声不好,因为继承被过度滥用,而当这种情况发生时,实际上会导致人们一开始就为 OO 声称的那种不灵活。

不要相信任何说 C++ 都是关于继承和多态的人。可悲的是,它仍然以这种方式被教导,尽管它已经有大约十年可能不是真的了。

如果您想对“真实”数据库进行建模,您可以创建一个 DatabaseServer 对象,该对象具有数据库集合和用户集合,这些用户可能被授予对零个或多个数据库的访问权限,然后在每个数据库中获得授权。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    • 2011-04-14
    • 2011-02-21
    相关资源
    最近更新 更多