C ++类继承设计选择

时间:2015-05-01 17:48:53

标签: c++ inheritance design-patterns

我想知道以下两种类设计选择中的一种在性能和/或可维护性方面是否优越,或者是否有利于一种方法优于另一种方法。

第一种方法:

基类(父)具有私有数据成员和一个纯虚拟void函数。子类(childA)将参数从其默认构造函数传递给父级,并使用父级的getter方法打印出一些其他值。

第二种方法:

基类(oncle)只包含一个虚拟析构函数和一个纯虚方法。子类(nephew)将值存储为私有数据成员,并具有自己的getter函数来执行某些计算。

以下是一些代码:

#include <iostream>

class parent{
public:
    parent(int x){ _x = x; }
    virtual ~parent(){}
    virtual void calc( void ) = 0;
    int getX( void ) { return _x; }
private:
    int _x;
};

class childA: public parent{
public:
    childA(int x): parent(x){}
    void calc( void ){std::cout << "x sq: " <<    parent::getX()*parent::getX()  << std::endl;}
};


class oncle{
public:
    virtual ~oncle(){}
    virtual void calc( void ) = 0; 
};

class nephewA: public oncle{
public:
    nephewA(int x): _x(x){}
    void calc( void ){std::cout << "x sq: " << getX()*getX() << std::endl;}
    int getX( void ){ return _x; }
private:
    int _x;
};

int main(int argc, char *argv[])
{

    parent *first = new childA(3);
    parent *second = new childB(3);

    first->calc();
    second->calc();

    oncle *foo = new nephewA(3);
    oncle *bar = new nephewB(3);
    foo->calc();
    bar->calc();


    delete bar;
    delete foo;
    delete second;
    delete first;

    return 0;
}

简而言之:第一种方法强调父类,第二种方法强调子类。

那么有任何可量化的方面可以帮助我选择两种方法中的一种吗?

1 个答案:

答案 0 :(得分:1)

<强>性能:

通常,性能问题在很大程度上取决于编译器的优化器,最好通过ad-hoc基准来解决。

尽管如此,对于这个简单的例子,两种方法的表现应该是相同的:

  • 方法1:每次调用calc()都是虚拟调用(间接函数调用)。该函数调用两次父的getX(),这是一个非虚函数调用(即每个calc()在编译时知道它必须调用哪个函数)在编译时完全确定。
  • 方法2:calc()的每次调用也是虚拟调用(间接函数调用)。该函数调用两次对象的getX(),这是一个非虚函数调用,在编译时完全确定。

此外,编译器可以非常容易地在此处内联非虚函数。因此,优化的代码肯定不会调用函数,而是直接使用变量。

<强>可维护性

你应该问问自己成员(x / getX())实际上与定义它们的类绑定了多少,以及你的设计的每个元素可能发展的可能性有多大。

  • 方法1 假设x对所有孩子的管理方式相同,没有例外。
    • 如果是这种情况,此设计具有集中维护的优势。例如,如果明天你需要x成为一个双重的,那么实现这样的演变将是直截了当的。
    • 如果这个基本的假设会发生变化,那么你所要做的就是为应该有不同行为的孩子定义getX()。唯一的缺点是这些孩子仍然会有一个私人的x,它将保持未使用状态。如果你有几个对象,这不是一个严重的问题。如果你在内存中有数百万个这样的对象,这可能不是最理想的。
  • 方法2 假设每个侄子都负责管理自己的getX()这是一个非常灵活的设计,特别是如果有许多不同的规则:
    • 内存管理将始终为每个侄子量身定制。如果一个侄子例如从另一个值推导出x,那么就不需要存储冗余信息。
    • 灵活性也有其缺点,特别是如果它不符合现实:它可能需要复制并粘贴许多侄子的相同成员的相同定义。这样的代码将难以维护(因为任何对x或getX()的chagne都必须在所有侄子中复制)。

结论:

如你所见,没有一个最好的答案。这完全取决于类和成员之间的真实关系。

一些例子:

  • 如果父母是雇员,而孩子在工作流中扮演角色,而x是年龄,我会选择方法1(x实际上是父母的约束)。
  • 如果oncle是一个抽象的几何形状,newphew具体的形状,如square / triangle / octogon,x是最大宽度,我会选择接近2(x真的与侄子绑定)。