将抽象c ++类传递给构造函数并调用成员方法

时间:2016-01-14 20:28:48

标签: c++ inheritance parameter-passing

是否可以将抽象类作为参数传递并使用其成员函数,如下所示? (摘要:创建的模型需要从基类派生的求解器,并且要求解的方程组可能会发生变化)。

[('a', 'b', 'c', 'l'), ('a', 'b', 'd', 'y', 'l')]

这将编译,但问题是它在上面放置注释的地方出错。任何人都可以解释为什么(我无法找到任何与此有关的东西)?欢迎您的建议

1 个答案:

答案 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);

这样你直接初始化对象,不需要任何复制,这更快。