2013-08-16 35 views
2

我想建一个工厂,我的消息来源,插件这样的:C++ PROGRAMM调用父类方法,而不是在工厂模式的子方法

class PluginFactory { 
public: 
    PluginFactory(){}; 
    virtual ~PluginFactory(){}; 

    static MySource* getSourceById(int id, ParameterList& pList){ 
     switch (id){ 
      case 1: 
       return new StringSource(pList); 
      default: 
       std::cout << "Unknown PluginId!" << std::endl; 
       return nullptr; 
     } 

    } 
}; 

MySource不能是抽象像往常一样在模式,因为它会稍后将用于模板类。

当我调用返回MySource*的方法时,我得到超类MySource的方法,而不是子类StringSource的重写方法。

任何想法如何解决这个问题?

编辑:

我声明超类方法,vritual:

MySource{ 
    ... 
    virtual std::streamsize read(char* s, std::streamsize n){ 
    ... 
    } 
}; 

我加入超驰命令将子类读取方法:

class StringSource: public MySource { 
    ... 
std::streamsize read(char* s, std::streamsize n) override 
{ 
    ... 
} 

};

但它仍然使用超类方法。必须有另一个原因...

Btw。我把源码级到一个boost :: iostream的:: filtering_istream这样的:

MySource* source = PluginFactory::getSourceById(1, pluginList[0].second); 
boost::iostreams::filtering_istream in; 
in.push(*source); 

所以我不叫读法自己。

+0

请向我们展示您实际返回源代码并尝试调用其方法。 – TemplateRex

+0

我不会自己调用这个方法。我将Source类推入boost :: iostream :: filtering_istream,然后调用read-Method。见上面的编辑。 – schindi

回答

3

问题是这样的:

in.push(*source); 

按照documentation,这将复制的论点。由于您的基类是可复制的而不是抽象的,因此您会遇到切片问题,其中只复制基础子对象。

您应该能够通过传递一个参考的包装,而不是修复它:

in.push(std::ref(*source)); 

我会建议你做的基类抽象的(或至少是不可复制),以防止切片的方法可行。我不明白你不把它抽象化的原因;但无论如何要求它是具体的声音可怕和容易出错。

UPDATE:既然你做它混凝土,所以您可以将它传递给这一功能,你应该让它再次抽象,并传递一个参考,而不是包装。

+0

这是非常可怕的... in.push()需要一个具体的类,因为它将用在模板中,模板不接受抽象类。有没有解决方案将抽象类插入in-push()? – schindi

+0

@schindi:是的。按照我的回答和链接文档中的建议使用引用包装器。 –

+0

你的意思是我可以使用抽象类,如果我使用引用包装?顺便说一句。我得到一个错误,即ref不是std的成员,尽管我包含了。 – schindi

0

当我调用返回的MySource *的方法时,我得到了超类MySource的方法,而不是子类StringSource的重载方法。

我不知道这会工作(没有看到任何相关的代码),但它听起来像是你需要声明的超类方法(你调用一个)作为virtual,让编译器知道运行覆盖版本(在本例中为StringSource),而不是超类版本。

希望这有助于!

+0

猜测,到目前为止,但可能对货币:-) – TemplateRex

+0

@TemplateRex那太:d但实际上,想不出为什么超类方法在运行任何其他原因,所以也许我是对的 – Oleksiy

+0

的方法声明为虚从一开始就。见上面的编辑。 – schindi

0

您调用的MySource中的方法必须是virtual否则,派生类中的方法不会覆盖,而是隐藏它。例如,

class Base { 
public: 
      void foo() const { std::cout << "Base::foo()\n"; } 
    virtual void bar() const { std::cout << "Base::bar()\n"; } 
}; 

class Derived : public Base { 
public: 
    void foo() const { std::cout << "Derived::foo()\n"; } // hides Base::foo 
    void bar() const { std::cout << "Derived::bar()\n"; } // overrides Base::bar 
}; 

Derived d; 
Base& b = d; 

d.foo(); // outputs Derived::foo() 
b.foo(); // outputs Base::foo() 
b.bar(); // outputs Derived::bar() 

如果您使用C++ 11,我会建议使用override关键字在Derived::bar()声明:

void bar() const override { std::cout << "Derived::bar()"; } 

更准确地说,应该在使用override所有派生方法的声明都是为了覆盖基类中的方法。如果你犯了一个错误,并且派生类中的方法没有覆盖基中的任何方法,那么编译器会引发错误。

香草萨特解释了问题here

更新:在OP添加更多信息之后。

该问题的另一个可能的原因如下。如果复制基类(例如,当它通过值传递给一个函数),那么副本将丢失有关动态类型的信息。例如,考虑上面的例子,并且这些功能:

void call_bar_pass_by_value(Base x) { 
    x.bar(); 
} 

void call_bar_pass_by_reference(const Base& x) { 
    x.bar(); 
} 

然后,称他们与b给出:

call_bar_pass_by_value(b);  // outputs Base::bar() 
call_bar_pass_by_reference(b); // outputs Derived::bar() 

我不熟悉了Boost.Iostreams但望着filtering_stream::push()参考文档here我们可以看到这个函数的确引用了它的参数。因此,我刚刚描述的问题在这里不会发生。然而,这个函数可能会调用另一个调用另一个的函数,并且其中一个可能会通过值来获取参数(或者创建一个副本)。

该OP指出“MySource不能在模式中像通常一样抽象,因为它稍后将用于模板类”。这表明试图复制对象。

我不知道现在是什么劝告,但只是为了测试我的理论之上(它并没有解决这个问题),我会暂时使MySource的拷贝构造函数protected看到了Boost.Iostreams是否尝试复制MySource。如果是这样,那么代码将无法编译。

+0

我在上面添加了一些代码...我的方法被声明为虚拟的,并添加了覆盖命令。目前为止没有改变... – schindi