Domanda

Quali sono le regole C ++ per chiamare il costruttore della superclasse da una sottoclasse?

Ad esempio, lo so in Java, devi farlo come prima riga del costruttore della sottoclasse (e, in caso contrario, si presume una chiamata implicita a un super costruttore no-arg, dandoti un errore di compilazione se che manca).

È stato utile?

Soluzione

I costruttori della classe base vengono chiamati automaticamente per te se non hanno argomenti. Se si desidera chiamare un costruttore di superclasse con un argomento, è necessario utilizzare l'elenco di inizializzazione del costruttore della sottoclasse. A differenza di Java, C ++ supporta l'ereditarietà multipla (nel bene o nel male), quindi la classe base deve essere chiamata per nome, piuttosto che "quot (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
        }
};

Ulteriori informazioni sulla lista di inizializzazione del costruttore qui e qui .

Altri suggerimenti

In C ++, i costruttori senza argomento per tutte le superclassi e le variabili membro sono chiamati per te, prima di entrare nel tuo costruttore. Se vuoi passare loro argomenti, c'è una sintassi separata per questo chiamato "concatenatore di costruttori", che assomiglia a questo:

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

Se qualcosa viene eseguito a questo punto, le basi / membri che avevano precedentemente completato la costruzione vengono chiamati i loro distruttori e l'eccezione viene riproposta al chiamante. Se si desidera rilevare eccezioni durante il concatenamento, è necessario utilizzare una funzione try block:

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

In questo modulo, nota che il blocco try è il corpo della funzione, piuttosto che essere all'interno del corpo della funzione; ciò gli consente di rilevare le eccezioni generate dalle inizializzazioni implicite o esplicite dei membri e della classe base, nonché durante il corpo della funzione. Tuttavia, se un blocco catch di funzioni non genera un'eccezione diversa, il runtime riproverà l'errore originale; le eccezioni durante l'inizializzazione non possono essere ignorate.

In C ++ esiste un concetto di elenco di inizializzazione del costruttore, che è dove puoi e dovresti chiamare il costruttore della classe base e dove dovresti anche inizializzare i membri dei dati. L'elenco di inizializzazione viene dopo la firma del costruttore che segue i due punti e prima del corpo del costruttore. Diciamo che abbiamo una classe A:


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

Quindi, supponendo che B abbia un costruttore che accetta un int, il costruttore di A potrebbe apparire così:


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

Come puoi vedere, il costruttore della classe base viene chiamato nell'elenco di inizializzazione. L'inizializzazione dei membri dei dati nell'elenco di inizializzazione, comunque, è preferibile all'assegnazione dei valori per b_ e c_ all'interno del corpo del costruttore, poiché si sta risparmiando il costo aggiuntivo dell'assegnazione.

Tieni presente che i membri dei dati vengono sempre inizializzati nell'ordine in cui sono dichiarati nella definizione della classe, indipendentemente dal loro ordine nell'elenco di inizializzazione. Per evitare strani bug, che possono sorgere se i membri dei dati dipendono l'uno dall'altro, è necessario assicurarsi sempre che l'ordine dei membri sia lo stesso nell'elenco di inizializzazione e nella definizione della classe. Per lo stesso motivo, il costruttore della classe base deve essere il primo elemento nell'elenco di inizializzazione. Se lo ometti del tutto, il costruttore predefinito per la classe base verrà chiamato automaticamente. In tal caso, se la classe base non ha un costruttore predefinito, verrà visualizzato un errore del compilatore.

Tutti hanno citato una chiamata del costruttore attraverso un elenco di inizializzazione, ma nessuno ha detto che il costruttore di una classe genitore può essere chiamato esplicitamente dal corpo del costruttore del membro derivato. Vedi la domanda Chiamare un costruttore della classe base dal corpo del costruttore di una sottoclasse , ad esempio. Il punto è che se si utilizza una chiamata esplicita a una classe genitore o a un costruttore di superclasse nel corpo di una classe derivata, questo in realtà sta semplicemente creando un'istanza della classe genitore e non sta invocando il costruttore di classe genitore sull'oggetto derivato . L'unico modo per invocare una classe genitore o un costruttore di superclasse sull'oggetto di una classe derivata è tramite l'elenco di inizializzazione e non nel corpo del costruttore di classi derivate. Quindi forse non dovrebbe essere chiamato un "superclass call del costruttore". Ho inserito questa risposta perché qualcuno potrebbe confondersi (come ho fatto io).

L'unico modo per passare valori a un costruttore principale è tramite un elenco di inizializzazione. L'elenco di inizializzazione viene implementato con un: e quindi un elenco di classi e i valori da passare al costruttore di tali classi.

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

Ricorda inoltre che se hai un costruttore che non accetta parametri sulla classe genitore, verrà chiamato automaticamente prima dell'esecuzione del costruttore figlio.

Se hai un costruttore senza argomenti, verrà chiamato prima che il costruttore della classe derivata venga eseguito.

Se vuoi chiamare un costruttore di base con argomenti devi scriverlo esplicitamente nel costruttore derivato in questo modo:

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

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

Non puoi costruire una classe derivata senza chiamare il costruttore parent in C ++. Ciò accade automaticamente se si tratta di un C'tor non-arg, succede se si chiama il costruttore derivato direttamente come mostrato sopra o il codice non viene compilato.

Se nel costruttore di base sono presenti parametri predefiniti, la classe base verrà chiamata automaticamente.

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

L'output è: 1

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

Nessuno ha menzionato la sequenza delle chiamate del costruttore quando una classe deriva da più classi. La sequenza è come menzionata mentre deriva le classi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top