将方法参数类型更改为子类中的派生类

Posted

技术标签:

【中文标题】将方法参数类型更改为子类中的派生类【英文标题】:Change method argument type to a derived class in child class 【发布时间】:2018-06-19 15:57:59 【问题描述】:

我正在创建一个数据库接口并有两个不同的数据库实现来使用这个接口

例如:

class DBOptions 
protected:
  DBOptions();
;

class SQLliteDBOptions : public DBOptions  bool verySpecificSQLiteOption; ;
class mysqlDBOptions : public DBOptions bool verySpecificMySQLOption; ;

class DBIface 
public:
  enum FileMode 
    READ = 1,
    WRITE = 2,
    READWRITE = 3
  ;
public:
  virtual bool connect(char * filename, DBIface::FileMode mode, DBOptions * opt) = 0;
  virtual bool disconnect() = 0;
;

class SQLiteDB : public DBIface 
public:
  bool connect(char * filename, DBIface::FileMode mode, SQLliteDBOptions * options)
   std::cout << "connect form sqlite\n"; 
  bool disconnect()  std::cout << "disconnect from sqlite\n"; 
;

class MySQLDB : public DBIface 
public:
  bool connect(char * filename, DBIface::FileMode mode, MySQLDBOptions * options)
   std::cout << "connect form mysql\n"; 
  bool disconnect()  std::cout << "disconnect from mysql\n"; 
;

int main() 
  DBIface * sqlitedb = new SQLiteDB();
  SQLliteDBOptions * opt = new SQLliteDBOptions();
  sqlitedb->connect("file", DBIface::READ, opt);
  return 0;

两个派生类中的方法connect 不被视为父类中纯方法的实现。我应该怎么做才能解决这个问题?

我希望我的班级接受正确的数据库选项实例。我想我可以将 DB 选项动态转换为我需要的类型,但我想这不是最好的解决方案。

我应该创建一个 setter 方法来为每个 DB 实现设置 DB 选项吗?

【问题讨论】:

这是一个经典问题。没有通用的好解决方案。一种常见的解决方案是使 DBIface 成为由 dbOptions 类型参数化的模板。另一种是动态演员表。一个二传手比任何一个恕我直言。 另一种选择是从DBIface 中删除connect,将其作为独立方法留在子类中。由于connect 无论如何都不能调用DBIface 引用,因此不会丢失功能。 【参考方案1】:

我会说将选项参数作为派生数据库类型构造函数的参数。这样,派生类型可以要求正确的选项类型,而不必担心匹配基本签名。 只要 options 参数在任何虚方法上,它就必须是相同的(或通过逆变的基本类型)。

【讨论】:

【参考方案2】:

您必须在派生类中声明一个与纯虚方法 connect() 具有相同参数的方法。也就是说,除非您覆盖所有纯虚函数,否则派生类也将被视为抽象类。

我尝试了SoronelHaetir提出的解决方案。正如预期的那样,它有效,我认为它以一种优雅的方式解决了您的问题。

class SQLiteDB : public DBIface
public:
    SQLiteDB(SQLliteDBOptions* opt)  pOtps = opt; 
    bool connect(char * filename, SQLiteDB::FileMode mode)
    
        std::cout << "connect form sqlite\n";
    
    bool disconnect()  std::cout << "disconnect from sqlite\n"; 

private:
    SQLliteDBOptions * pOtps;
;



int main() 
    SQLliteDBOptions * opt = new SQLliteDBOptions();
    DBIface * sqlitedb = new SQLiteDB(opt);

    sqlitedb->connect("file", DBIface::READ);
    return 0;

当然,抽象基类中的虚函数也要相应修改:

virtual bool connect(char * filename, DBIface::FileMode mode) = 0;

【讨论】:

"我花了一些时间才找到问题" 问题已经给出,这不是问题的解决方案。 @krisz 你完全正确。我最终“解决”了一个没有被问到的问题。现在,我只是尝试了之前提出的一种解决方案,看看如何实现它。希望这是有用的。谢谢【参考方案3】:

我会完全摆脱connectdisconnect。此功能应分别移至派生类的构造函数和析构函数。每个构造函数可能有不同的签名,这是完全可以的,因为它们不是虚拟的。

这样,如果您有一个从DBIface 派生的对象,您可以绝对确定它是连接的。

此解决方案符合 RAII 习惯用法,因此可以推荐为首选方案。

【讨论】:

以上是关于将方法参数类型更改为子类中的派生类的主要内容,如果未能解决你的问题,请参考以下文章

java中,实现接口的方法的过程是重写吗?

比较Java方法的重载与覆盖

overload和override

抽象类和接口

类的方法重写和私有变量

九重写,重载,和new隐藏的用法区别