【问题标题】:Default value for abstract class pointer parameter抽象类指针参数的默认值
【发布时间】:2021-01-13 12:05:19
【问题描述】:

我正在尝试做这样的事情:

class Movement {
public:
    virtual void move() = 0;
};

class Walk : public Movement {
public:
    void move() { cout << "walking"; }
};

class Run : public Movement {
public:
    void move() { cout << "run"; }
};
class Animal {
public:
    virtual void print();
};

class Human : public Animal {
public:
    void print() { cout << "Human"; }
};

class Lion : public Animal {
public:
    void print() { cout << "Lion"; }
};
class Model {
    Animal* animal;
    Movement* movement;

public:
    Model(Animal* animal = new Human(), Movement* movement = new Walk()) {
        this->animal = animal;
        this->movement = movement;
    }
    void print() {
        cout << "This Model consist of one: ";
        animal->print();
        cout << ", which is: ";
        movement->move();
    }
};
int main() {
    Model first = Model(), second = Model(new Lion(), new Run());
    first.print();
    cout << endl;
    second.print();
    return 0;
}

我们如何设置抽象类指针的默认值以及如何将它们作为参数传递给 main?

我还希望能够像这样仅在一行中从 main 传递参数,而无需之前进行初始化。

谁能帮我看看我们如何在 C++ 中做这些事情?

我已经尝试并搜索了很多,但没有运气。

我正在寻找一种解决方法来做这样的事情,我们使用抽象类作为其他类的参数。

我知道不能将对象分配给指针,我只是不知道该怎么做才能满足我的要求,将抽象类作为具有默认值的参数。

这是我最近尝试使用精确代码,但不幸的是new,有人知道如何摆脱new 并达到预期的结果吗?

注意:
我的实际代码非常复杂,基本上使用抽象类进行多态性并将这些抽象类作为参数传递给具有默认参数的另一个类,如果有 ANY 其他方式来做类似的事情,我将非常感激帮助。

【问题讨论】:

  • 对象不是指针,指针不是对象。在 C++ 中没有办法做这样的事情。你可以使用new,但这显然会泄漏内存。这看起来像 Java 代码,但是 C++ 不是 Java。
  • 使用重载而不是默认参数
  • 当你在main()中声明Model first()时,你声明了函数first返回Model。谷歌“最令人头疼的解析”。
  • 再次:你了解指针和对象的区别吗?您不能创建一个对象并将其分配给一个指针,无论是在默认参数值中,还是在其他任何地方。 C++ 不能以这种方式工作。
  • 不幸的是,代码胜于雄辩,没有人能够找出甚至没有显示的代码的问题。

标签: c++ class polymorphism abstract-class default-value


【解决方案1】:

这确实是一个设计问题。在Modelclass 设计中,您要么需要决定对象所有权,要么将决定推迟到调用代码。在后一种情况下,你不能有默认参数(除非你想有全局常量HumanWalk,但我不推荐它)。

获得默认参数的一种方法是确定Model 拥有AnimalMovement 的独占所有权,并将unique_ptrs 存储给它们。像这样的:

class Model {
    unique_ptr<Animal> animal;
    unique_ptr<Movement> movement;

public:
  Model(unique_ptr<Animal> animal = make_unique<Human>(), unique_ptr<Movement> movement = make_unique<Walk>()){ 
    this->animal = std::move(animal);
    this->movement = std::move(movement);
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};

int main() {
  Model first/*no () here!*/, second(make_unique<Lion>(), make_unique<Run>()); 
  first.print();
  cout << endl;
  second.print();
  return 0;
}

【讨论】:

  • @πάνταῥεῖ 谢谢。我已经从您编译的源代码中更新了答案。
  • Np,很高兴为您提供帮助。糟糕,在我们松开 link 之前:P
【解决方案2】:

我想我想出了适合我情况的最佳解决方案。

#include <iostream>
#include <memory>
using namespace std;
class Movement {
 public:
  virtual void move() = 0;
  virtual unique_ptr<Movement> movement() const = 0;
};

class Walk : public Movement {
 public:
  void move() { cout << "walking"; }
  unique_ptr<Movement> movement() const { return make_unique<Walk>(); }
};

class Run : public Movement {
 public:
  void move() { cout << "run"; }
  unique_ptr<Movement> movement() const { return make_unique<Run>(); }
};
class Animal {
 public:
  virtual void print() = 0;
  virtual unique_ptr<Animal> animal() const = 0;
};

class Human : public Animal {
 public:
  void print() { cout << "Human"; }
  unique_ptr<Animal> animal() const { return make_unique<Human>(); }
};

class Lion : public Animal {
 public:
  void print() { cout << "Lion"; }
  unique_ptr<Animal> animal() const { return make_unique<Lion>(); }
};
class Model {
  unique_ptr<Animal> animal;
  unique_ptr<Movement> movement;

 public:
  Model(const Animal& animal = Human(), const Movement& movement = Walk()) {
    this->animal = animal.animal();
    this->movement = movement.movement();
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};
int main() {
  Model first = Model(), second = Model(Lion(), Run());
  first.print();
  cout << endl;
  second.print();
  return 0;
}

【讨论】:

  • 您的新 animalmovement 方法传统上命名为 clone。使用此名称,您的来源会更容易理解。
【解决方案3】:

你的问题是编译错误吗?有多种方法可以解决编译错误,但鉴于您的问题是关于从抽象类继承,我将重点介绍这一点。

首先,如提供的那样,您的Animal不是抽象类。抽象类不能被实例化,因为它的所有方法都是纯虚的。在 C++ 中,纯虚函数由 virtual 关键字前缀指定,在其定义中以 = 0 为后缀。例如

...
virtual void print() = 0;
...

通过将Animal 类设为抽象类,可以编译以下代码:

#include <iostream>
using namespace std;

class Movement {
 public:
  virtual void move() = 0;
};

class Walk : public Movement {
 public:
  void move() { cout << "walking"; }
};

class Run : public Movement {
 public:
  void move() { cout << "run"; }
};

class Animal {
 public:
  virtual void print() = 0;
};

class Human : public Animal {
 public:
  void print() { cout << "Human"; }
};

class Lion : public Animal {
 public:
  void print() { cout << "Lion"; }
};

class Model {
  Animal* animal;
  Movement* movement;

 public:
  Model(Animal* animal = new Human(), Movement* movement = new Walk()) {
    this->animal = animal;
    this->movement = movement;
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};

int main() {
  Model first = Model(),
        second = Model(new Lion(), new Run());
  first.print();
  cout << endl;
  second.print();
  return 0;
}

顺便说一句,您的代码也可以通过提供Animal::print() 的实现来编译。以下代码也是可编译的,但Animal不是抽象类,因为它提供了Animal::print() 的实现,而不是用= 0 后缀: p>

#include <iostream>
using namespace std;

class Movement {
 public:
  virtual void move() = 0;
};

class Walk : public Movement {
 public:
  void move() { cout << "walking"; }
};

class Run : public Movement {
 public:
  void move() { cout << "run"; }
};

class Animal {
 public:
  virtual void print() {};
};

class Human : public Animal {
 public:
  void print() { cout << "Human"; }
};

class Lion : public Animal {
 public:
  void print() { cout << "Lion"; }
};

class Model {
  Animal* animal;
  Movement* movement;

 public:
  Model(Animal* animal = new Human(), Movement* movement = new Walk()) {
    this->animal = animal;
    this->movement = movement;
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};

int main() {
  Model first = Model(),
        second = Model(new Lion(), new Run());
  first.print();
  cout << endl;
  second.print();
  return 0;
}

否则,从概念上讲,您正在做的事情在 C++ 中很好并且完全可能:将默认值分配给某个函数的参数列表中的基类指针。


重要提示:正如评论者正确指出的那样,您编写的模式很危险:您的界面使得用户可以选择性地提供一个Animal 实例。问题是:如果Model 创建者这样做了,那么可以合理地辩称他正确地拥有该对象。如果他不这样做,那么您的构造函数创建一个新的Animal 实例,但Model 既不获取对象的所有权,也不提供用户可以获取所有权的接口新的Animal 实例。因此,这会造成内存泄漏。同样,代码风险是在 Model 构造函数中使用的 Animal 实例的不明确所有权。

【讨论】:

  • 你至少应该在Model析构函数中提到没有适当删除的内存泄漏(但是你仍然有所有权的问题)。
  • 问题是关于从抽象类继承。 OP 没有故意提供Animal::print 的定义
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-12
  • 2015-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 2015-12-28
相关资源
最近更新 更多