Menggunakan "super" di C ++

203

Gaya pengkodean saya termasuk idiom berikut:

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

      // Etc.
} ;

Ini memungkinkan saya untuk menggunakan "super" sebagai alias ke Pangkalan, misalnya, dalam konstruktor:

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

Atau bahkan ketika memanggil metode dari kelas dasar di dalam versinya yang ditimpa:

void Derived::foo()
{
   super::foo() ;

   // ... And then, do something else
}

Bahkan dapat dirantai (saya masih harus menemukan kegunaannya untuk itu):

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
}

Lagi pula, saya menemukan penggunaan "typedef super" sangat berguna, misalnya, ketika Base adalah verbose dan / atau templated.

Faktanya adalah bahwa super diimplementasikan di Jawa, serta di C # (di mana ia disebut "basis", kecuali saya salah). Tetapi C ++ tidak memiliki kata kunci ini.

Jadi, pertanyaan saya:

  • Apakah penggunaan typedef ini sangat umum / jarang / tidak pernah terlihat dalam kode yang Anda gunakan?
  • apakah ini penggunaan typedef super Ok (yaitu apakah Anda melihat alasan kuat atau tidak begitu kuat untuk tidak menggunakannya)?
  • haruskah "super" menjadi hal yang baik, haruskah itu dibakukan dalam C ++, atau apakah sudah cukup menggunakan typedef?

Sunting: Roddy menyebutkan fakta bahwa typedef harus bersifat pribadi. Ini berarti setiap kelas turunan tidak akan dapat menggunakannya tanpa mendeklarasikan ulang. Tapi saya kira itu juga akan mencegah rantai super :: super (tapi siapa yang akan menangis untuk itu?).

Sunting 2: Sekarang, beberapa bulan setelah menggunakan "super" secara besar-besaran, saya sepenuh hati setuju dengan sudut pandang Roddy: "super" harus bersifat pribadi. Saya akan mengangkat jawaban dua kali, tapi saya rasa saya tidak bisa.

paercebal
sumber
Luar biasa! Persis apa yang saya cari. Jangan pikir saya pernah perlu menggunakan teknik ini sampai sekarang. Solusi luar biasa untuk kode lintas platform saya.
AlanKley
6
Bagi saya superkelihatannya Javadan itu tidak buruk, tapi ... Tapi C++mendukung banyak warisan.
ST3
2
@ user2623967: Benar. Dalam kasus pewarisan sederhana, satu "super" sudah cukup. Sekarang, jika Anda memiliki banyak warisan, memiliki "superA", "superB", dll. Adalah solusi yang baik: Anda INGIN memanggil metode dari satu implementasi atau yang lain, jadi Anda harus KATAKAN implementasi apa yang Anda inginkan. Menggunakan "super" - seperti typedef memungkinkan Anda untuk memberikan nama yang mudah diakses / dapat ditulis (alih-alih, katakanlah, menulis di MyFirstBase<MyString, MyStruct<MyData, MyValue>>mana-mana)
paercebal
Perhatikan bahwa saat mewarisi dari templat, Anda tidak perlu menyertakan argumen templat saat merujuknya. Sebagai contoh: template <class baz> struct Foo {...void bar() {...} ...}; struct Foo2: Foo<AnnoyinglyLongListOfArguments> { void bar2() { ... Foo::bar(); ...} };Ini bekerja untuk saya dengan gcc 9.1 --std = c ++ 1y (c ++ 14).
Thanasis Papoutsidakis
1
... Um, koreksi. Tampaknya berfungsi dalam standar c ++ apa pun, bukan hanya 14.
Thanasis Papoutsidakis

Jawaban:

151

Bjarne Stroustrup menyebutkan dalam Desain dan Evolusi C ++ bahwa supersebagai kata kunci dianggap oleh komite Standar ISO C ++ pertama kali C ++ distandarisasi.

Dag Bruck mengusulkan ekstensi ini, menyebut kelas dasar "warisan." Proposal menyebutkan masalah pewarisan berganda, dan akan menandai penggunaan yang ambigu. Bahkan Stroustrup yakin.

Setelah diskusi, Dag Bruck (ya, orang yang sama yang membuat proposal) menulis bahwa proposal itu dapat diterapkan, secara teknis sehat, dan bebas dari kelemahan utama, dan menangani banyak warisan. Di sisi lain, tidak ada cukup uang untuk uang, dan komite harus menangani masalah yang lebih sulit.

Michael Tiemann datang terlambat, dan kemudian menunjukkan bahwa seorang super mengetik akan bekerja dengan baik, menggunakan teknik yang sama yang ditanyakan dalam posting ini.

Jadi, tidak, ini mungkin tidak akan pernah distandarisasi.

Jika Anda tidak memiliki salinan, Desain dan Evolusi sepadan dengan harga sampulnya. Salinan bekas bisa didapat sekitar $ 10.

Max Lybbert
sumber
5
D & E memang buku yang bagus. Tapi sepertinya saya harus membacanya lagi - saya tidak ingat cerita mana pun.
Michael Burr
2
Saya ingat tiga fitur yang tidak diterima dibahas dalam D&E. Ini adalah yang pertama (lihat "Michael Tiemann" dalam indeks untuk menemukan cerita), aturan dua minggu adalah yang kedua (lihat "aturan dua minggu" dalam indeks), dan yang ketiga bernama parameter (lihat ke atas " argumen bernama "dalam indeks).
Max Lybbert
12
Ada kelemahan utama dalam typedefteknik ini: tidak menghormati KERING. Satu-satunya cara adalah menggunakan makro jelek untuk mendeklarasikan kelas. Saat Anda mewarisi, basisnya bisa berupa kelas templat multi-parameter yang panjang, atau lebih buruk. (mis. multi-kelas) Anda harus menulis ulang semua itu untuk kedua kalinya. Akhirnya, saya melihat masalah besar dengan basis template yang memiliki argumen kelas template. Dalam hal ini super adalah templat (dan bukan instanciation templat). Yang tidak dapat diketikkan. Bahkan dalam C ++ 11 yang Anda butuhkan usinguntuk kasus ini.
v.oddou
105

Saya selalu menggunakan "warisan" daripada super. (Mungkin karena latar belakang Delphi), dan saya selalu menjadikannya pribadi , untuk menghindari masalah ketika 'warisan' secara keliru dihilangkan dari suatu kelas tetapi subkelas mencoba menggunakannya.

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

'Templat kode' standar saya untuk membuat kelas baru termasuk typedef, jadi saya memiliki sedikit peluang untuk tidak sengaja menghilangkannya.

Saya tidak berpikir saran "super :: super" dirantai adalah ide yang bagus- Jika Anda melakukan itu, Anda mungkin terikat sangat keras pada hierarki tertentu, dan mengubahnya kemungkinan akan merusak barang-barang.

Roddy
sumber
2
Adapun chaining super :: super, seperti yang saya sebutkan dalam pertanyaan, saya masih harus menemukan penggunaan yang menarik untuk itu. Untuk saat ini, saya hanya melihatnya sebagai peretasan, tetapi layak disebut, jika hanya untuk perbedaan dengan Java (di mana Anda tidak dapat membuat rantai "super").
paercebal
4
Setelah saya beberapa bulan, saya dikonversi ke sudut pandang Anda (dan saya TIDAK punya bug karena "super" yang terlupakan, seperti yang Anda sebutkan ...). Anda benar dalam jawaban Anda, termasuk rantai, saya kira. ^ _ ^ ...
paercebal
bagaimana cara menggunakan ini sekarang untuk memanggil metode kelas induk?
mLstudent33
apakah ini berarti saya harus mendeklarasikan semua metode kelas dasar virtualseperti yang ditunjukkan di sini: martinbroadhurst.com/typedef-super.html
mLstudent33
36

Satu masalah dengan ini adalah bahwa jika Anda lupa (kembali) mendefinisikan super untuk kelas turunan, maka setiap panggilan ke super :: sesuatu akan dikompilasi dengan baik tetapi mungkin tidak akan memanggil fungsi yang diinginkan.

Sebagai contoh:

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()

        ...
    }
};

(Seperti yang ditunjukkan oleh Martin York dalam komentar untuk jawaban ini, masalah ini dapat dihilangkan dengan membuat typedef bersifat pribadi daripada publik atau dilindungi.)

Kristopher Johnson
sumber
Terima kasih atas komentarnya. Efek samping ini telah luput dari perhatian saya. Meskipun mungkin tidak akan dikompilasi untuk penggunaan konstruktor, di tempat lain, saya kira, bug akan ada di sana.
paercebal
5
Namun, typedef pribadi akan mencegah penggunaan rantai, yang disebutkan dalam pos asli.
AnT
1
Berlari ke bug yang tepat inilah yang mengarahkan saya ke pertanyaan ini :(
Steve Vermeulen
jadi gunakan super1untuk Base dan super2Berasal? DerivedAgain dapat menggunakan keduanya?
mLstudent33
20

FWIW Microsoft telah menambahkan ekstensi untuk __super dalam kompiler mereka.

krujos
sumber
Beberapa pengembang di sini mulai mendorong untuk menggunakan __super. Pada awalnya saya mendorong kembali karena saya merasa itu "salah" dan "tidak standar". NAMUN, saya sudah tumbuh menyukainya.
Aardvark
8
Saya sedang mengerjakan aplikasi windows, dan menyukai ekstensi __super. Ini menyedihkan saya bahwa komite standar menolaknya demi trik typedef yang disebutkan di sini, karena meskipun trik typedef ini baik, itu membutuhkan lebih banyak pemeliharaan daripada kata kunci kompiler ketika Anda mengubah hierarki pewarisan, dan menangani banyak pewarisan dengan benar (tanpa memerlukan dua typedef seperti super1 dan super2). Singkatnya, saya setuju dengan komentator lain bahwa ekstensi MS SANGAT berguna, dan siapa pun yang menggunakan Visual Studio secara eksklusif harus sangat mempertimbangkan menggunakannya.
Brian
15

Super (atau diwariskan) adalah Very Good Thing karena jika Anda perlu menempelkan lapisan pewarisan lain di antara Base dan Derived, Anda hanya perlu mengubah dua hal: 1. "class Base: foo" dan 2. the typedef

Jika saya ingat dengan benar, komite Standar C ++ sedang mempertimbangkan untuk menambahkan kata kunci untuk ini ... sampai Michael Tiemann menunjukkan bahwa trik typedef ini berfungsi.

Adapun warisan ganda, karena itu di bawah kendali programmer Anda dapat melakukan apa pun yang Anda inginkan: mungkin super1 dan super2, atau apa pun.

Colin Jensen
sumber
13

Saya baru saja menemukan solusi alternatif. Saya memiliki masalah besar dengan pendekatan typedef yang menggigit saya hari ini:

  • Typedef membutuhkan salinan yang tepat dari nama kelas. Jika seseorang mengubah nama kelas tetapi tidak mengubah typedef maka Anda akan mengalami masalah.

Jadi saya datang dengan solusi yang lebih baik menggunakan templat yang sangat sederhana.

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

Jadi sekarang, bukannya

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

kamu punya

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

Dalam hal ini, BaseAliastidak bersifat pribadi dan saya sudah mencoba untuk menjaga terhadap penggunaan yang ceroboh dengan memilih nama jenis yang harus mengingatkan pengembang lain.

paperjam
sumber
4
publicalias adalah kelemahan, karena Anda terbuka terhadap bug yang disebutkan dalam jawaban Roddy dan Kristopher (mis. Anda dapat (secara tidak sengaja) berasal dari Derivedalih-alih MakeAlias<Derived>)
Alexander Malakhov
3
Anda juga tidak memiliki akses ke konstruktor kelas dasar dalam daftar penginisialisasi Anda. (Ini dapat dikompensasi dalam C ++ 11 menggunakan konstruktor yang mewarisi dalam MakeAliasatau penerusan yang sempurna, tetapi itu mengharuskan Anda untuk merujuk ke MakeAlias<BaseAlias>konstruktor daripada hanya merujuk ke Ckonstruktor secara langsung.)
Adam H. Peterson
12

Saya tidak ingat pernah melihat ini sebelumnya, tetapi pada pandangan pertama saya menyukainya. Seperti yang dicatat Ferruccio , itu tidak bekerja dengan baik di hadapan MI, tetapi MI lebih merupakan pengecualian daripada aturan dan tidak ada yang mengatakan bahwa sesuatu harus digunakan di mana saja agar bermanfaat.

Michael Burr
sumber
6
Suara positif hanya untuk frasa, "tidak ada yang mengatakan bahwa sesuatu harus dapat digunakan di mana saja untuk menjadi berguna."
Tanktalus
1
Sepakat. Ini bermanfaat. Saya hanya melihat di perpustakaan tipe dorongan untuk melihat apakah ada cara untuk menghasilkan typedef melalui templat. Sayangnya, sepertinya Anda tidak bisa.
Ferruccio
9

Saya telah melihat idiom ini digunakan dalam banyak kode dan saya cukup yakin saya bahkan pernah melihatnya di suatu tempat di perpustakaan Boost. Namun, sejauh yang saya ingat nama yang paling umum adalah base(atau Base) bukansuper .

Ungkapan ini sangat berguna jika bekerja dengan kelas template. Sebagai contoh, pertimbangkan kelas berikut (dari proyek nyata ):

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;
    // …
}

Jangan pedulikan nama yang lucu. Poin penting di sini adalah bahwa rantai pewarisan menggunakan argumen tipe untuk mencapai polimorfisme waktu kompilasi. Sayangnya, level bersarang dari template ini menjadi cukup tinggi. Oleh karena itu, singkatan sangat penting untuk keterbacaan dan pemeliharaan.

Konrad Rudolph
sumber
Saya telah menggunakan "super" belakangan ini untuk alasan yang sama. Warisan publik terlalu menyakitkan untuk ditangani jika tidak ... :-) ...
paercebal
4

Saya sudah cukup sering melihatnya menggunakannya, kadang-kadang sebagai super_t, ketika basis adalah tipe template yang kompleks ( boost::iterator_adaptorapakah ini, misalnya)

James Hopkin
sumber
1
Kamu benar. Saya menemukan referensi di sini. boost.org/doc/libs/1_36_0/libs/iterator/doc/…
paercebal
4

Apakah penggunaan typedef ini sangat umum / jarang / tidak pernah terlihat dalam kode yang Anda gunakan?

Saya belum pernah melihat pola khusus ini dalam kode C ++ saya bekerja dengan, tetapi itu tidak berarti itu tidak ada di luar sana.

apakah ini penggunaan typedef super Ok (yaitu apakah Anda melihat alasan kuat atau tidak begitu kuat untuk tidak menggunakannya)?

Itu tidak memungkinkan pewarisan berganda (bersih, pokoknya).

haruskah "super" menjadi hal yang baik, haruskah itu dibakukan dalam C ++, atau apakah sudah cukup menggunakan typedef?

Untuk alasan yang dikutip di atas (multiple inheritance), no. Alasan mengapa Anda melihat "super" dalam bahasa lain yang Anda cantumkan adalah bahwa mereka hanya mendukung satu warisan, sehingga tidak ada kebingungan mengenai apa yang dimaksud "super". Memang, dalam bahasa-bahasa itu IS berguna tetapi tidak benar-benar memiliki tempat dalam model data C ++.

Oh, dan FYI: C ++ / CLI mendukung konsep ini dalam bentuk kata kunci "__super". Harap dicatat, bahwa C ++ / CLI juga tidak mendukung banyak pewarisan.

Toji
sumber
4
Sebagai titik balik, Perl memiliki banyak pewarisan dan SUPER. Ada beberapa kebingungan, tetapi algoritma yang dilalui VM untuk menemukan sesuatu didokumentasikan dengan jelas. Yang mengatakan, saya jarang melihat MI digunakan di mana beberapa kelas dasar menawarkan metode yang sama di mana kebingungan bisa muncul.
Tanktalus
1
Microsoft Visual Studio mengimplementasikan kata kunci __super untuk C ++ apakah itu untuk CLI atau tidak. Jika hanya satu dari kelas warisan yang menawarkan metode tanda tangan yang benar (kasus yang paling umum, seperti yang disebutkan oleh Tanktalus) maka hanya memilih satu-satunya pilihan yang valid. Jika dua atau lebih kelas yang diwariskan menyediakan kecocokan fungsi, itu tidak berfungsi dan mengharuskan Anda untuk eksplisit.
Brian
3

Salah satu alasan tambahan untuk menggunakan typedef untuk superclass adalah ketika Anda menggunakan templat kompleks dalam warisan objek.

Misalnya:

template <typename T, size_t C, typename U>
class A
{ ... };

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

Di kelas B itu akan ideal untuk memiliki typedef untuk A kalau tidak, Anda akan terjebak mengulanginya di mana pun Anda ingin referensi anggota A.

Dalam kasus ini ia dapat bekerja dengan banyak pewarisan juga, tetapi Anda tidak akan memiliki typedef bernama 'super', itu akan disebut 'base_A_t' atau sesuatu seperti itu.

--jeffk ++

jdkoftinoff
sumber
2

Setelah bermigrasi dari Turbo Pascal ke C ++ kembali pada hari itu, saya biasa melakukan ini untuk memiliki setara dengan kata kunci "turunan" Turbo Pascal, yang bekerja dengan cara yang sama. Namun, setelah pemrograman dalam C ++ selama beberapa tahun saya berhenti melakukannya. Saya menemukan saya hanya tidak membutuhkannya.

Greg Hewgill
sumber
1

Saya tidak tahu apakah ini langka atau tidak, tetapi saya sudah melakukan hal yang sama.

Seperti yang telah ditunjukkan, kesulitan untuk membuat bagian dari bahasa itu sendiri adalah ketika sebuah kelas memanfaatkan pewarisan berganda.

Matt Dillard
sumber
1

Saya menggunakan ini dari waktu ke waktu. Tepat ketika saya menemukan diri saya mengetik tipe kelas dasar beberapa kali, saya akan menggantinya dengan typedef yang serupa dengan milik Anda.

Saya pikir ini bisa bermanfaat. Seperti yang Anda katakan, jika kelas dasar Anda adalah templat, ia dapat menyimpan pengetikan. Juga, kelas templat dapat mengambil argumen yang bertindak sebagai kebijakan untuk bagaimana templat seharusnya bekerja. Anda bebas mengubah jenis basis tanpa harus memperbaiki semua referensi Anda selama antarmuka basis tetap kompatibel.

Saya pikir penggunaan melalui typedef sudah cukup. Saya tidak bisa melihat bagaimana itu akan dibangun ke dalam bahasa itu karena multiple inheritence berarti ada banyak kelas dasar, jadi Anda bisa mengetiknya sesuai keinginan Anda untuk kelas yang menurut Anda logis adalah kelas dasar yang paling penting.

Scott Langham
sumber
1

Saya mencoba memecahkan masalah yang sama persis ini; Saya melemparkan beberapa ide, seperti menggunakan templat variadic dan ekspansi paket untuk memungkinkan jumlah orang tua yang sewenang-wenang, tetapi saya menyadari bahwa akan menghasilkan implementasi seperti 'super0' dan 'super1'. Saya membuangnya karena itu akan lebih bermanfaat daripada tidak memulainya.

Solusi saya melibatkan kelas pembantu PrimaryParentdan diimplementasikan seperti itu:

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

Kemudian kelas mana yang ingin Anda gunakan akan dinyatakan seperti itu:

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

Untuk menghindari perlunya menggunakan pewarisan virtual PrimaryParentpada BaseClass, konstruktor yang mengambil sejumlah variabel argumen digunakan untuk memungkinkan pembangunan BaseClass.

Alasan di balik publicwarisan BaseClasske PrimaryParentadalah untuk MyObjectmemiliki kontrol penuh atas warisanBaseClass meskipun memiliki kelas penolong di antara mereka.

Ini berarti bahwa setiap kelas yang Anda inginkan superharus menggunakan PrimaryParentkelas helper, dan setiap anak hanya dapat mewarisi dari satu kelas menggunakan PrimaryParent(karenanya namanya).

Batasan lain untuk metode ini, adalah MyObjectdapat mewarisi hanya satu kelas yang mewarisi dari PrimaryParent, dan bahwa seseorang harus diwarisi menggunakan PrimaryParent. Inilah yang saya maksud:

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>{}

Sebelum Anda membuang ini sebagai opsi karena jumlah pembatasan yang tampaknya dan fakta ada kelas perantara antara setiap warisan, hal-hal ini tidak buruk.

Warisan berganda adalah alat yang kuat, tetapi dalam sebagian besar keadaan, hanya akan ada satu orang tua utama, dan jika ada orang tua lain, kemungkinan besar mereka adalah kelas Mixin, atau kelas yang tidak mewarisi dari mana pun PrimaryParent. Jika pewarisan berganda masih diperlukan (meskipun banyak situasi akan menguntungkan untuk menggunakan komposisi untuk mendefinisikan objek daripada pewarisan), daripada hanya secara eksplisit mendefinisikan superdi kelas itu dan tidak mewarisi dari PrimaryParent.

Gagasan harus mendefinisikan superdi setiap kelas tidak terlalu menarik bagi saya, menggunakan PrimaryParentmemungkinkan super, jelas alias berbasis warisan, untuk tetap berada di garis definisi kelas, bukan badan kelas di mana data harus pergi.

Mungkin itu hanya aku.

Tentu saja setiap situasi berbeda, tetapi pertimbangkan hal-hal ini yang saya katakan ketika memutuskan opsi mana yang akan digunakan.

Kevin
sumber
1

Saya tidak akan mengatakan banyak kecuali kode sekarang dengan komentar yang menunjukkan bahwa super tidak berarti memanggil basis!

super != base.

Singkatnya, apa yang dimaksud dengan "super"? lalu apa artinya "basis"?

  1. Super means, memanggil implementor terakhir dari suatu metode (bukan metode dasar)
  2. basis artinya, memilih kelas mana yang merupakan basis default dalam banyak pewarisan.

2 aturan ini berlaku untuk typedef di kelas.

Pertimbangkan implementor perpustakaan dan pengguna perpustakaan, siapa yang super dan siapa yang basis?

untuk info lebih lanjut di sini adalah kode kerja untuk copy paste ke IDE Anda:

#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();
}

Poin penting lain di sini adalah ini:

  1. panggilan polimorfik: panggilan ke bawah
  2. panggilan super: panggilan ke atas

di dalam main()kita menggunakan down panggilan polimorfik yang super panggilan ke atas, tidak benar-benar berguna dalam kehidupan nyata, tetapi menunjukkan perbedaannya.

metablaster
sumber
0

Ini adalah metode yang saya gunakan yang menggunakan makro, bukan typedef. Saya tahu bahwa ini bukan cara C ++ dalam melakukan sesuatu tetapi bisa lebih nyaman ketika merantai iterator bersama-sama melalui warisan ketika hanya kelas dasar terjauh dalam hirarki yang bertindak berdasarkan offset yang diwarisi.

Sebagai contoh:

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

Setup generik yang saya gunakan membuatnya sangat mudah untuk membaca dan menyalin / menempel di antara pohon warisan yang memiliki kode duplikat tetapi harus diganti karena jenis kembali harus sesuai dengan kelas saat ini.

Orang dapat menggunakan huruf kecil superuntuk mereplikasi perilaku yang terlihat di Jawa tetapi gaya pengkodean saya adalah dengan menggunakan semua huruf besar untuk makro.

Zhro
sumber