3

我们有一个限制,一个类不能作为超过 7 个类的基类。有没有办法在编译时强制执行上述规则?

我知道 Andrew Koenig 的 Usable_Lock 技术可以防止类被继承,但只有当我们尝试实例化该类时它才会失败。自己推导时不能这样做吗?

允许基类知道谁是它的孩子。所以我想我们可以声明一个朋友类的组合并封装它们来执行这个规则。假设我们尝试这样的事情

class AA {
   friend class BB;
   private:
      AA() {}
      ~AA() {}
};

class BB : public AA {

};

class CC : public AA 
{};

类 CC 的派生将生成编译器警告 abt inaccessible dtor。然后,我们可以使用编译器调整将此类警告标记为错误(例如将所有警告标记为错误),但我不想依赖此类技术。

另一种方式,但在我看来相当笨拙的是: -

class B;

class InheritanceRule{
    class A {
    public:
        A() {}
        ~A() {}
    };
    friend class B;
};

class B {
public:
    class C : public InheritanceRule::A
    {};
};


class D : public InheritanceRule::A{};

类 D 的派生将被标记为编译器错误,这意味着要派生的所有类都应在类 B 中派生。这将允许至少检查从类 A 派生的类的数量,但不会阻止任何人添加更多.

这里有人有办法吗?如果基类不需要知道谁是它的孩子,那就更好了。

注意:作为基类的类本身可以被实例化(它不是抽象的)。

提前致谢,

EDIT-1:根据 jon.h 的评论,稍作修改

// create a template class without a body, so all uses of it fail
template < typename D> 
class AllowedInheritance;

class Derived; // forward declaration
// but allow Derived by explicit specialization 
template<> 
class AllowedInheritance< Derived> {};

template<class T>
class Base : private AllowedInheritance<T> {};

// privately inherit Derived from that explicit specialization    
class Derived : public Base<Derived> {};

// Do the same with class Fail Error
// it has no explicit specialization, so it causes a compiler error
class Fail : public Base<Fail> {}; // this is error

int main()
{   
   Derived d;

   return 0;
}
4

4 回答 4

5

抱歉,我不知道如何使用编译器强制执行任何此类限制。

就我个人而言,我不会费心试图将规则强加到代码本身中——你正在用与代码所做的事情无关的东西弄乱代码——这不是干净的代码。

与其跳过篮球,我会尝试放松该规则。相反,它应该是一个可以在必要时打破并与团队中其他人达成一致的指导方针。

当然,我不知道你在做什么,所以这条规则可能是合适的,但总的来说可能不是。

任何说你不能做 x 或者你必须总是做 y 的编程“规则”几乎总是错误的!注意里面的“几乎”这个词。

有时你可能需要超过 7 个派生类——那你会怎么做?跳过更多的箍。另外,为什么是7?为什么不是 6 或 8?这太武断了——这是一个糟糕的规则的另一个迹象。

如果你必须这样做,正如 JP 所说,静态分析可能是更好的方法。

于 2009-04-30T08:06:36.270 回答
2

许多各种静态代码分析工具都提供了有关继承层次结构的信息。我不会尝试在您的代码中处理它,而是研究一种工具,该工具可以为继承层次结构设置一些规则,如果不遵循这些规则,则构建失败。可能要花一点钱,并且您可能必须编写自定义规则(我已经看到了继承深度,但没有看到您想要的继承“宽度”)。但是,从长远来看,我认为这是你最好的选择。

每条评论:我使用Coverity取得了一些成功。有点花钱。有几个不错的 SO 线程可能有更好的选择。

于 2009-04-30T05:31:49.043 回答
2

我累得要命,几乎睁不开眼睛,所以可能有一种更优雅的方法来做到这一点,而且我当然不赞同 Base 最多应该有七个子类的奇怪想法。

// create a template class without a body, so all uses of it fail
template < typename D, typename B> class AllowedInheritance;


class Base {};
class Derived; // forward declaration

// but allow Derived, Base by explicit specialization 

template<> class AllowedInheritance< Derived, Base> {};

// privately inherit Derived from that explicit specialization    
class Derived : public Base, private AllowedInheritance<Derived, Base> {};


// Do the same with class Compiler Error
// it has no explicit specialization, so it causes a compiler error
class CompileError: public Base, 
     private AllowedInheritance<CompileError, Base> { };

//error: invalid use of incomplete type 
//‘struct AllowedInheritance<CompileError, Base>’


int main() {

   Base b;
   Derived d;
   return 0;
}

来自 jon.h 的评论:

例如,这是如何停止的:class Fail : public Base { }; ? \

它没有。但是OP的原始示例也没有。

致 OP:您对我的回答的修订几乎是 Coplien 的“Curiously recurring template pattern”的直接应用]

我也考虑过这一点,但问题是 aderived1 : pubic base<derived1>和 a之间没有继承关系derived2 : pubic base<derived2>,因为base<derived1>andbase<derived2>是两个完全不相关的类。

如果您唯一关心的是实现的继承,那没问题,但是如果您想要接口的继承,您的解决方案就会打破这一点。

我认为有一种方法可以同时获得继承和更简洁的语法;正如我所提到的,当我编写解决方案时我很累。如果不出意外,通过在您的示例中将 RealBase 作为 Base 的基类是一个快速修复。

可能有很多方法可以清理它。但我想强调的是,我同意 markh44:尽管我的解决方案更简洁,但我们仍然将代码弄得杂乱无章,以支持一个毫无意义的规则。仅仅因为可以做到这一点,并不意味着就应该这样做。

如果所讨论的基类已经使用了十年并且太脆弱而无法继承,那么真正的答案是修复它。

于 2009-04-30T06:20:30.673 回答
1

您可以使用GCC-XML 之类的东西,而不是用断言使代码混乱,它使用 g++ 编译器前端解析 C++ 源代码并生成 XML 输出。我希望开发一个解析此输出并检查是否违反规则的工具会相当简单;然后可以将其与源代码签入集成。

顺便说一句,让基类知道它们的后代违反了开闭原则,这意味着它实际上削弱了一般 OO 编程的有用性。将代码分成基类和子类的主要原因是基类不必知道它的子类——这使得安装后交付插件包之类的事情成为可能。

于 2009-04-30T11:28:23.913 回答