C++ 子类中的方法没有';无法被父类中的虚函数调用
在我的应用程序中,我有几个从.csv文件中读取数据的读卡器。我现在想通过为那些拥有方法C++ 子类中的方法没有';无法被父类中的虚函数调用,c++,virtual-functions,C++,Virtual Functions,在我的应用程序中,我有几个从.csv文件中读取数据的读卡器。我现在想通过为那些拥有方法getData(std::string filename)的读者创建一个父类来构造它们。我想通过在基类中实现一个虚拟方法来实现它。该文件名应由构造函数传递 主要 儿童读取器1.h class ChildReader1: public ParentReader { public: ChildReader1(std::string filename) : Pa
getData(std::string filename)
的读者创建一个父类来构造它们。我想通过在基类中实现一个虚拟方法来实现它。该文件名应由构造函数传递
主要
儿童读取器1.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) {};
};
此时,filename被传递给ParentReader,但是getData(filename)在ParentReader中打开虚拟方法,而不是在ChildReader 1中打开实际方法。如何解决这个问题?在构建基类(父类)时,不会调用虚拟方法。因为派生的还没有准备好 12.7施工和破坏[类别cdtor]#4 ISO/IEC N3797 构件函数,包括虚拟函数(10.3),可在构造或销毁过程中调用(12.6.2)。当从构造函数或析构函数直接或间接调用虚函数时,包括在类的非静态数据成员的构造或销毁过程中,调用应用的对象是正在构造或销毁的对象(称为x),调用的函数是构造函数或析构函数类中的最终重写器,而不是在派生类中重写它的函数。如果虚拟函数调用使用显式类成员访问(5.2.5),并且对象表达式引用x的完整对象或该对象的一个基类子对象,而不是x或其一个基类子对象,则该行为未定义
正如其他人所提到的,您不能在基类构造函数中对派生类调用虚方法,因为派生类还没有准备好 一种解决方案是在
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);
}
类父读取器{
公众:
ParentReader(){};
无效初始化(常量std::字符串和文件名){
getData(文件名);
};
虚拟~ParentReader(){};
虚拟void getData(const std::string&/*filename*/){
};
};
类ChildReader1:公共父读取器{
私人:
ChildReader1(){}
公众:
void getData(const std::string&/*filename*/)重写{
//在这里获取数据
}
静态std::unique_ptr create(const std::string和filename){
自动读卡器=std::unique_ptr(新的儿童读取器1);
读卡器->初始化(文件名);
返回读取器;
}
};
int main(){
std::string filename=“file.csv”;
auto reader=ChildReader1::create(文件名);
}
工厂函数创建一个完全成形的对象,然后可以在返回之前调用虚拟函数。可以将对象的构造函数设置为私有,以强制调用方使用工厂函数
为了避免派生类之间的代码重复,您可以引入一个中间CRTP类。您可以通过工厂解决您的问题:
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;
}
类父读取器
{
公众:
virtual~ParentReader()=默认值
虚拟void getData(const std::string&filename)=0;
};
模板
std::unique_ptr MakeReader(常量std::string和filename、Ts和…args)
{
静态断言(std::是::value,“!”)的基础;
auto res=std::make_unique(std::forward(args)…);
res->getData(文件名);
返回res;
}
正如其他人所说,您不应该在构造函数中调用虚函数。如果你想想发生了什么,原因很简单: 调用ChildConstructor->调用ParentConstructor->创建父级->调用getData->创建子级
在调用getData时,唯一存在的对象是父对象,因此它不可能调用子对象的getData。在构建基(父)类期间不会调用虚拟方法。因为派生类尚未就绪。好的,那么我如何调用基类中子类中的方法?如果要调用子方法,则必须创建一个子对象。@Stefan,这将引入基类对其派生类的依赖关系。您必须在派生类的构造函数调用
ChildReader1
的构造函数中调用getData
。@Jarod42更好、更通用。如果父类保存所有数据,而子类仅加载数据,则可以将其作为函数传递给父类构造函数。@AA Jarod42也很好。这样做的一个问题是,没有任何东西可以阻止调用方直接调用构造函数并最终导致一个未加载的读取器。但这可以通过隐藏构造函数并使模板成为朋友来解决。此外,这些工厂解决方案也不是例外安全的-您可以获得一个notinitialize
d对象,最好使用std::make_unique
而不是new
@AA使用new
和notmake_unique
是由于构造函数是私有的,有很多方法可以解决这个问题,但我认为它超出了这个问题的范围。在什么方面它不是例外安全的?如果initialize引发异常,则工厂函数中的本地读取器将被删除。
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;
}