在我的应用程序中,我有几个读取器从.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中打开实际方法。我该如何解决?
答案 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。