在C ++中使用“超级”

我的编码风格包括以下习惯用法:

class Derived : public Base { public : typedef Base super; // note that it could be hidden in // protected/private section, instead // Etc. } ; 

这使我可以使用“super”作为Base的别名,例如,在构造函数中:

 Derived(int i, int j) : super(i), J(j) { } 

或者甚至在其覆盖版本内从基类中调用该方法时:

 void Derived::foo() { super::foo() ; // ... And then, do something else } 

它甚至可以被链接(但我仍然可以find它的用途):

 class DerivedDerived : public Derived { public : typedef Derived super; // note that it could be hidden in // protected/private section, instead // Etc. } ; void DerivedDerived::bar() { super::bar() ; // will call Derived::bar super::super::bar ; // will call Base::bar // ... And then, do something else } 

无论如何,我发现使用“typedef super”非常有用,例如,当Base是冗长的和/或模板的时候。

事实上,super是用Java和C#实现的(除非我错了,否则它叫做“base”)。 但是C ++缺less这个关键字。

所以,我的问题是:

  • 这是使用typedef超常见/稀有/从未在您使用的代码中看到?
  • 这是使用typedef超好(即你看到强大或不强大的理由,不使用它)?
  • 应该“超级”是一件好事,它应该是有点标准化的C + +,或者是通过一个typedef已经足够使用?

编辑:罗迪提到的事实typedef应该是私人的。 这将意味着任何派生类将不能使用它没有重新声明它。 但我想这也会阻止super :: super链接(但是谁会为此哭泣?)。

编辑2:现在,在大量使用“超级”之后的几个月,我全心全意地同意罗迪的观点:“超级”应该是私人的。 我会两次提出他的答案,但我想我不能。

Bjarne Stroustrup在C ++的devise和演进中提到, super作为关键字被ISO C ++标准委员会第一次认为C ++被标准化。

Dag Bruck提出了这个扩展,称基类为“inheritance”。 该提案提到了多重inheritance问题,并且标明了不明确的用途。 即使Stroustrup也相信。

经过讨论,Dag Bruck(是的,提出这个提议的同一个人)写道,这个提案是可以实施的,技术上是可靠的,没有重大缺陷,并且处理了多重inheritance。 另一方面,还没有足够的压力,委员会应该处理更棘手的问题。

迈克尔·蒂曼迟到了,然后表明,一个typedef的超级将工作得很好,使用在这篇文章中被问到的相同的技术。

所以,不,这可能永远不会标准化。

如果你没有一个副本, devise和进化是非常值得的封面价格。 使用的副本可以有大约10美元。

我一直使用“inheritance”而不是超级。 (可能是由于Delphi的背景),而且我总是把它变成私有的 ,以避免当从类中错误地忽略了“inheritance”,但是子类尝试使用它时出现的问题。

 class MyClass : public MyBase { private: // Prevents erroneous use by other classes. typedef MyBase inherited; ... 

我用于创build新类的标准“代码模板”包含typedef,所以我几乎没有机会意外地忽略它。

我不认为这个“超级::超级”build议是一个好主意 – 如果你这样做了,你可能很难绑定到一个特定的层次结构,而改变它可能会把事情弄糟。

这样做的一个问题是,如果忘记(重新)为派生类定义super,那么对super :: something的任何调用都可以很好地编译,但可能不会调用所需的函数。

例如:

 class Base { public: virtual void foo() { ... } }; class Derived: public Base { public: typedef Base super; virtual void foo() { super::foo(); // call superclass implementation // do other stuff ... } }; class DerivedAgain: public Derived { public: virtual void foo() { // Call superclass function super::foo(); // oops, calls Base::foo() rather than Derived::foo() ... } }; 

(正如马丁·约克在这个答案的评论中所指出的,这个问题可以通过使typedef是私人的,而不是公共的或保护的来消除的。)

FWIW微软在其编译器中增加了__super的扩展。

超级(或inheritance)是非常好的事情,因为如果你需要在Base和Derived之间插入另一个inheritance层,你只需要改变两件事:1.“class Base:foo”和2. typedef

如果我没有记错,C ++标准委员会正在考虑为此添加一个关键字…直到Michael Tiemann指出这个typedef技巧有效。

至于多inheritance,因为它在程序员控制之下,所以你可以做任何你想要的:也许是super1和super2,或者其他。

我刚刚find一个替代解决方法。 我今天使用的typedef方法遇到了很大的问题:

  • typedef需要类名的精确副本。 如果有人更改类名但不改变typedef,那么你将遇到问题。

所以我想出了一个更好的解决scheme,使用一个非常简单的模板。

 template <class C> struct MakeAlias : C { typedef C BaseAlias; }; 

所以现在,而不是

 class Derived : public Base { private: typedef Base Super; }; 

你有

 class Derived : public MakeAlias<Base> { // Can refer to Base as BaseAlias here }; 

在这种情况下, BaseAlias不是私有的,我试图通过select一个应该提醒其他开发者的types名来防止不小心的使用。

我不记得以前看过这个,但乍一看我喜欢。 正如Ferruccio所指出的那样,在MI面前performance不佳,但MI更多的是例外情况,没有什么东西需要在任何地方都可以使用才能有用。

我见过很多代码中使用的这个成语,我很确定我甚至在Boost的图书馆里看到过它。 但是,据我记得最常见的名字是base (或Base ),而不是super

如果使用模板类,这个习语特别有用。 作为一个例子,考虑下面的类(从一个真实的项目 ):

 template <typename TText, typename TSpec> class Finder<Index<TText, PizzaChili<TSpec> >, PizzaChiliFinder> : public Finder<Index<TText, PizzaChili<TSpec> >, Default> { typedef Finder<Index<TText, PizzaChili<TSpec> >, Default> TBase; // … } 

不要介意有趣的名字。 这里重要的一点是,inheritance链使用types参数来实现编译时多态性。 不幸的是,这些模板的嵌套级别相当高。 因此,缩写对于可读性和可维护性至关重要。

我经常看到它使用,有时作为super_t,当基础是一个复杂的模板types( boost::iterator_adaptor这样做,例如)

这是使用typedef超常见/稀有/从未在您使用的代码中看到?

我从来没有在C ++代码中看到这种特殊模式,但这并不意味着它不在那里。

这是使用typedef超好(即你看到强大或不强大的理由,不使用它)?

它不允许多重inheritance(干净,反正)。

应该“超级”是一件好事,它应该是有点标准化的C + +,或者是通过一个typedef已经足够使用?

由于上述原因(多重inheritance),没有。 你列举的其他语言中看到“超级”的原因是它们只支持单一inheritance,所以对于“超级”是指什么没有混淆。 当然,在这些语言中它是有用的,但它在C ++数据模型中并没有真正的地位。

哦,和FYI:C ++ / CLI以“__super”关键字的forms支持这个概念。 但请注意,C ++ / CLI不支持多重inheritance。

在当天从Turbo Pascal迁移到C ++之后,我曾经这样做过,以便获得与Turbo Pascal“inherited”关键字相同的效果,这同样适用。 但是,在C ++编程几年之后,我停止了这个工作。 我发现我并不需要太多。

在超类中使用typedef的另外一个原因是在对象的inheritance中使用复杂模板时。

例如:

 template <typename T, size_t C, typename U> class A { ... }; template <typename T> class B : public A<T,99,T> { ... }; 

在B类中,对于A有一个typedef是理想的,否则在任何想引用A的成员的地方都会被卡住。

在这些情况下,它也可以使用多重inheritance,但是不会有一个名为“super”的typedef,它将被称为“base_A_t”或类似的东西。

–jeffk ++

我不知道这是否是罕见的,但我确实做了同样的事情。

正如已经指出的那样,语言本身的这一部分的困难是当一个类使用多重inheritance。

我不时使用这个。 就在我发现自己打出基类types几次的时候,我会用类似于你的types定义replace它。

我认为这可以是一个很好的使用。 正如你所说,如果你的基类是一个模板,它可以节省input。 另外,模板类可能需要参数来充当模板应该如何工作的策略。 只要基本界面保持兼容,您就可以自由地更改基本types,而无需修复所有对它的引用。

我认为通过typedef的使用已经足够了。 我无法看到它将如何构build到语言中,因为多重inheritance意味着可以有许多基类,所以您可以键入自定义的类,如果您觉得适合您的逻辑上感觉是最重要的基类。

我试图解决这个完全相同的问题; 我提出了一些想法,比如使用可变参数模板和包扩展来允许任意数量的父项,但是我意识到这会导致像“super0”和“super1”这样的实现。 我抛弃了它,因为这样做比没有它开始会更有用。

我的解决scheme涉及辅助类PrimaryParent并按如下实现:

 template<typename BaseClass> class PrimaryParent : virtual public BaseClass { protected: using super = BaseClass; public: template<typename ...ArgTypes> PrimaryParent<BaseClass>(ArgTypes... args) : BaseClass(args...){} } 

那么你想使用哪一个类将被声明为:

 class MyObject : public PrimaryParent<SomeBaseClass> { public: MyObject() : PrimaryParent<SomeBaseClass>(SomeParams) {} } 

为了避免需要在BaseClass上的PrimaryParent使用虚拟inheritance,需要使用可变数量的参数的构造函数来构造BaseClass

BaseClass publicinheritance到PrimaryParent是让MyObject完全控制了BaseClass的inheritance,尽pipe它们之间有一个辅助类。

这意味着你想要super每个类都必须使用PrimaryParent帮助器类,每个孩子只能使用PrimaryParent (因此名称)从一个类inheritance。

此方法的另一个限制是MyObject只能inheritance一个从PrimaryParentinheritance的类,并且必须使用PrimaryParentinheritancePrimaryParent 。 这是我的意思:

 class SomeOtherBase : public PrimaryParent<Ancestor>{} class MixinClass {} //Good class BaseClass : public PrimaryParent<SomeOtherBase>, public MixinClass {} //Not Good (now 'super' is ambiguous) class MyObject : public PrimaryParent<BaseClass>, public SomeOtherBase{} //Also Not Good ('super' is again ambiguous) class MyObject : public PrimaryParent<BaseClass>, public PrimaryParent<SomeOtherBase>{} 

在你放弃这个选项之前,由于看起来有很多限制,事实上在每个inheritance之间都有一个中产阶级,所以这些东西并不坏。

多重inheritance是一个强大的工具,但是在大多数情况下,只有一个主父项,如果还有其他父项,它们很可能是Mixin类,或者不能从PrimaryParentinheritance的类。 如果多重inheritance仍然是必要的(尽pipe许多情况下会使用组合来定义一个对象而不是inheritance),而不仅仅是在该类中显式定义super ,并且不从PrimaryParentinheritance。

在每个类中定义super类的想法对我来说并不是很吸引人,使用PrimaryParent允许super ,显然是一个基于inheritance的别名,留在类定义行而不是数据应该去的类体。

那可能只是我而已。

当然,每种情况都不一样,但是在决定使用哪个选项时,要考虑到我所说的这些事情。

我使用__super关键字。 但这是微软的具体情况:

http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx

这是我使用的macros而不是typedef的方法。 我知道这不是C ++的做事方式,但是当只通过inheritance将迭代器链接在一起的时候,只有最底层的基类inheritance了一个inheritance的偏移量,这样可以很方便。

例如:

 // some header.h #define CLASS some_iterator #define SUPER_CLASS some_const_iterator #define SUPER static_cast<SUPER_CLASS&>(*this) template<typename T> class CLASS : SUPER_CLASS { typedef CLASS<T> class_type; class_type& operator++(); }; template<typename T> typename CLASS<T>::class_type CLASS<T>::operator++( int) { class_type copy = *this; // Macro ++SUPER; // vs // Typedef // super::operator++(); return copy; } #undef CLASS #undef SUPER_CLASS #undef SUPER 

我使用的通用设置使得在具有重复代码的inheritance树之间读取和复制/粘贴非常容易,但是必须重写,因为返回types必须与当前类匹配。

可以使用小写super复制Java中看到的行为,但是我的编码风格是使用所有大写字母作为macros。