简体   繁体   English

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

[英]Passing abstract c++ class to constructor and calling member method

Is it possible to pass an abstract class as an argument and use its member functions as shown below? 是否可以将抽象类作为参数传递并使用其成员函数,如下所示? (Summary: A model is created that needs a solver derived from a base class, and where the system of equations to be solved may change). (摘要:创建了一个需要从基类派生的求解器的模型,并且要求解的方程组可能会发生变化)。

#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);

}

This will compile, but the problem is that it seg faults where I've put the comment above. 这将编译,但问题是它在我上面的注释中出现了错误。 Can anyone explain why (I wasn't able to find anything with regards to this)? 谁能解释为什么(我对此一无所获)? Your suggestions are welcome 欢迎您的建议

To your first question: you can have abstract classes as parameter of methods or constructor - this is the core of the polymorphism. 第一个问题:您可以将抽象类用作方法或构造函数的参数-这是多态性的核心。 After have said this, off to your problem. 说完这些,就解决了您的问题。

The problem in your code is a double delete/dangling pointer. 您的代码中的问题是双重删除/悬挂指针。 And as pointed out by others you should adhere to Rule of Three and (better) use smart pointers rather than raw pointers to prevent this. 正如其他人所指出的那样,您应该遵守“三规则”,并且(更好)使用智能指针而不是原始指针来防止这种情况。

The problem starts at line: 问题开始于:

Model model = Model(solver, system);

With Model(solver, system) a Model-object is created and a copy of it is assigned to model . 使用Model(solver, system)创建一个Model对象,并将其副本分配给model Now there are two Model-object, but both share the same solver-pointer (because you didn't overwritten the assingment operator, remember the Rule of Three!). 现在有两个模型对象,但是都共享相同的求解器指针(因为您没有覆盖分配运算符,所以请记住三个规则!)。 When the fist of them goes out of scope, the destructor is called and the pointer solver is destroyed. 当它们的拳头超出范围时,将调用析构函数,并且销毁指针solver The second Model-object has now a bad pointer. 第二个模型对象现在有一个错误的指针。 When the second object goes out of scope, the solver will be freed once again - which ends in undefined behaviour. 当第二个对象超出范围时,求解器将再次被释放-以未定义的行为结束。 My compiler is forgiving and I don't see anything. 我的编译器可以原谅,但我什么也看不到。

It is worse, if you dereference the solver-pointer after it was freed once - a segfault is the result. 更糟糕的是,如果释放一次解引用指针后就取消引用,则会导致段错误。 This is not the case in your code, because both objects go out of scope directly after each other at the end of main . 在您的代码中情况并非如此,因为两个对象在main的末尾彼此直接超出范围。 Yet you can trigger the segfault by: 但是,您可以通过以下方式触发段错误:

  Model model = Model(NULL, system);
  {
     model = Model(solver, system);
  }
  model.getSolution(0.1);

Now, the first model goes out of scope before the call of getSolution because its scope is in between {} . 现在,第一个模型超出了调用getSolution的范围,因为它的范围在{}之间。 Thereafter the model has a bad pointer and dereferences it somewhere along the call of getSolution . 此后,该模型有一个错误的指针,并在调用getSolution某个地方取消了对它的引用。

A better design would be to use smart pointers rather then raw pointer: std::shared_ptr<AbstractSolver> or (depending on your design) std::unique_ptr<AbstractSolver> 更好的设计是使用智能指针,而不是原始指针: std::shared_ptr<AbstractSolver>或(取决于您的设计) std::unique_ptr<AbstractSolver>

With shared pointer your Model class would look like: 使用共享指针,您的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}

... }; ...};

The biggest gain is: you don't have to manage the memory at all and thus cannot make mistakes. 最大的好处是:您根本不需要管理内存,因此不会出错。 Solver pointer is deleted first after the last owner goes out of scope. 最后一个所有者超出范围后,解算器指针将首先删除。 Due to the Rule of Three you don't need neither copy constructor nor assignment operator, as you don't need destructor. 由于三规则,您不需要复制构造函数或赋值运算符,因为您不需要析构函数。

If you come from java: shared_ptr behaves (almost) exactly as pointers from java. 如果您来自Java: shared_ptr行为(几乎)与java的指针完全一样。

If your Model owns (opposed to shares) the solver, you should use std::unique_ptr -this way it is much clearer and you can be sure, that your solver isn't shared. 如果您的模型拥有(反对共享)求解器,则应使用std::unique_ptr这样一来,它就更加清晰了,并且可以确定,您的求解器没有共享。

With this you error should go away. 有了这个,错误就会消失。 But there are some other points you could improve: 但是您还可以改善其他一些方面:

a) use rather private than protected members: with protected members the subclasses are tighter coupled with the base class as it would be the case with private members - it is much harder later on to change those. a)使用私有成员而不是受保护成员:受保护成员的情况下,子类与基类之间的联系更加紧密,私有成员会遇到这种情况-以后更改这些成员要困难得多。

b) don't use: b)不要使用:

Model model = Model(solver, system);

use rather: 使用:

Model model(solver, system);

this way you initialize the object directly and don't need any copying, which is faster. 这样,您可以直接初始化对象,而无需任何复制,这更快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM