2011-05-14 104 views
5

鉴于传统代码的覆盖方法,该系统具有的类层次结构如下:为家庭子类

  Base 
     ^
      | 
----------+--------------- 
^ ^ ^ ^ ^
|  |  |  |  | 
A1  B1 C1 D1 E1 
^ ^ ^ ^ ^
|  |  |  |  | 
A2  B2 C2 D2 E2 
....... 
^ ^ ^ ^ ^
|  |  |  |  | 
An  Bn Cn Dn En 

层次结构表示在某些特定域的消息。

基地类当然是所有消息的基类。 A1..E1是属于版本1的域的消息,A2..E2到版本2等等。请注意,An必须直接从An-1继承,因为An覆盖了An-1的特定方法。

所有消息都有一些共同的功能,所以它被定义为Base :: PerformFunctionality。功能的某些部分仅限于版本n,所以存在虚拟功能Base :: SpecificPartOfFunctionality,其被Base :: PerformFunctionality调用。

所以我的问题是如何覆盖所有An..En的Base :: SpecificPartOfFunctionality

我看到2个可能的解决方案,我不喜欢太:

  1. 实施基地::每个SpecificPartOfFunctionality和每个An..En。

    该解决方案的问题是,对于每个类的实现应该是完全相同,所以我只是重复该代码。

    其他问题是,如果引入了新类Fn,开发人员可能会忘记执行SpecificPartOfFunctionality

  2. 从基地介绍BASEN类导出,而每个从An..En BASEN太导出:

    class BaseN : public Base { 
        //... 
        SpecificPartOfFunctionality() { ...} 
    }; 
    
    class An: public An-1, public BaseN { .. } 
    

    这种解决方案的问题是,它引入了金刚石问题。

    另外一个问题是会发生什么,如果其他一些版本需要重写基地:: SpecificPartOfFunctionality了。继解决方案后,我们将介绍BaseM,这将覆盖Base :: SpecificPartOfFunctionality。所以哪个SpecificPartOfFunctionality将要求 - 的基本N基本N。这是完全混乱。

有什么建议吗?

+0

您可以更改A1..E1类吗?如果是这样的话,为什么不实施'SpecificBase'来实现A1..E1派生的'SpecificPartsOfFunctionality',在一个单一的继承方案中。 'SpecificBase'当然是从'Base'派生的。 – Constantinius 2011-05-14 21:02:31

+0

我可以改变A1 ... E1。我想你正在描述我提出的解决方案2。 – dimba 2011-05-14 21:07:20

+0

不完全,因为你使用多重继承(嗯......你的语法看起来不正确。) – Constantinius 2011-05-14 21:10:47

回答

3
struct Base { 
    virtual void PerformFunctionality() { 
    stuff(); 
    SpecificPartOfFunctionality(); 
    more_stuff(); 
    } 
private: 
    virtual void SpecificPartOfFunctionality() {} 
}; 

template<class Prev> 
struct Version3 : Prev { 
protected: // Not private if v4's override might need to call this. 
    virtual void SpecificPartOfFunctionality() { 
    v3_specific_stuff(); 
    } 
}; 

struct A1 : Base {}; 
struct A2 : A1 {}; 
struct A3 : Version3<A2> {}; 

struct B1 : Base {}; 
struct B2 : B1 {}; 
struct B3 : Version3<B2> {}; 

唯一的缺点是你不能轻易向前当前的C++构造,并可能将永远使用默认的构造函数A2和B2只是为了简单起见。

在C++ 0x中,但是,您可以转发构造函数:

template<class Prev> 
struct Version3 : Prev { 
    using Prev::Prev; 
    //... 
}; 

struct C2 : C1 { 
    C2(int); // No default constructor. 
}; 
struct C3 : Version3<C2> { 
    C3() : Version3<C2>(42) {} 
}; 

请注意,必须直接从-1继承,由于覆盖的-1的具体方法。

你误会了一些东西。 An不需要从An-1直接继承。例如,这工作得很好:

struct Example { 
    virtual void f(); 
}; 

template<class B> 
struct Intermediate : B {}; 

struct Concrete : Intermediate<Example> { 
    virtual void f(); // Overrides Example::f. 
}; 

请记住,如果这个没有的工作,那么你的无法从你的基类摆在首位覆盖SpecificPartOfFunctionality!在您的任何示例中,An不会直接从Base继承。

+0

An应直接从An-1派生的事实与我的问题无关。 An是An-1的一个子类型,因此An-1定义了一些被An覆盖的虚拟函数。 – dimba 2011-05-14 22:17:24

+1

但是他的上面的代码仍然有效。直接就是错误的术语。 An应该从An-1派生(不一定直接)。 – 2011-05-14 22:45:41

+0

@ChristianRau,是的,你说的对 – dimba 2011-05-15 04:04:25

1

在全局范围内定义一个实现该功能的朋友函数,并且只需调用它即可。例如。

void FunctionalityN(Base* b) 
{ 
    b->DoThings(); 
    ... 
} 

class An : public Base 
{ 
    friend void FunctionalityN(); 
    virtual void SpecificPartOfFunctionality() { FunctionalityN(this); } 
    .... 
}; 

严格来说FunctionalityN甚至不需要成为朋友,尽管它会让事情变得简单。这使得直接延伸到Fn,Gn等,并且随后用NewFunctionalityN替代FunctionalityN也很容易。

+0

+1 @Matt。这是解决方案1的改进的改进,但它在An..En中有一个针对SpecificPartOfFunctionality()的实现。 – dimba 2011-05-14 21:29:44

+0

@dimba对。这并不能完全消除代码重复,尽管它确实将它最小化了,所以它可能是一个可以接受的折衷方案,因为在通用性(所有方法之一)和可伸缩性(可能随后被覆盖)之间存在固有的紧张关系,不能改变基地(我假设)。我注意到如果你可以在'Base'或其他地方声明'SpecificPartOfFunctionality'纯虚拟,那么你就避免了将来的开发者忘记覆盖它的问题。 – 2011-05-14 21:41:34

+0

不,这不会避免未来开发者忘记重写它的问题。因为1)更高版本,例如A9,你首先在A3中重写SpecificPart,必须考虑是否需要重写它,如果它们没有编译错误,但是2)如果它是纯虚拟的,那么不需要SpecificPart的A1和A2可以'将被实例化! – 2011-05-14 22:02:00