重写虚函数 - 派生类有不同的参数
比方说,我们有以下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
如果您有多个派生类不“行为”相同(不同的派生类例如,具有不同数量的参数),你有两个选择(有一些变化):
在所有变体的基类中实现虚函数。
你的课程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
,为Derived3
op("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
循环相同的操作,并且分配不需要if
或for
循环所需的任何操作。
编辑:虽然准备自己一天的工作,但我想到了另一种解决方案,在某些情况下可能是正确的:使用op(vector<string>& sv)
而不是1,2,3或n个参数。
与许多事情一样,如果不进一步了解确切的问题,正确的解决方案并不总是清晰的。
在我的情况下,调用base-> op的代码知道哪个派生我们正在处理的对象,因此确切知道要通过多少参数。所以,不需要num_strings函数。在上面的代码中,base中的2个op函数是纯虚拟的。所以,这将导致Derived1类不能编译,除非它添加了一个接受2个参数的空操作函数。这一切似乎并不干净。主要担心的是,当我们添加一个新的派生类时,我们需要向基类添加一个新的op函数。 –
这些解决方案都不是特别“干净”的,因为如果您有多个相同功能的变体,它就不是一个干净的界面 - 就是这样。如果你总是知道派生类型和参数个数,为什么它首先需要成为一个虚函数?也许这就是你出错的地方? –
创建对象的代码将对象存储在地图中。地图
从垫答案以线索,并利用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")});
}
但是,如果没有共同的功能,你为什么使用继承所有在Base,Derived1和Derived2之间 – Kapil
它与多态的主要思想相矛盾。基类必须描述所有衍生物的通用接口。 –
op函数是3个类之间的通用功能。派生类的对象被创建并保存在地图中。地图中的值是“Base *”。当某个事件发生时,我们从地图中检索对象并调用base-> op()。这里的基指针可以指向任何派生类。调用base-> op的代码知道我们引用的是哪个派生类,因此知道是否传递1个字符串,2个字符串或其他一组参数。 –