Wat zijn de regels voor het aanroepen van de constructor van de basisklasse?

Wat zijn de C++-regels voor het aanroepen van de constructor van de basisklasse vanuit een afgeleide klasse?

Bijvoorbeeld, ik weet dat in Java, je het moet doen als de eerste regel van de subklasse-constructor (en als je dat niet doet, wordt een impliciete aanroep naar een no-arg superconstructor verondersteld – waardoor je een compileerfout krijgt als dat ontbreekt).


Antwoord 1, autoriteit 100%

Configuratoren van basisklassen worden automatisch voor u aangeroepen als ze geen argument hebben. Als u een superklasse-constructor met een argument wilt aanroepen, moet u de constructor-initialisatielijst van de subklasse gebruiken. In tegenstelling tot Java ondersteunt C++ meervoudige overerving (voor beter of slechter), dus de basisklasse moet bij naam worden genoemd in plaats van “super()”.

class SuperClass
{
    public:
        SuperClass(int foo)
        {
            // do something with foo
        }
};
class SubClass : public SuperClass
{
    public:
        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

Meer informatie over de initialisatielijst van de constructor hieren hier.


Antwoord 2, autoriteit 25%

In C++ worden de no-argument constructors voor alle superklassen en lidvariabelen voor je aangeroepen, voordat je je constructor invoert. Als je ze argumenten wilt doorgeven, is er een aparte syntaxis hiervoor genaamd “constructor chaining”, die er als volgt uitziet:

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

Als er op dit punt iets wordt gegooid, worden de bases/leden die eerder de constructie hadden voltooid, hun destructors genoemd en wordt de uitzondering teruggeworpen naar de beller. Als u uitzonderingen wilt opvangen tijdens het koppelen, moet u een functie-try-blok gebruiken:

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

Houd er in deze vorm rekening mee dat het try-blok isde hoofdtekst van de functie, in plaats van zich in de hoofdtekst van de functie te bevinden; hierdoor kan het uitzonderingen opvangen die worden veroorzaakt door impliciete of expliciete initialisaties van leden en basisklassen, evenals tijdens de hoofdtekst van de functie. Als een functie-catch-blok echter geen andere uitzondering genereert, zal de runtime de oorspronkelijke fout opnieuw genereren; uitzonderingen tijdens initialisatie kunnen nietworden genegeerd.


Antwoord 3, autoriteit 6%

In C++ is er een concept van de initialisatielijst van de constructor, waar je de constructor van de basisklasse kunt en moet aanroepen en waar je ook de gegevensleden moet initialiseren. De initialisatielijst komt na de handtekening van de constructor na een dubbele punt en vóór de hoofdtekst van de constructor. Laten we zeggen dat we een klasse A hebben:


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

In de veronderstelling dat B een constructor heeft die een int krijgt, kan de constructor van A er als volgt uitzien:


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

Zoals je kunt zien, wordt de constructor van de basisklasse aangeroepen in de initialisatielijst. Het initialiseren van de gegevensleden in de initialisatielijst verdient trouwens de voorkeur boven het toewijzen van de waarden voor b_ en c_ in de hoofdtekst van de constructor, omdat u de extra toewijzingskosten bespaart.

Houd er rekening mee dat gegevensleden altijd worden geïnitialiseerd in de volgorde waarin ze zijn gedeclareerd in de klassedefinitie, ongeacht hun volgorde in de initialisatielijst. Om vreemde fouten te voorkomen, die kunnen optreden als uw gegevensleden van elkaar afhankelijk zijn, moet u er altijd voor zorgen dat de volgorde van de leden hetzelfde is in de initialisatielijst en de klassedefinitie. Om dezelfde reden moet de constructor van de basisklasse het eerste item in de initialisatielijst zijn. Als u het helemaal weglaat, wordt de standaardconstructor voor de basisklasse automatisch aangeroepen. In dat geval, als de basisklasse geen standaardconstructor heeft, krijgt u een compilerfout.


Antwoord 4, autoriteit 2%

Iedereen noemde een constructor-aanroep via een initialisatielijst, maar niemand zei dat de constructor van een bovenliggende klasse expliciet kan worden aangeroepen vanuit de body van de constructor van het afgeleide lid. Zie de vraag Bijvoorbeeld een constructor van de basisklasse aanroepen vanuit de constructorbody van een subklasse.
Het punt is dat als je een expliciete aanroep van een bovenliggende klasse of superklasseconstructor in de hoofdtekst van een afgeleide klasse gebruikt, dit eigenlijk gewoon een instantie van de bovenliggende klasse maakt en niet de bovenliggende klasseconstructor aanroept op het afgeleide object . De enige manier om een bovenliggende klasse of superklasse-constructor aan te roepen op het object van een afgeleide klasse, is via de initialisatielijst en niet in de hoofdtekst van de afgeleide klasse-constructor. Dus misschien moet het geen “superklasse-constructoraanroep” worden genoemd. Ik plaats dit antwoord hier omdat iemand in de war kan raken (zoals ik deed).


Antwoord 5, autoriteit 2%

De enige manier om waarden door te geven aan een bovenliggende constructor is via een initialisatielijst. De initialisatielijst wordt geïmplementeerd met een : en vervolgens een lijst met klassen en de waarden die aan die klassenconstructor moeten worden doorgegeven.

Class2::Class2(string id) : Class1(id) {
....
}

Onthoud ook dat als je een constructor hebt die geen parameters voor de bovenliggende klasse gebruikt, deze automatisch wordt aangeroepen voordat de onderliggende constructor wordt uitgevoerd.


Antwoord 6, autoriteit 2%

Als je een constructor zonder argumenten hebt, wordt deze aangeroepen voordat de afgeleide klassenconstructor wordt uitgevoerd.

Als je een base-constructor met argumenten wilt aanroepen, moet je dat expliciet als volgt in de afgeleide constructor schrijven:

class base
{
  public:
  base (int arg)
  {
  }
};
class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

Je kunt geen afgeleide klasse maken zonder de constructor van de ouders in C++ aan te roepen. Dat gebeurt ofwel automatisch als het een niet-arg C’tor is, het gebeurt als je de afgeleide constructor rechtstreeks aanroept zoals hierboven weergegeven, of je code wordt niet gecompileerd.


Antwoord 7

Als je standaard parameters in je basisconstructor hebt, wordt de basisklasse automatisch aangeroepen.

using namespace std;
class Base
{
    public:
    Base(int a=1) : _a(a) {}
    protected:
    int _a;
};
class Derived : public Base
{
  public:
  Derived() {}
  void printit() { cout << _a << endl; }
};
int main()
{
   Derived d;
   d.printit();
   return 0;
}

Uitvoer is: 1


Antwoord 8

CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }

Antwoord 9

Niemand noemde de volgorde van constructor-aanroepen wanneer een klasse voortkomt uit meerdere klassen. De volgorde is zoals vermeld bij het afleiden van de klassen.


Antwoord 10

Als u gewoon alle constructorargumenten wilt doorgeven aan de basisklasse(=ouder), is hier een minimaal voorbeeld.

Dit gebruikt sjablonen om elke constructoraanroep met 1, 2 of 3 argumenten door te sturen naar de bovenliggende klasse std::string.

Code

Live-versie

#include <iostream>
#include <string>
class ChildString: public std::string
{
    public:
        template<typename... Args>
        ChildString(Args... args): std::string(args...)
        {
            std::cout 
                << "\tConstructor call ChildString(nArgs="
                << sizeof...(Args) << "): " << *this
                << std::endl;
        }
};
int main()
{
    std::cout << "Check out:" << std::endl;
    std::cout << "\thttp://www.cplusplus.com/reference/string/string/string/" << std::endl;
    std::cout << "for available string constructors" << std::endl;
    std::cout << std::endl;
    std::cout << "Initialization:" << std::endl;
    ChildString cs1 ("copy (2)");
    char char_arr[] = "from c-string (4)";
    ChildString cs2 (char_arr);
    std::string str = "substring (3)";
    ChildString cs3 (str, 0, str.length());
    std::cout << std::endl;
    std::cout << "Usage:" << std::endl;
    std::cout << "\tcs1: " << cs1 << std::endl;
    std::cout << "\tcs2: " << cs2 << std::endl;
    std::cout << "\tcs3: " << cs3 << std::endl;
    return 0;
}

Uitvoer

Check out:
    http://www.cplusplus.com/reference/string/string/string/
for available string constructors
Initialization:
    Constructor call ChildString(nArgs=1): copy (2)
    Constructor call ChildString(nArgs=1): from c-string (4)
    Constructor call ChildString(nArgs=3): substring (3)
Usage:
    cs1: copy (2)
    cs2: from c-string (4)
    cs3: substring (3)

Update: Variadic-sjablonen gebruiken

Veralgemenen naar n argumenten en vereenvoudigen

       template <class C>
        ChildString(C arg): std::string(arg)
        {
            std::cout << "\tConstructor call ChildString(C arg): " << *this << std::endl;
        }
        template <class C1, class C2>
        ChildString(C1 arg1, C2 arg2): std::string(arg1, arg2)
        {
            std::cout << "\tConstructor call ChildString(C1 arg1, C2 arg2, C3 arg3): " << *this << std::endl;
        }
        template <class C1, class C2, class C3>
        ChildString(C1 arg1, C2 arg2, C3 arg3): std::string(arg1, arg2, arg3)
        {
            std::cout << "\tConstructor call ChildString(C1 arg1, C2 arg2, C3 arg3): " << *this << std::endl;
        }

naar

template<typename... Args>
        ChildString(Args... args): std::string(args...)
        {
            std::cout 
                << "\tConstructor call ChildString(nArgs="
                << sizeof...(Args) << "): " << *this
                << std::endl;
        }

Other episodes