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:
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.
È 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) {}
};
_capacity
, _data
e _len
hai tipi di classe senza un costruttore predefinito accessibile?
const
membro nel corpo del costruttore, è necessario utilizzare l'elenco di inizializzazione - i non const
membri possono essere inizializzati nell'elenco di inizializzazione o nel corpo del costruttore.
Sì. Nel primo caso puoi dichiarare _capacity
, _data
e _len
come costanti:
class MyClass
{
private:
const int _capacity;
const void *_data;
const int _len;
// ...
};
Ciò sarebbe importante se si desidera garantire la const
correttezza 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())
{
}
const
le istanze devono essere inizializzate nell'elenco degli inizializzatori oppure i tipi sottostanti devono fornire costruttori pubblici senza parametri (cosa che fanno i tipi primitivi).
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)
Aggiungerò che se hai membri di tipo classe senza un costruttore predefinito disponibile, l'inizializzazione è l'unico modo per costruire la tua classe.
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.
La vera differenza sta nel modo in cui il compilatore gcc genera il codice macchina e deposita la memoria. Spiegare:
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
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 const
membri 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, è const
qualificato e ha il tipo POD o è un tipo di classe POD o array di tipo di classe POD contenente un const
membro qualificato (direttamente o indirettamente), il programma è mal formato.
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.
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.
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!