[英]Passing abstract c++ class to constructor and calling member method
是否可以将抽象类作为参数传递并使用其成员函数,如下所示? (摘要:创建了一个需要从基类派生的求解器的模型,并且要求解的方程组可能会发生变化)。
#include <iostream>
#include <string>
#include <functional>
class AbstractSolver {
public:
virtual ~AbstractSolver(){}
virtual double solve() = 0;
virtual void setSystem(std::function<double(double,double)> system) = 0;
};
class ConcreteSolver : public AbstractSolver {
protected:
double stepSize;
double initialValue;
std::function<double(double, double)> system;
public:
ConcreteSolver(double stepSize,
double initialValue) :
stepSize(stepSize),
initialValue(initialValue) {}
double solve() {
// implementation here ...
}
void setSystem(std::function<double(double,double)> system) {
this->system = system;
}
};
class Model {
protected:
std::function<double(double,double,double)> system;
AbstractSolver * solver;
public:
Model(AbstractSolver * solver,
std::function<double(double, double, double)> system ) {
this->solver = solver;
this->system = system;
}
~Model() {
delete solver;
}
double getSolution(double tau) {
std::function<double(double, double)> sys =
[this, tau](double t, double y) { return system(t, y, tau); };
solver->setSystem(sys); // SIGSEGV
return solver->solve();
}
};
int main(){
std::function<double(double, double, double)> system =
[](double t, double y, double tau) {
return 0;// return some mathematical expression
};
AbstractSolver * solver = new ConcreteSolver(0, 1);
Model model = Model(solver, system);
model.getSolution(0.1);
}
这将编译,但问题是它在我上面的注释中出现了错误。 谁能解释为什么(我对此一无所获)? 欢迎您的建议
第一个问题:您可以将抽象类用作方法或构造函数的参数-这是多态性的核心。 说完这些,就解决了您的问题。
您的代码中的问题是双重删除/悬挂指针。 正如其他人所指出的那样,您应该遵守“三规则”,并且(更好)使用智能指针而不是原始指针来防止这种情况。
问题开始于:
Model model = Model(solver, system);
使用Model(solver, system)
创建一个Model对象,并将其副本分配给model
。 现在有两个模型对象,但是都共享相同的求解器指针(因为您没有覆盖分配运算符,所以请记住三个规则!)。 当它们的拳头超出范围时,将调用析构函数,并且销毁指针solver
。 第二个模型对象现在有一个错误的指针。 当第二个对象超出范围时,求解器将再次被释放-以未定义的行为结束。 我的编译器可以原谅,但我什么也看不到。
更糟糕的是,如果释放一次解引用指针后就取消引用,则会导致段错误。 在您的代码中情况并非如此,因为两个对象在main
的末尾彼此直接超出范围。 但是,您可以通过以下方式触发段错误:
Model model = Model(NULL, system);
{
model = Model(solver, system);
}
model.getSolution(0.1);
现在,第一个模型超出了调用getSolution
的范围,因为它的范围在{}
之间。 此后,该模型有一个错误的指针,并在调用getSolution
某个地方取消了对它的引用。
更好的设计是使用智能指针,而不是原始指针: std::shared_ptr<AbstractSolver>
或(取决于您的设计) std::unique_ptr<AbstractSolver>
使用共享指针,您的Model类将如下所示:
class Model {
protected:
std::function<double(double,double,double)> system;
std::shared_ptr<AbstractSolver> solver;
public:
Model(const std::shared_ptr<AbstractSolver> &solver,
std::function<double(double, double, double)> system ) {
this->solver = solver;
this->system = system;
}
//not needed anymore
// ~Model() {//nothing to do}
...};
最大的好处是:您根本不需要管理内存,因此不会出错。 最后一个所有者超出范围后,解算器指针将首先删除。 由于三规则,您不需要复制构造函数或赋值运算符,因为您不需要析构函数。
如果您来自Java: shared_ptr
行为(几乎)与java的指针完全一样。
如果您的模型拥有(反对共享)求解器,则应使用std::unique_ptr
这样一来,它就更加清晰了,并且可以确定,您的求解器没有共享。
有了这个,错误就会消失。 但是您还可以改善其他一些方面:
a)使用私有成员而不是受保护成员:受保护成员的情况下,子类与基类之间的联系更加紧密,私有成员会遇到这种情况-以后更改这些成员要困难得多。
b)不要使用:
Model model = Model(solver, system);
使用:
Model model(solver, system);
这样,您可以直接初始化对象,而无需任何复制,这更快。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.