我的编码风格包括以下习语:

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”应该是私人的。


当前回答

我试图解决这个完全相同的问题;我抛出了一些想法,比如使用可变模板和包扩展来允许任意数量的父元素,但我意识到这将导致像“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,显然是一个基于继承的别名,留在类定义行中,而不是数据应该去的类主体中。

不过可能只有我是这样。

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

其他回答

我试图解决这个完全相同的问题;我抛出了一些想法,比如使用可变模板和包扩展来允许任意数量的父元素,但我意识到这将导致像“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,显然是一个基于继承的别名,留在类定义行中,而不是数据应该去的类主体中。

不过可能只有我是这样。

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

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

这是我使用的一种方法,它使用宏而不是类型定义。我知道这不是c++做事情的方式,但是当只有层次结构中最下面的基类作用于继承的偏移时,通过继承将迭代器链接在一起是很方便的。

例如:

// 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

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

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

我不记得以前见过这个,但乍一看我很喜欢。正如Ferruccio所指出的那样,它在MI面前并没有很好地发挥作用,但MI更像是一个例外,而不是规则,没有什么东西需要在任何地方都可用才能有用。

我不会说太多,除了目前的代码注释,说明super并不意味着调用base!

超级!=基本。

简而言之,“超级”到底是什么意思?那么"base"是什么意思呢?

Super表示调用方法的最后一个实现者(不是基方法) 基是指在多重继承中选择哪个类是默认基。

这两条规则适用于类的typedefs。

考虑库的实现者和库的用户,谁是超级谁是基础?

更多信息,这里是工作代码复制粘贴到您的IDE:

#include <iostream>

// Library defiens 4 classes in typical library class hierarchy
class Abstract
{
public:
    virtual void f() = 0;
};

class LibraryBase1 :
    virtual public Abstract
{
public:
    void f() override
    {
        std::cout << "Base1" << std::endl;
    }
};

class LibraryBase2 :
    virtual public Abstract
{
public:
    void f() override
    {
        std::cout << "Base2" << std::endl;
    }
};

class LibraryDerivate :
    public LibraryBase1,
    public LibraryBase2
{
    // base is meaningfull only for this class,
    // this class decides who is my base in multiple inheritance
private:
    using base = LibraryBase1;

protected:
    // this is super! base is not super but base!
    using super = LibraryDerivate;

public:
    void f() override
    {
        std::cout << "I'm super not my Base" << std::endl;
        std::cout << "Calling my *default* base: " << std::endl;
        base::f();
    }
};

// Library user
struct UserBase :
    public LibraryDerivate
{
protected:
    // NOTE: If user overrides f() he must update who is super, in one class before base!
    using super = UserBase; // this typedef is needed only so that most derived version
    // is called, which calls next super in hierarchy.
    // it's not needed here, just saying how to chain "super" calls if needed

    // NOTE: User can't call base, base is a concept private to each class, super is not.
private:
    using base = LibraryDerivate; // example of typedefing base.

};

struct UserDerived :
    public UserBase
{
    // NOTE: to typedef who is super here we would need to specify full name
    // when calling super method, but in this sample is it's not needed.

    // Good super is called, example of good super is last implementor of f()
    // example of bad super is calling base (but which base??)
    void f() override
    {
        super::f();
    }
};

int main()
{
    UserDerived derived;
    // derived calls super implementation because that's what
    // "super" is supposed to mean! super != base
    derived.f();

    // Yes it work with polymorphism!
    Abstract* pUser = new LibraryDerivate;
    pUser->f();

    Abstract* pUserBase = new UserBase;
    pUserBase->f();
}

这里还有一点很重要:

多态呼叫:向下呼叫 超级呼叫:向上呼叫

在main()内部,我们向下使用多态调用,而super向上调用,这在实际生活中并不是很有用,但它说明了两者的区别。