2017-04-18 130 views
0

比方说,我们有以下2类重写虚函数 - 派生类有不同的参数

class Base { 
public: 
    virtual ~Base(); 
    virtual void op(string& s1) = 0; 
}; 

class Derived1 : public Base { 
public: 
    virtual void op(string& s1) override; 
}; 

到目前为止,一切都很好。现在,需要一个新的要求,其中需要创建Derived2类的 ,但是该类需要2个字符串 作为op虚拟函数中的参数。当然,如果我在Derived2中添加带有2个字符串作为输入的函数op ,它将被重载而不会被重写。 所以,我必须做这样的事情 -

class Base { 
public: 
    virtual ~Base(); 
    virtual void op(string& s1) {} 
    virtual void op(string& s1, string& s2) {} 
}; 

class Derived1 : public Base { 
public: 
    virtual void op(string& s1) override; 
}; 

class Derived2 : public Base { 
public: 
    virtual void op(string& s1, string& s2) override; 
}; 

这工作,但我不得不添加第二运算功能的基类。 此外,基类功能现在不是纯虚拟的,这是 必需的,否则派生类将需要实现不必要的 op函数。这对我来说似乎有点不合适。我们需要在Base类中再添加一个 op函数。理想的情况是,基类不应该改变,如果 某人添加了一个新的派生类。

我相信有更好的方法来做到这一点。相当肯定有一个 设计模式,因为这似乎是一个常见问题,并且 必须已经解决。

有什么建议吗?

感谢, Amarnath

+2

但是,如果没有共同的功能,你为什么使用继承所有在Base,Derived1和Derived2之间 – Kapil

+0

它与多态的主要思想相矛盾。基类必须描述所有衍生物的通用接口。 –

+0

op函数是3个类之间的通用功能。派生类的对象被创建并保存在地图中。地图中的值是“Base *”。当某个事件发生时,我们从地图中检索对象并调用base-> op()。这里的基指针可以指向任何派生类。调用base-> op的代码知道我们引用的是哪个派生类,因此知道是否传递1个字符串,2个字符串或其他一组参数。 –

回答

0

如果您有多个派生类不“行为”相同(不同的派生类例如,具有不同数量的参数),你有两个选择(有一些变化):

  1. 在所有变体的基类中实现虚函数。

  2. 你的课程hieararchy不是“干净的”,你可能需要“知道”在某些情况下它是哪种类型。使用dynamic_cast(或你自己的类型检测机制,如LLVM项目,其中有llvm::dyn_cast

考虑一下:

vector<Base*> v; 

... insert derived types in v ... 

for(auto i : v) 
{ 
    v->op(???) 
} 

怎样的代码知道op是1,2或3参数呢?它不能,对吧?所以,你的选择是询问类为op多少arguemnts需要,有它取相同数量始终(例如op("abc", "", "");将用于Derived1,为Derived3op("abc", "def", "");Derived2,和op("abc", "def", "ghi");)。

或者你可以有这样的:

class Base { 
public: 
    virtual ~Base(); 
    virtual void op(string& s1) = 0; 
    virtual void op(string& s1, string &s2) = 0; 
    ... for as many string arguments you need ... 
    virtual int num_strings() { return 0; } 
}; 

class Derived1 : public Base { 
public: 
    virtual void op(string& s1) override; 
    virtual int num_strings() override { return 1; } 
}; 

class Derived2 : public Base { 
public: 
    virtual void op(string& s1, string& s2) override; 
    virtual int num_strings() override { return 2; } 
}; 

现在我们可以实现我们的通用(-ish)循环:

for(auto i : v) 
{ 
    switch(v->num_strings()) 
    { 
    case 1: 
     v->op(s1); 
     break; 
    case 2: 
     v->op(s1, s2); 
     break; 
    default: 
     ... some handling of unknown number .... 
    } 
} 

这里的关键是,如果你是“做”了同样的事情来自同一基类派生的所有对象,它们必须具有相同的可用接口。

在我有一个编译器项目中,我需要时使用llvm::dyn_cast,因为基类有许多通用操作,但不是所有操作都可用于所有派生类,因为这会使基类有一个巨大的成员函数的数量完全无用 - if不必具有与for循环相同的操作,并且分配不需要iffor循环所需的任何操作。

编辑:虽然准备自己一天的工作,但我想到了另一种解决方案,在某些情况下可能是正确的:使用op(vector<string>& sv)而不是1,2,3或n个参数。

与许多事情一样,如果不进一步了解确切的问题,正确的解决方案并不总是清晰的。

+0

在我的情况下,调用base-> op的代码知道哪个派生我们正在处理的对象,因此确切知道要通过多少参数。所以,不需要num_strings函数。在上面的代码中,base中的2个op函数是纯虚拟的。所以,这将导致Derived1类不能编译,除非它添加了一个接受2个参数的空操作函数。这一切似乎并不干净。主要担心的是,当我们添加一个新的派生类时,我们需要向基类添加一个新的op函数。 –

+1

这些解决方案都不是特别“干净”的,因为如果您有多个相同功能的变体,它就不是一个干净的界面 - 就是这样。如果你总是知道派生类型和参数个数,为什么它首先需要成为一个虚函数?也许这就是你出错的地方? –

+0

创建对象的代码将对象存储在地图中。地图。实际上,此映射中的值将指向Derived1或Derived2。发生异步事件从地图中检索基础对象,并调用base-> op()来发出事件发生的信号。根据事件,我知道这是哪个派生对象。我可以将基指针转换为派生(向下),然后使用正确数目的参数调用derived-> op()。在这种情况下,op函数不必是虚拟的。但是,我正在尝试摆脱沮丧的方式。 –

0

从垫答案以线索,并利用C++初始化列表构造正确的做法可能是(注括号{}同时呼吁OP functon):

#include <iostream> 
#include <string> 
#include <vector> 
using namespace std; 
class Base { 
public: 
    virtual ~Base() { }; 
    virtual void op(const std::vector<string>& s1) {std::cout<<"Base..."<<s1.at(0)<<std::endl;} 
    // virtual void op(const stdstring& s1, const string& s2) {std::cout<<"Base..."<<s1<<" "<<s2<<std::endl;} 
}; 

class Derived1 : public Base { 
public: 
    virtual void op(const std::vector<string>& s1) override {std::cout<<"Derived1..."<<s1.at(0)<<std::endl;} 
}; 

class Derived2 : public Base { 
public: 
    virtual void op(const std::vector<string>& s1) {std::cout<<"Derived2..."<<s1.at(0)<<std::endl;} 
}; 
int main() 
{ 
    Base *ptr= new Derived2(); 
    ptr->op({std::string("Test1"),std::string("Test2")}); 

} 
相关问题