【问题标题】:How to implement pure virtual functions with different parameter structures如何实现不同参数结构的纯虚函数
【发布时间】:2015-07-03 18:01:11
【问题描述】:

我正在构建一个名为Database 的纯虚函数类。这个想法是有一个类来处理所有数据库接口(即:openclose),并且可以在我的业务层上使用。

Database 类将以多种“风格”实现,适用于不同的数据库,例如 mySqlDatabase 和 OracleDatabase。

我想象Database 有没有代码的纯虚方法 - 只是一个头文件,如下所示:

Database.hpp

class Database {

    public:
        Database();
        virtual ~Database();

        virtual void open(const std::string databasename) = 0;
        virtual void open(const std::string databasename, const std::string username, const std::string password) = 0;
        virtual void open(const std::string databasename, const std::string schema, const std::string username, const std::string password) = 0;

.
<Other stuff>
.

}

open 的三个变体用于支持不同的数据库连接要求,从最简单的一个(例如只需要一个文件名的 Sqlite3)到 Oracle(需要所有这些变量来连接)。

我有一些关于实现的问题(我们以 oracle 为例):

a) 我是否需要在派生类头文件上再次重新声明虚拟方法,例如:

    class  OracleDatabase : public Database {

    public:
        OracleDatabase ();
        virtual ~OracleDatabase ();

        void open(const std::string databasename);
        void open(const std::string databasename, const std::string username, const std::string password);
        void open(const std::string databasename, const std::string schema, const std::string username, const std::string password);
}

b) 如何在派生类中构造open 方法的实现(让我们采用Sqlite3)?

void Sqlite3Database::open(const std::string databasename){
          ...do some stuff...
}

void Sqlite3Database::open(const std::string databasename, const std::string username, const std::string password) {
          ...do some stuff...
    }

void Sqlite3Database::open(const std::string databasename, const std::string schema, const std::string username, const std::string password) {
          ...do some stuff...
    }

我是否使用了正确的策略?我一直在浏览虚拟和纯虚拟策略,并认为这是解决我的问题的最佳方法。

有什么建议/提示吗?

OBS:我来自 C# 世界,如果这里有一些误解,我深表歉意。

【问题讨论】:

  • 你研究过基于接口的设计模式吗? en.wikibooks.org/wiki/C%2B%2B_Programming/Code/…
  • 我熟悉界面设计模式。我已经在 C# 中使用过它,这就是我想要完成的事情...我的疑问与 C++ 语法和如何实现的行为有关它,特别是具有不同的“打开”方法...
  • 我建议在子类中将您的成员函数标记为override,如:void open(const std::string databasename) override; 如果您实际上没有覆盖任何内容,这将确保您收到编译器错误。
  • 此外,像“open”/“close”和“require”/“release”这样的东西在 C++ 中最好留给 RAII
  • @Mendez:这就是 RAII 的意思,是的。

标签: c++ polymorphism virtual abstract pure-virtual


【解决方案1】:

对于编写查询函数(即所有数据库的接口相同),纯虚函数是可行的方法。

在这里,您正在尝试编写open 函数,您可能需要考虑使用Factory Design Pattern:您编写数据库时不使用任何open 函数;然后你写了一个函数,比如static std::unique_ptr&lt;Database&gt; Sqlite3Database::open(/*...*/)

使用像您所提倡的虚函数并不是一个好主意:无论如何,您有 3 个完全依赖于所使用数据库的不同函数;更糟糕的是,您的母类依赖于它的子类:要添加具有其他日志记录方案的新数据库,您必须将函数原型添加到 Database

另一种方法是使用纯虚函数(最好是 protected 并从构造函数调用以保留 RAII;并遵循 NVI idiom),它将初始化字符串作为参数,例如 @987654323 使用的字符串@。无论如何,数据库类型不完全一样可以从实例化的类型中推断出来,但想法是保留一个参数,以免有多个版本的open

(保留其试图解释的原则的旧答案)

实际上你可以做的更简单:忘记open,只需在Sqlite3Database::Sqlite3Database(/* ... */)中进行所有初始化。

毕竟,你无法在不知道数据库类型的情况下打开数据库(因为你必须知道用户名/密码,甚至更多:你必须知道需要哪些参数),所以试图从中制作一个虚拟纯函数是没有意义的。

所以,你可以做一个例子:

class Database {
    public virtual void create(/* ... */) = 0;
    // ...
};

class Sqlite3Database : public Database {
    Sqlite3Database(string filename);
    public virtual void create(/* ... */) override;
    // ...
};

class MySqlDatabase : public Database {
    MySqlDatabase(int host, short port, string username, string password);
    public virtual void create(/* ... */) override;
};

【讨论】:

  • Ekleog,我明白了你的意思......构造函数/析构函数的想法是有道理的......我只是不想使用 PDO,因为它会混淆上述 sw 层......我是考虑构建不同的构造函数,需要为实现的 db 类型提供确切的参数......
  • 我刚刚添加了一个例子来说明我的意思。这不使用 PDO。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-07
  • 2019-12-30
  • 2016-02-18
  • 2016-01-01
  • 2013-12-31
  • 2011-01-06
相关资源
最近更新 更多