9

我找到了一些我正在处理的代码,并且想知道最好的设计实现是什么。

如果基类将方法定义为虚拟方法,但也实现了空主体,因此不需要派生类实现主体,是否应该改为纯方法?

virtual void AMethod1() {}                 // 1
virtual void AMethod2() {assert(false);}   // 2
virtual void AMethod3() = 0;               // 3
  1. 当前代码。
  2. Idea1:提醒用户这个派生对象没有实现这个方法体。
  3. Idea2:强制派生类实现一个主体,无论是否为空。

您,可信赖的、了不起的 SO 人,怎么看?


Edit1:发布(并阅读答案)后,我意识到断言很糟糕!

virtual void AMethod3() = {throw (ENotImplemented)};               // 4
4

10 回答 10

9

这在一定程度上取决于您的编码风格有多“纯粹”。有些人认为您应该始终只使用纯虚函数定义接口并从中派生所有具体类。

其他人则更务实,相信如果有一个好的默认实现,您可以将其添加到基类中(选项 1)。

第二个选项似乎最没用,因为它将检测延迟到运行时。大多数程序员宁愿选择选项 3 中的编译错误。

像往常一样,C++ 支持多种范例,您可以选择您喜欢的一种。

于 2011-07-27T11:11:20.373 回答
6

如果派生类必须实现此方法,则应使用选项 3。如果在派生类中实现是可选的,请使用选项 1。完全避免选项 2。

于 2011-07-27T11:06:51.273 回答
3

如果一个基类将一个方法定义为虚拟的,但也实现了一个空的主体,因此不需要派生类实现一个主体,它不应该改为纯的吗?

这取决于您是否要强制派生类覆盖该方法。如果你这样做,那么使用纯virtual; 它正是对这一要求的语言支持。如果有或以后可能有 的默认实现amethod,则使用带有实现的纯virtual方法:

class Foo {
    virtual void amethod() = 0;
};

void Foo::amethod() {
    // whatever
}

该函数现在仍然是纯virtual的,因此Foo无法实例化该类,但任何派生类都将继承实现,并且其方法可以将其调用为Foo::amethod.

于 2011-07-27T11:07:13.223 回答
2

使方法成为纯虚拟方法比使用assert实现默认实现更直观。如果在大多数情况下什么都不做是默认实现,则当前代码会更好。当然,如果您想使用多态性,它应该保持虚拟。

于 2011-07-27T11:11:44.103 回答
2

我见过很多这样的例子,你需要实例化类,所以你使用virtual一个空的主体:

virtual void AMethod1() {}                 // 1

当你想强制派生类覆盖这个函数并且你不需要默认值时使用它:

virtual void AMethod3() = 0;               // 3

所以这真的取决于你想做什么。

于 2011-07-27T11:16:14.727 回答
2
  • virtual void AMethod1() = 0;:当您的基类没有提供实现并且应该实现此行为时,纯虚拟最好的。(这是您问题中的选项 3)

  • virtual void AMethod1() {}:当您的基类没有提供实现并且可以实现此行为时,具有空实现的虚拟最好的。(这是您问题中的选项 1)

  • virtual void AMethod1() { assert(false); }assert(false):在我看来,必须避免使用带有 an 的虚拟。我看不出它有任何有效的用途。其背后的基本原理是所有用例都包含在上面的两个选项中:行为可能应该被实现,因此定义总是失败的可调用方法是没有用的。编译器可以通过阻止此调用为您处理此问题,因此此选项通过将此检查推迟到运行时来引入风险。(这是您问题中的选项 2)

于 2011-07-27T11:23:11.257 回答
1

既然你需要virtual机制;以下是我的简短回答:

(1)virtual void AMethod1() {}

要求:

- Allow creating objects of base class
- Base `virtual` method is use.

(2) virtual void AMethod2() {assert(false);}

要求:

- Allow creating objects of base class
- Base method is not used
- Force derived classes to implement the method (hard way, because it happens at runtime).

(3) virtual void AMethod3() = 0;

要求:

- Don't allow base class object creation
- Force derived classes to implement the method at compile time
于 2011-07-27T11:11:11.573 回答
1

如果我们不想实例化一个类,而是让它作为派生自它的所有类的基类,我们应该使用纯虚函数。

关于纯虚函数需要注意的重要一点是,必须在所有派生类中重写这些函数,否则编译会标记出错误。

简单例如,

class alpha     {
     public:virtual void show()=0; //pure virtual function
    };

class beta : public alpha {

     public:void show()   //overriding
        {
         cout<<"OOP in C++";
        }
    };
void main() {
     alpha *p;
     beta b;
     p=&b;
     p->show();
   }
于 2011-07-27T11:06:29.103 回答
1

如果一个基类将一个方法定义为虚拟的,但也实现了一个空的主体,因此不需要派生类实现一个主体,它不应该改为纯的吗?

这取决于您的设计。如果您的方法是纯虚拟的,那么您将向派生类开发人员发送一条消息,上面写着“您必须在此处放置一些实际代码才能使您的类正常工作”。另一方面,如果您的方法是虚拟的,有一个空主体,则消息是“您可以在此处放置一些代码,但这取决于您的实际需要”。

virtual void AMethod1() {}                 // 1
virtual void AMethod2() {assert(false);}   // 2
virtual void AMethod3() = 0;               // 3

我肯定更喜欢选项 3 而不是 2,如果派生类没有实现虚拟方法,它将产生编译错误而不是运行时错误。

于 2011-07-27T11:21:14.463 回答
0

没有简单的规则:

如果调用函数时某些派生类不做任何事情是有意义的,则使用 1(空实现)。

如果派生类不实现该功能没有意义,请使用 3。

在极少数情况下使用 2,其中函数的先决条件是另一个虚函数返回 true,并且该函数具有返回 false 的默认实现(或类似的东西)。基本上,如果接口的一部分是可选的。(但通常,在这种情况下,最好派生一个接口;实现扩展接口的类从它派生,而客户希望将它 dynamic_cast用于扩展接口。)

根据经验(但您的编程风格可能不同),1 似乎至少在 90% 的情况下都适用,而且我认为在 C++ 的 20 多年中,我使用过 3 一次,或者可能两次。

于 2011-07-27T12:56:03.850 回答