我的编码风格包括以下习语:
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
}
它甚至可以被链接(尽管我仍然需要找到它的用途):
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和/或模板化的时候。
事实上,super是在Java和c#中实现的(在c#中,它被称为“base”,除非我错了)。但是c++缺少这个关键字。
我的问题是:
这种typedef的使用在你工作的代码中是超级常见/罕见/从未见过吗?
这种typedef的使用是超级Ok(也就是说,你看到强烈或不那么强烈的理由不使用它)?
“super”应该是一个好东西吗?它应该在c++中标准化吗?或者通过类型定义来使用已经足够了吗?
编辑:Roddy提到的事实类型定义应该是私有的。这意味着任何派生类在不重新声明它的情况下都不能使用它。但我猜它也会阻止super::super链接(但谁会为它哭泣呢?)
编辑2:现在,在大量使用“super”几个月后,我完全同意罗迪的观点:“super”应该是私人的。
我只是找到了一个替代方案。我有一个大问题与类型定义方法今天咬我:
类型定义需要类名的精确副本。如果有人改变了类名,但没有改变类型定义,那么你就会遇到问题。
所以我用一个非常简单的模板想出了一个更好的解决方案。
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不是私有的,我已经尝试通过选择一个应该提醒其他开发人员的类型名称来防止粗心使用。
我试图解决这个完全相同的问题;我抛出了一些想法,比如使用可变模板和包扩展来允许任意数量的父元素,但我意识到这将导致像“super0”和“super1”这样的实现。我把它扔了,因为那只会比一开始就没有它有用多少。
我的解决方案涉及一个辅助类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) {}
}
为了避免在PrimaryParenton BaseClass中使用虚拟继承,使用一个接受可变数量参数的构造函数来允许构造BaseClass。
BaseClass的公共继承到PrimaryParent的原因是让MyObject对BaseClass的继承有完全的控制,尽管它们之间有一个帮助类。
这确实意味着您希望拥有super的每个类都必须使用PrimaryParent助手类,并且每个子类只能从一个使用PrimaryParent的类继承(因此得名)。
这个方法的另一个限制是,MyObject只能继承一个从PrimaryParent继承的类,而且这个类必须使用PrimaryParent继承。我的意思是:
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>{}
在你放弃这个选择之前,因为似乎有很多限制,而且每一项继承之间都有一个中间阶层,这些东西并不坏。
多重继承是一个强大的工具,但在大多数情况下,只会有一个主父类,如果有其他父类,它们可能是Mixin类,或者无论如何都不从PrimaryParent继承的类。如果仍然需要多重继承(尽管在许多情况下使用组合来定义对象而不是继承会更有利),则不要在该类中显式地定义super,而不从PrimaryParent继承。
必须在每个类中定义super的想法对我来说不是很有吸引力,使用PrimaryParent允许super,显然是一个基于继承的别名,留在类定义行中,而不是数据应该去的类主体中。
不过可能只有我是这样。
当然,每个情况都是不同的,但在决定使用哪个选项时,请考虑我所说的这些事情。
我在许多代码库中都看到过这个习语,我很确定我甚至在Boost的库中看到过它。然而,据我所知,最常见的名称是base(或base)而不是super。
这种习惯用法在处理类模板时特别有用。作为一个例子,考虑下面的类(来自一个真实的项目):
template <typename TText, typename TSpec>
class Finder<Index<TText, PizzaChili<TSpec>>, MyFinderType>
: public Finder<Index<TText, MyFinderImpl<TSpec>>, Default>
{
using TBase = Finder<Index<TText, MyFinderImpl<TSpec>>, Default>;
// …
}
继承链使用类型参数来实现编译时多态性。不幸的是,这些模板的嵌套级别非常高。因此,完整类型名的有意义的缩写对于可读性和可维护性至关重要。