在基类中混合虚拟和非虚函数是不好的编程习惯吗?

时间:2014-03-28 04:42:48

标签: c++ class polymorphism virtual subclass

我有一个基类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”的函数的基类,但对于其他函数将是多态的。

所以我想知道这是不是很好的做法。如果你有一个抽象的基类,你应该把它做成完全抽象的,还是可以做这种混合方法?

2 个答案:

答案 0 :(得分:5)

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

它是否有用取决于您的确切用例:您是否有必要在所有基类之间共享您的float成员数据存储的实现?对于您发布的示例,这有点难以回答,因为派生类不以任何方式依赖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); }  
  ...
}

答案 1 :(得分:2)

能够拥有一些非虚拟功能有一定的好处,许多强烈相关:

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

    • 例如,您可以收集所有Base*/&的效果衡量标准,无论其推导如何

    • 非虚拟接口(NVI)方法旨在实现“两全其美”的目标。 - 非虚拟函数调用非公共虚拟函数,通过Base*/&中的Base以及可定制性

    • 为您提供一个拦截调用的单一位置
  • 对非虚函数的调用可能会更快 - 如果是内联的,对于少量字节的get / set这样的简单函数,速度可快一个数量级

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

  • 拥有数据/功能&#34;最终确定&#34;在Base类中帮助理解和推理类行为,并且因子分析使整体代码更简洁,但必然以牺牲灵活性和无法预料的重用为代价 - 调整品味