如何使用不同的参数结构实现纯虚函数

时间:2015-04-24 23:36:20

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

我正在使用名为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#世界所以如果在这里有一些误解,我会道歉。

1 个答案:

答案 0 :(得分:3)

为了编写查询函数(即所有数据库的相同接口),可以使用纯虚函数。

在这里,您正在尝试编写open函数,您可能需要考虑Factory Design Pattern:您使用任何open函数编写数据库;并编写一个函数,如static std::unique_ptr<Database> Sqlite3Database::open(/*...*/)

使用像你所倡导的那样的虚拟功能并不是一个好主意:无论如何,你有3种不同的功能完全取决于所使用的数据库;更糟糕的是,你的母班依赖于它的孩子:要用另一个日志计划添加新数据库,你必须将函数原型添加到Database

另一种方法是使用纯虚函数(最好是protected并从构造函数调用以保留RAII;并遵循NVI idiom)作为参数初始化字符串,例如PDO使用的字符串。不完全相同,数据库类型可以从实例化的类型中推断出来,但想法是保留一个参数,以便不具有多个版本的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;
};