【问题标题】:Virtual function must return a value in MS C++虚函数必须在 MS C++ 中返回一个值
【发布时间】:2014-11-02 10:56:16
【问题描述】:

我有一个基类Animal 和两个派生类BirdFish。我有两个虚函数,Animal 中的fly(int)swim(int) 分别对应BirdFishAnimal中有一个常用函数aliveSince()

class Animal{
public:
  Animal() {
    age = 0;
  }
  virtual ~Animal(){}
  static Animal* factory(int type);
  int aliveSince(){
    return age;
  }
  virtual int fly(int m){} //error, needs a return value
  virtual int swim(int m){} //error, needs a return value

private:
  int age;
};

class Bird: public Animal{
public:
  Bird(){
    totalFlight = 0;
  }
  int fly(int m){
    cout<<"Bird flew "<<m<<" metres\n";
    return totalFlight = totalFlight+m;
  }
private:
  int totalFlight;
};

class Fish: public Animal{
public:
  Fish(){
    totalSwim = 0;
  }
  int swim(int m){
    cout<<"Fish swam "<<m<<" metres underground\n";
    return totalSwim = totalSwim + m;
  }
private:
  int totalSwim;
};

Animal* Animal::factory(int type){
    if (type) return new Bird();
    else return new Fish();
}

我想像这样使用这些类:

Animal *a = Animal::factory(1); //Bird
Animal *b = Animal::factory(0); //Fish
a->aliveSince();
b->aliveSince();
a->fly(5);
b->swim(5);

我在 MS VC++ 中编译,它会产生错误,Animal::flyAnimal::swim 需要返回值。但是,这在 GNU C++ 中编译得很好。 如何在无需在虚拟方法中指定返回值的情况下摆脱错误?

请注意,我不能使这些函数纯虚拟,因为它会使 Animal 类抽象,所有派生类都需要实现 fly()swim()

【问题讨论】:

  • 你可能想写virtual int fly(int m) = 0;
  • 在这种情况下它不会使Animal 类抽象,这是我做不到的吗?我已经在问题陈述中指定了这一点。
  • "请注意,我不能将这些函数设为纯虚函数,因为这会使 Animal 类抽象化" 那么您必须返回一些合理的默认值。

标签: c++ oop object design-patterns


【解决方案1】:

更新:刚刚看到您不能使用纯虚拟方法,因为您需要一些默认值。

如果你需要一个默认值,你最好明确地返回它,而不是让编译器(或机会)决定返回值。所以只需返回 0。

原创

如果您没有在基类中提供任何实现,请使用Pure Virtual 方法。像这样:

class Animal
{
public:
  virtual int fly(int m) = 0;
  virtual int swim(int m) = 0;
};

Pure-Virtual 方法是一种没有主体的方法。它使 Animal 基类抽象 - 您将无法构造它的任何实例,只能构造实现这些方法的派生类。

【讨论】:

  • 让我们说,如果我让这些方法纯虚拟,我需要在Fish 类中定义方法fly 还是可以避免?
  • 我想我需要在派生类中定义所有的虚方法,否则它不会让我实例化BirdFish 对象?
  • 所有具体类都需要有实现(无论是它们自己还是在基类中)。
  • 编译器不会提供默认值。不返回是未定义的行为。
【解决方案2】:

正如你猜到的,这两个函数都需要返回一个值:

virtual int fly(int m){ return 0; }
virtual int swim(int m){ return 0; }

要么,返回0;在这两个范围内。这将是你的答案'正如你所问'。

或者尝试这样的事情来简化你的代码:

class Animal{
public:
  Animal()
  {
    totalMoved = 0;
  }
  virtual ~Animal(){}
  virtual int move( int m )
  {
    totalMoved += m;
    cout<<"Total distance " << totalMoved << std::endl;
    return totalMoved ;
  }

protected:
  int totalMoved;
};

class Bird: public Animal{
public:
  int move(int m)
  {
    cout<<"Bird flew "<<m<<" metres\n";
    return Animal::move( m );
  }
};

class Fish: public Animal{
public:
  int swim(int m){
    cout<<"Fish swam "<<m<<" metres underground\n";
    return Animal::move( m );
  }
};

可以在 Bird 和 Fish 类中编写特定的移动算法。 totalMove 可以共享,所以共享它。这就是 OOP 编程的概念,让您不必做双重事情。

在 Animal 中也添加了控制台输出,所以你可以看到什么时候发生。

【讨论】:

  • 在这种情况下你可以想出一个move 函数。这只是我创建的一个简化示例。在一个大项目中做这样的事情并不总是可能的。例如,我正在尝试使用基类 FileTransfer 为 FTP 和 SFTP 实现派生类,而这两种协议的某些功能非常不同。
  • 那么你的例子不适合。功能写在 Fish::move() 和 Bird::move() 中,totalMove(或 totalUploadBytes)仍然可以/可以共享。不调用 Animal::move() 会给后代类 100% 自己的功能,使后代有责任自己添加到 totalMoved 中。但是调用 Animal::move(),可以(考虑到 FTP/SFTP)处理一些共享的事件。
  • 事件如 std::function OnBytesReceived; if( OnBytesReceived ) OnBytesReceived();
  • 嗯,是的。他们确实有一些共享事件,这就是为什么我们首先有基类FileTransfer,就像Animal 类中的aliveSince()。感谢您的回答。如果我们不想将基类抽象化,确实必须在虚函数中指定返回值。这是有道理的,因为编译器不知道要返回什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-25
  • 1970-01-01
相关资源
最近更新 更多