子类中的方法不会被父类中的虚函数调用

时间:2017-11-15 12:36:51

标签: c++ virtual-method

在我的应用程序中,我有几个读取器从.csv文件中读取数据。我想通过为那些共同拥有方法getData(std::string filename)的读者创建一个父类来构建它们。我想通过在基类中实现虚方法来实现。文件名应该由构造函数传递。

int main()
{
    std::string filename = "file.csv";
    ChildReader1 reader = new ChildReader1(filename);
}

ChildReader1.h

class ChildReader1: public ParentReader
    {
    public:
        ChildReader1(std::string filename)
            : ParentReader(filename)
        {

        };
        void getData(std::string filename)
        {
             //get the data here
        }
    };

ParentReader.h

class ParentReader
    {
    public:
        ParentReader() {};
        ParentReader(std::string filename)
        {
            getData(filename);
        };
        ~ParentReader() {};

        virtual void getData(std::string filename) {};
    };

目前,文件名传递给ParentReader,但getData(filename)在ParentReader中打开虚拟方法,而不是在ChildReader 1中打开实际方法。我该如何解决?

4 个答案:

答案 0 :(得分:2)

您可以通过工厂解决问题:

class ParentReader
{
public:
    virtual ~ParentReader() = default
    virtual void getData(const std::string& filename) = 0;
};

template <typename T, typename ... Ts>
std::unique_ptr<T> MakeReader(const std::string& filename, Ts&&... args)
{
    static_assert(std::is_base_of<ParentReader, T>::value, "!");
    auto res = std::make_unique<T>(std::forward<Ts>(args)...);
    res->getData(filename);
    return res;
}

答案 1 :(得分:1)

在构建基(父)类时不会调用虚方法。因为派生尚未准备好。

12.7构造和销毁[class.cdtor]#4 ISO / IEC N3797

  

成员函数,包括虚函数(10.3),可以在构造或销毁期间调用(12.6.2)。当从构造函数或析构函数直接或间接调用虚函数时,包括在构造或销毁类的非静态数据成员期间,以及调用所适用的对象是正在构造的对象(称为x)或者破坏,被调用的函数是构造函数或析构函数类中的最终覆盖,而不是在更派生的类中覆盖它。如果虚函数调用使用显式类成员访问(5.2.5)并且对象表达式引用x的完整对象或该对象的基类子对象之一但不是x或其基类子对象之一,则行为未定义

答案 2 :(得分:0)

正如其他人所提到的,你不能在基类构造函数中对派生类调用虚方法,因为派生类还没有准备好。

一种解决方案是在ChildReader1

上设置工厂功能
class ParentReader {
public:
  ParentReader(){};
  void initialize(const std::string& filename){
    getData(filename);
  };
  virtual ~ParentReader(){};
  virtual void getData(const std::string& /*filename*/) {
  };
};

class ChildReader1 : public ParentReader {
private:
  ChildReader1(){}
public:
  void getData(const std::string& /*filename*/) override {
    // get the data here
  }
  static std::unique_ptr<ChildReader1> create(const std::string &filename) {
    auto reader = std::unique_ptr<ChildReader1>(new ChildReader1);
    reader->initialize(filename);
    return reader;
  }
};

int main() {
  std::string filename = "file.csv";
  auto reader = ChildReader1::create(filename);
}

工厂函数创建一个完全形成的对象,然后可以在返回之前调用虚函数。您可以将对象的构造函数设置为private,以强制调用者使用您的工厂函数。

为避免派生类之间的代码重复,您可以引入中间CRTP类。

答案 3 :(得分:0)

正如其他人所说,你不应该在构造函数中调用虚函数。如果你想一想发生了什么,原因很简单:

调用ChildConstructor - &gt;调用ParentConstructor - &gt;创建父母 - &gt;调用getData - &gt;创造儿童

在调用getData时,唯一的现有对象是Parent,因此它无法调用Child的getData。