Using “super” in C++
我的编码风格包括以下习惯用法:
1 2 3 4 5 6 7 8 | class Derived : public Base { public : typedef Base super; // note that it could be hidden in // protected/private section, instead // Etc. } ; |
这使我能够使用"super"作为基别名,例如,在构造函数中:
1 2 3 4 | Derived(int i, int j) : super(i), J(j) { } |
甚至当从重写版本内的基类调用方法时:
1 2 3 4 5 6 | void Derived::foo() { super::foo() ; // ... And then, do something else } |
它甚至可以被锁住(不过,我仍然需要找到它的用途):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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为verbose和/或templated时。
事实上,超级是用Java实现的,在C语言中实现的(在这里,它被称为"BASE",除非我错了)。但是C++缺少这个关键字。
所以,我的问题是:
- 使用typedef的代码是否非常常见/罕见/从未见过?
- typedef super ok的用法是否正确(即,您是否认为不使用它的原因很强烈)?
- "超级"是否应该是一个好东西,它在C++中是否有一定的标准化,或者通过TyPulf已经足够了?
编辑:Roddy提到typedef应该是私有的。这意味着任何派生类都不能在不重新声明的情况下使用它。但我想这也会阻止超级链接(但谁会为此哭泣?).
编辑2:现在,在大量使用"super"几个月后,我完全同意Roddy的观点:"super"应该是私有的。我会对他的回答投两次赞成票,但我想我不能。
Bjarne Stroustrup在C++的设计和演化中提到EDOCX1 0作为一个关键词被ISO C++标准委员会考虑,第一次C++是标准化的。
DagBruck提出了这个扩展,称基类为"继承的"。这个建议提到了多重继承问题,并且会标记出不明确的用途。甚至斯特劳斯特鲁普也深信不疑。
经过讨论,DagBruck(是的,提出建议的同一个人)写道,该建议是可实施的,技术上是健全的,没有重大缺陷,并且处理了多重继承。另一方面,没有足够的资金支持,委员会应该处理一个更棘手的问题。
迈克尔·蒂曼迟到了,然后展示了一个typedef'ed super可以很好地工作,使用的技术与本文中提到的相同。
所以,不,这可能永远不会被标准化。
如果你没有一个副本,设计和发展是非常值得的封面价格。用过的复印件大约10美元。
我总是用"继承"而不是"超级"。(可能是由于delphi的背景),我总是将其设为私有的,以避免在类中错误地省略"继承"而子类试图使用它时出现问题。
1 2 3 4 5 | class MyClass : public MyBase { private: // Prevents erroneous use by other classes. typedef MyBase inherited; ... |
我用于创建新类的标准"代码模板"包括typedef,因此我几乎没有机会意外地忽略它。
我不认为链接的"super::super"建议是一个好主意-如果你这样做,你可能很难与一个特定的层次结构联系在一起,改变它可能会严重破坏东西。
这方面的一个问题是,如果忘记(重新)为派生类定义super,那么对super::的任何调用都将编译良好,但可能不会调用所需的函数。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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() ... } }; |
(正如Martin York在对该答案的评论中指出的,通过将typedef设置为私有而不是公共或受保护可以消除此问题。)
fwiw-microsoft已经在其编译器中添加了对super的扩展。
超级(或继承)是非常好的事情,因为如果您需要在基和派生之间插入另一个继承层,您只需要更改两件事情:1。"班级基础:foo"和2。Type
如果我记得正确的话,C++标准委员会正在考虑为此添加关键字。直到迈克尔·蒂曼指出这种typedef技巧奏效。
至于多重继承,因为它是由程序员控制的,所以您可以做任何您想要做的事情:可能是super1和super2,或者其他什么。
我刚刚找到了另一个解决办法。我对今天咬我的typedef方法有一个大问题:
- typedef需要类名的精确副本。如果有人更改类名但不更改typedef,那么您将遇到问题。
所以我用一个非常简单的模板提出了一个更好的解决方案。
1 2 3 4 5 | template <class C> struct MakeAlias : C { typedef C BaseAlias; }; |
所以现在,而不是
1 2 3 4 5 | class Derived : public Base { private: typedef Base Super; }; |
你有
1 2 3 4 | class Derived : public MakeAlias<Base> { // Can refer to Base as BaseAlias here }; |
在这种情况下,
我不记得以前看过这个,但乍一看我喜欢。正如Ferroccio所指出的,它在面对MI时并不好用,但是MI更是一个例外,而不是规则,而且没有任何东西表明需要在任何地方都可用才能发挥作用。
我在很多代码中都看到过这个习语,我很肯定在Boost的库中也看到过。然而,据我所知,最常见的名字是EDOCX1(或
如果使用模板类,这个习语特别有用。例如,考虑以下类(来自实际项目):
1 2 3 4 5 6 7 | 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; // … } |
别介意那些有趣的名字。这里重要的一点是继承链使用类型参数来实现编译时多态性。不幸的是,这些模板的嵌套级别非常高。因此,缩写词对于可读性和可维护性至关重要。
我经常看到它被使用,有时作为超级模板,当基础是一个复杂的模板类型时(例如,
使用typedef的代码是否非常常见/罕见/从未见过?
我从来没有在C++代码中看到过这样的模式,但这并不意味着它不在那里。
typedef super ok的用法是否正确(即,您是否认为不使用它的原因很强烈)?
它不允许多重继承(不管怎样,完全不允许)。
"超级"是否应该是一个好东西,它在C++中是否有一定的标准化,或者通过TyPulf已经足够了?
由于以上引用的原因(多重继承),没有。您在列出的其他语言中看到"super"的原因是它们只支持单一继承,所以"super"指的是什么并不混淆。当然,在这些语言中,它是有用的,但它在C++数据模型中没有真正的地位。
和FIY:C++/CLI支持这个概念的形式为"超我超级"关键字。但是请注意,C++/CLI也不支持多重继承。
为超类使用typedef的另一个原因是在对象继承中使用复杂模板。
例如:
1 2 3 4 5 6 7 | template <typename T, size_t C, typename U> class A { ... }; template <typename T> class B : public A<T,99,T> { ... }; |
在B类中,最好有一个a的typedef,否则在引用a的成员时,您会一直重复这个typedef。
在这些情况下,它也可以处理多个继承,但您不会有一个名为"super"的typedef,它将被称为"base"或类似的东西。
——杰弗克+
在一天内从Turbo Pascal迁移到C++之后,我就这样做了,以便对Turbo Pascal的"继承"关键字有一个等价的词,它的工作方式是相同的。然而,在C++编程了几年后,我停止了它。我发现我只是不太需要它。
我试图解决这个完全相同的问题;我提出了一些想法,例如使用可变模板和包扩展来允许任意数量的父代,但我意识到这将导致类似"super0"和"super1"的实现。我把它弄坏了,因为那几乎比不从一开始就有用。
我的解决方案涉及一个助手类
1 2 3 4 5 6 7 8 9 | template<typename BaseClass> class PrimaryParent : virtual public BaseClass { protected: using super = BaseClass; public: template<typename ...ArgTypes> PrimaryParent<BaseClass>(ArgTypes... args) : BaseClass(args...){} } |
那么,您想要使用的任何类都将声明为:
1 2 3 4 5 | class MyObject : public PrimaryParent<SomeBaseClass> { public: MyObject() : PrimaryParent<SomeBaseClass>(SomeParams) {} } |
为了避免需要在
这意味着你想要拥有
此方法的另一个限制是,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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>{} |
在你放弃这个选择之前,因为似乎有很多限制,而且每一个继承之间都有一个中产阶级,这些事情并不坏。
多重继承是一个强大的工具,但在大多数情况下,只有一个主父类,如果有其他父类,它们很可能是混合类,或者无论如何都不从
在每个类中定义
那可能就是我。
当然,每种情况都是不同的,但在决定使用哪种选择时,请考虑一下我说过的这些话。
我经常用这个。就在我发现自己多次输入基类类型时,我将用一个类似于您的typedef替换它。
我认为这是一个很好的用途。如您所说,如果您的基类是一个模板,它可以保存输入。此外,模板类可以使用参数作为模板应如何工作的策略。只要基的接口保持兼容,就可以自由地更改基类型,而不必修复对它的所有引用。
我认为通过typedef使用已经足够了。我看不出它是如何构建到语言中的,因为多重继承意味着可以有许多基类,所以您可以根据逻辑上认为最重要的基类所适合的类对它进行typedef。
我不知道这是否罕见,但我确实做过同样的事。
正如已经指出的,使语言本身成为这一部分的困难在于类使用多个继承。
我使用超级关键字。但它是微软特有的:
http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx
这是我使用的方法,它使用宏而不是typedef。我知道这不是C++的做事方式,但是当继承者链接到继承时,当只有最底层的层次结构对继承的偏移进行作用时,这是很方便的。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // 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 |
我使用的通用设置使得在继承树之间读取和复制/粘贴非常容易,继承树具有重复的代码,但必须重写,因为返回类型必须与当前类匹配。
我们可以使用一个小写的EDCOX1 10来复制在爪哇中看到的行为,但是我的编码风格是使用宏的所有大写字母。