【问题标题】:Is it bad programming practice to mix virtual and non-virtual functions in a base class?在基类中混合虚拟和非虚拟函数是不好的编程习惯吗?
【发布时间】:2014-05-07 09:57:44
【问题描述】:

我有一个基类 Base,我声明了它的几个多态子类。一些基类的函数是纯虚拟的,而另一些则由子类直接使用。

(这都是C++)

例如:

class Base
{
protected:
     float my_float;
public:

    virtual void Function() = 0;

    void SetFloat(float value){ my_float = value}

}

class subclass : public Base
{

  void Function(){ std::cout<<"Hello, world!"<<std::endl; }
}

class subclass2 : public Base
{

  void Function(){ std::cout<<"Hello, mars!"<<std::endl; }
}

如您所见,子类将依赖于设置“my_float”的函数的基类,但相对于其他函数将是多态的。

所以我想知道这是否是一种好习惯。如果你有一个抽象基类,你应该让它完全抽象还是可以做这种混合方法?

【问题讨论】:

  • 这对我来说听起来很正常。我每天都使用它们。
  • 你可以这样做。没有什么比我在这里能感觉到的糟糕的编程了。这就是制作纯虚函数的原因。

标签: c++ class polymorphism virtual subclass


【解决方案1】:

这是一种常见的做法。事实上,一些著名的设计模式都依赖于此,例如Template Method Pattern。简而言之,这允许您将通过类层次结构描述的行为的某些方面指定为不变,同时让该行为的其他方面根据您在给定点引用的实例的特定类型而变化。

它是否好用取决于您的精确用例:在所有基类之间共享浮点成员数据存储的实现是否有意义?这对于您发布的示例有点难以回答,因为派生类不以任何方式依赖my_float,但是在很多情况下这是有意义的,并且是拆分类层次结构职责的好方法.

即使在跨类共享细节实现确实有意义的情况下,您也有其他几种选择,例如使用组合来共享功能。与通过组合共享此功能相比,通过基类共享功能通常可以让您不那么冗长,因为它允许您共享实现和接口。为了说明,您的解决方案比使用组合的替代方案具有更少的重复代码:

class DataStorage {
private:
  float data_;
public:
  DataStorage()
  : data_(0.f) {
  }

  void setFloat(float data) {
    data_ = data;
  }
};

class NotASubclass1 {
private:
  DataStorage data_;
public:
  void SetFloat(float value){ data_.setFloat(value); }  
  ...
}

class NotASubclass2 {
private:
  DataStorage data_;
public:
  void SetFloat(float value){ data_.setFloat(value); }  
  ...
}

【讨论】:

    【解决方案2】:

    能够拥有一些非虚拟的功能有一定的好处,其中许多密切相关:

    • 您可以修改它们,知道通过 Base*/Base&amp; 的调用将使用您修改后的代码,而不管 Base* 指向的实际派生类型是什么

      • 例如,您可以收集所有Base*/&amp;s 的性能测量值,而不管它们是由什么派生而来的

      • 非虚拟接口 (NVI) 方法旨在实现“两全其美” - 非虚拟函数调用非公共虚拟函数,为您提供通过 Base*/&amp; in @ 拦截调用的单一位置987654326@ 以及可定制性

    • 对非虚拟函数的调用可能会更快 - 如果是内联的,对于像 get/set 这样的小字节字段这样的琐碎函数来说,速度会快一个数量级

    • 您可以确保从Base 派生的所有对象的不变量,选择性地封装一些私有数据和影响它的函数(C++11 中引入的final 关键字允许您在层次结构中进一步执行此操作)

    • Base 类中“最终确定”数据/功能有助于理解和推理类行为,并且因式分解使整体代码更加简洁,但必然以令人沮丧的灵活性和不可预见的重用为代价 - 调整口味

    【讨论】:

      猜你喜欢
      • 2016-05-20
      • 2016-04-26
      • 1970-01-01
      • 1970-01-01
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      • 2016-10-21
      • 2015-04-19
      相关资源
      最近更新 更多