是否可以将抽象类作为参数传递并使用其成员函数,如下所示? (摘要:创建的模型需要从基类派生的求解器,并且要求解的方程组可能会发生变化)。
[('a', 'b', 'c', 'l'), ('a', 'b', 'd', 'y', 'l')]
这将编译,但问题是它在上面放置注释的地方出错。任何人都可以解释为什么(我无法找到任何与此有关的东西)?欢迎您的建议
答案 0 :(得分:4)
对于您的第一个问题:您可以将抽象类作为方法或构造函数的参数 - 这是多态的核心。在说完之后,关闭你的问题。
代码中的问题是双删除/悬空指针。正如其他人所指出的那样,你应该坚持使用Rule of Three和(更好)使用智能指针而不是原始指针来防止这种情况发生。
问题从第一行开始:
Model model = Model(solver, system);
使用Model(solver, system)
创建一个Model对象,并将其副本分配给model
。现在有两个Model-object,但两者共享相同的求解器指针(因为你没有覆盖assingment操作符,请记住Rule of Three!)。当它们的第一个超出范围时,将调用析构函数并销毁指针solver
。第二个Model-object现在有一个坏指针。当第二个对象超出范围时,解算器将再次释放 - 以未定义的行为结束。我的编译器是宽容的,我什么也看不见。
更糟糕的是,如果在释放一次解析器指针后取消它 - 会导致段错误。在您的代码中不是这种情况,因为两个对象在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}
... };
最大的收获是:你根本不需要管理内存,因此不会犯错误。在最后一个所有者超出范围后,首先删除解算器指针。由于规则为3,您既不需要复制构造函数也不需要赋值运算符,因为您不需要析构函数。
如果你来自java:shared_ptr
表现得(几乎)与java的指针一样。
如果您的模型拥有(与股票相对)求解器,您应该使用std::unique_ptr
- 这样更清晰,您可以确定,您的求解器不会被共享。
有了这个,你的错误就会消失。但是还有一些其他方面可以改进:
a)使用相当私有而不是受保护的成员:对于受保护的成员,子类与基类更紧密地结合在一起,就像私有成员一样 - 以后要更改它们要困难得多。
b)不要使用:
Model model = Model(solver, system);
使用而不是:
Model model(solver, system);
这样你直接初始化对象,不需要任何复制,这更快。