2

我想为我的 Source-Plugins 建立一个工厂,如下所示:

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

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

编辑:

我将超类方法声明为虚拟方法:

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

我将覆盖命令添加到子类的读取方法中:

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

};

但它仍然使用超类方法。一定还有别的原因……

顺便提一句。我将 Source-Class 放入 boost::iostream::filtering_istream 中,如下所示:

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

所以我自己不调用 read-method。

4

3 回答 3

3

问题是这样的:

in.push(*source);

根据文档,这将复制参数。由于您的基类是可复制的而不是抽象的,因此您会遇到仅复制基本子对象的切片问题。

您应该能够通过传递引用包装器来修复它:

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

我建议您将基类设为抽象(或至少不可复制),以防止切片的可能性。我不明白你为什么不把它抽象化;但是任何要求它具体的东西听起来很可怕而且容易出错。

更新:由于您将其具体化只是为了将其传递给此函数,因此您应该再次将其抽象化,并改为传递引用包装器。

于 2013-08-16T13:44:52.230 回答
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在声明中使用旨在覆盖基类中的派生方法的所有派生方法。如果你犯了一个错误并且派生类中的方法没有覆盖基类中的任何方法,那么编译器将引发错误。

Herb Sutter 在这里解释了这些问题。

更新:在 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() 这里的参考文档,我们可以看到这个函数确实通过引用来获取它的参数。因此,我刚才描述的问题不会在这里发生。但是,此函数可能会调用另一个调用另一个函数的函数......其中一个可能会按值获取参数(或制作它的副本)。

OP 声明“ MySource 在模式中不能像往常一样抽象,因为它稍后将在模板类中使用”。这表明尝试复制该对象。

我现在不知道该建议什么,只是为了测试我上面的理论(它不能解决问题)我会暂时制作MySource' 复制构造函数protected来查看 Boost.Iostreams 是否尝试复制MySource. 如果是这样,那么代码将无法编译。

于 2013-08-16T12:59:16.440 回答
0

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

我不确定这是否可行(没有看到任何相关代码),但听起来您需要将超类方法(您调用的方法)声明为virtual,以便编译器知道运行覆盖的版本(在这种情况下, of StringSource),而不是超类版本。

希望这可以帮助!

于 2013-08-16T12:47:05.197 回答