In questo caso specifico, c'è una differenza tra l'utilizzo di un elenco di inizializzatori di membri e l'assegnazione di valori in un costruttore?


90

Internamente e riguardo al codice generato, c'è davvero una differenza tra:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

e

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

Grazie...

Risposte:


60

Supponendo che quei valori siano tipi primitivi, allora no, non c'è differenza. Gli elenchi di inizializzazione fanno la differenza solo quando si hanno oggetti come membri, poiché invece di utilizzare l'inizializzazione predefinita seguita dall'assegnazione, l'elenco di inizializzazione consente di inizializzare l'oggetto al suo valore finale. Questo può effettivamente essere notevolmente più veloce.


17
come ha detto Richard, fa la differenza se quei valori sono tipi primitivi e const, gli elenchi di inizializzazione sono l'unico modo per assegnare valori ai membri const.
thbusch

11
Funziona solo come descritto quando le variabili non sono riferimenti o costanti, se sono costanti o riferimenti non verrà nemmeno compilato senza utilizzare l'elenco di inizializzazione.
stefanB

77

È necessario utilizzare l'elenco di inizializzazione per inizializzare i membri costanti, i riferimenti e la classe di base

Quando è necessario inizializzare membri costanti, riferimenti e passare parametri ai costruttori di classi di base, come indicato nei commenti, è necessario utilizzare l'elenco di inizializzazione.

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

La classe / struttura di esempio (non esaustiva) contiene riferimenti:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

Ed esempio di inizializzazione di una classe base che richiede un parametro (ad es. Nessun costruttore predefinito):

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

4
E se _capacity, _datae _lenhai tipi di classe senza un costruttore predefinito accessibile?
CB Bailey

2
Chiami quali costruttori sono disponibili, se hai bisogno di impostare più valori allora li chiami dal corpo del tuo costruttore. La differenza qui è che non è possibile inizializzare il constmembro nel corpo del costruttore, è necessario utilizzare l'elenco di inizializzazione - i non constmembri possono essere inizializzati nell'elenco di inizializzazione o nel corpo del costruttore.
stefanB

@stefan: non puoi chiamare i costruttori. Una classe senza un costruttore predefinito deve essere inizializzata nell'elenco degli inizializzatori, proprio come i membri const.
GManNickG

2
è inoltre necessario inizializzare i riferimenti nell'elenco di inizializzazione
Andriy Tylychko

2
@stefanB: Mi scuso se ho insinuato che non lo capisci quando lo fai effettivamente. Nella tua risposta hai solo affermato quando una base o un membro deve essere nominato nella lista degli inizializzatori, non hai spiegato quale sia la differenza concettuale tra l'inizializzazione nella lista degli inizializzatori e l'assegnazione nel corpo del costruttore è in realtà che è dove il mio malinteso potrebbe provenire da.
CB Bailey

18

Sì. Nel primo caso puoi dichiarare _capacity, _datae _lencome costanti:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

Ciò sarebbe importante se si desidera garantire la constcorrettezza di queste variabili di istanza durante il calcolo dei loro valori in fase di esecuzione, ad esempio:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

constle istanze devono essere inizializzate nell'elenco degli inizializzatori oppure i tipi sottostanti devono fornire costruttori pubblici senza parametri (cosa che fanno i tipi primitivi).


2
Lo stesso vale per i riferimenti. Se la tua classe ha membri di riferimento, devono essere inizializzati nell'elenco degli inizializzatori.
Mark Ransom

7

Penso che questo link http://www.cplusplus.com/forum/articles/17820/ offra un'ottima spiegazione, specialmente per chi è nuovo al C ++.

Il motivo per cui le liste di inizializzazione sono più efficienti è che all'interno del corpo del costruttore avvengono solo gli incarichi, non l'inizializzazione. Quindi, se hai a che fare con un tipo non incorporato, il costruttore predefinito per quell'oggetto è già stato chiamato prima che il corpo del costruttore sia stato inserito. All'interno del corpo del costruttore, stai assegnando un valore a quell'oggetto.

In effetti, questa è una chiamata al costruttore predefinito seguita da una chiamata all'operatore di assegnazione della copia. L'elenco degli inizializzatori ti consente di chiamare direttamente il costruttore della copia, e questo a volte può essere significativamente più veloce (ricorda che l'elenco degli inizializzatori è prima del corpo del costruttore)


5

Aggiungerò che se hai membri di tipo classe senza un costruttore predefinito disponibile, l'inizializzazione è l'unico modo per costruire la tua classe.


3

Una grande differenza è che l'assegnazione può inizializzare i membri di una classe genitore; l'inizializzatore funziona solo sui membri dichiarati nell'ambito della classe corrente.


2

Dipende dai tipi coinvolti. La differenza è simile tra

std::string a;
a = "hai";

e

std::string a("hai");

dove la seconda forma è la lista di inizializzazione, ovvero fa la differenza se il tipo richiede argomenti del costruttore o è più efficiente con gli argomenti del costruttore.


1

La vera differenza sta nel modo in cui il compilatore gcc genera il codice macchina e deposita la memoria. Spiegare:

  • (fase1) Prima del corpo dell'init (incluso l'elenco di inizializzazione): il compilatore alloca la memoria richiesta per la classe. La classe è già viva!
  • (fase2) Nel corpo dell'init: poiché la memoria è allocata, ogni assegnazione ora indica un'operazione sulla variabile già in uscita / 'inizializzata'.

Esistono certamente altri modi per gestire i membri di tipo const. Ma per facilitare la loro vita, gli autori del compilatore gcc decidono di impostare alcune regole

  1. i membri del tipo const devono essere inizializzati prima del corpo dell'init.
  2. Dopo la fase1, qualsiasi operazione di scrittura è valida solo per i membri non costanti.

1

Esiste un solo modo per inizializzare le istanze della classe base e le variabili membro non statiche e consiste nell'usare l'elenco degli inizializzatori.

Se non specifichi una variabile membro di base o non statica nell'elenco di inizializzatori del tuo costruttore, quel membro o base verrà inizializzato per impostazione predefinita (se il membro / base è un tipo di classe non POD o un array di classe non POD tipi) o altrimenti non inizializzati.

Una volta immesso il corpo del costruttore, tutte le basi o tutti i membri saranno stati inizializzati o lasciati non inizializzati (cioè avranno un valore indeterminato). Non vi è alcuna possibilità nel corpo del costruttore di influenzare il modo in cui dovrebbero essere inizializzati.

Potrebbe essere possibile assegnare nuovi valori ai membri nel corpo del costruttore, ma non è possibile assegnare a constmembri o membri di tipo classe che sono stati resi non assegnabili e non è possibile riassociare i riferimenti.

Per i tipi incorporati e alcuni tipi definiti dall'utente, l'assegnazione nel corpo del costruttore può avere esattamente lo stesso effetto dell'inizializzazione con lo stesso valore nell'elenco degli inizializzatori.

Se non si riesce a nominare un membro o una base in un elenco di inizializzatori e tale entità è un riferimento, ha un tipo di classe senza un costruttore predefinito dichiarato dall'utente accessibile, è constqualificato e ha il tipo POD o è un tipo di classe POD o array di tipo di classe POD contenente un constmembro qualificato (direttamente o indirettamente), il programma è mal formato.


0

Se scrivi un elenco di inizializzatori, fai tutto in un unico passaggio; se non scrivi un elenco di inizializzatori, eseguirai 2 passaggi: uno per la dichiarazione e uno per assegnare il valore.


0

C'è una differenza tra la lista di inizializzazione e l'istruzione di inizializzazione in un costruttore. Consideriamo il codice seguente:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

Quando si utilizza MyClass, tutti i membri verranno inizializzati prima dell'esecuzione della prima istruzione in un costruttore.

Tuttavia, quando si utilizza MyClass2, tutti i membri non vengono inizializzati quando viene eseguita la prima istruzione in un costruttore.

In un secondo momento, potrebbe esserci un problema di regressione quando qualcuno aggiunge del codice in un costruttore prima che un determinato membro venga inizializzato.


0

Ecco un punto che non ho visto altri riferirsi ad esso:

class temp{
public:
   temp(int var);
};

La classe temporanea non ha un ctor predefinito. Quando lo usiamo in un'altra classe come segue:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

il codice non verrà compilato, perché il compilatore non sa come inizializzare obj, perché ha solo un ctor esplicito che riceve un valore int, quindi dobbiamo cambiare il ctor come segue:

mainClass(int sth):obj(sth){}

Quindi, non si tratta solo di const e riferimenti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.