le mie informazioni di contatto
Posta[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Nel capitolo precedente abbiamo accennato brevemente ad alcuni concetti di classi e oggetti. Ora passiamoli in rassegna insieme.
class è la parola chiave della classe di definizione, seguita dal nome della classe di definizione, {} è il corpo principale della classe, seguito da ";", e le funzioni membro definite nella classe vengono espanse per impostazione predefinita (inline)
Un formato di incapsulamento, diviso in tre tipi: pubblico, privato e protetto. L'ambito delle autorizzazioni di accesso inizia dalla posizione in cui appare il qualificatore di accesso fino a quando appare il qualificatore di accesso successivo. Se non è presente alcun qualificatore di accesso successivo, utilizzerà il dominio termina con } cioè class.
La classe definisce un nuovo ambito. Tutti i membri della classe rientrano nell'ambito della classe Quando si definiscono membri all'esterno della classe, è necessario utilizzare l'operatore di ambito :: per indicare a quale dominio della classe appartiene il membro.
Quello che segue è il lungometraggio///
• Il processo di creazione di un oggetto nella memoria fisica utilizzando un tipo di classe è chiamato creazione di istanze di classe.
• Una classe è una descrizione astratta di un oggetto. È come un modello. Limita solo le variabili membro della classe
È una dichiarazione e non viene allocato spazio. Lo spazio verrà allocato solo quando viene istanziata un oggetto utilizzando una classe.
• Una classe può istanziare più oggetti. Gli oggetti istanziati occupano lo spazio fisico effettivo e memorizzano le variabili dei membri della classe.
Per riassumerlo in una frase: solo quando gli oggetti vengono istanziati con le classi allocheranno spazio (proprio come costruire una casa con i disegni occuperà spazio).
- class Date
- {
- public:
- void Init(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
Nel codice precedente, le variabili membro in private sono solo definite e non viene aperto alcun nuovo spazio e
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
Quindi vengono istanziati due nuovi oggetti, quindi viene aperto un nuovo spazio.
• Il primo membro si trova all'indirizzo offset 0 dalla struttura.
• Le altre variabili membro dovrebbero essere mappate su indirizzi che sono multipli interi di un certo numero (logaritmo).
• Nota: logaritmo = il più piccolo tra il logaritmo predefinito del compilatore e la dimensione del membro.
• Il logaritmo predefinito in VS è 8
• La dimensione totale della struttura è: un multiplo intero del numero massimo di coppie (il più grande di tutti i tipi di variabile e il più piccolo parametro di abbinamento predefinito).
• Se una struttura è annidata, la struttura annidata si allinea a un multiplo intero del suo logaritmo massimo e alla dimensione complessiva della struttura
È un multiplo intero di tutti i logaritmi più grandi (compresi i logaritmi delle strutture annidate).
Questo è ciò che sappiamo nel linguaggio C. Prendiamo come esempio le seguenti domande:
- // 计算⼀下A实例化的对象是多⼤?
- class A
- {
- public:
- void Print()
- {
- cout << _ch << endl;
- }
- private:
- char _ch;
- int _i;
- };
Non viene chiamata alcuna funzione, quindi basta guardare le variabili membro. _ch è un tipo di carattere char, con una dimensione di 1 byte; _i è un tipo int, con una dimensione di 4 byte numero di allineamento, quindi è _ Metti ch nella posizione con pedice 0, lascia tre spazi vuoti, quindi inserisci _i Occupa un totale di 8 byte di spazio da 0 a 7, che è un multiplo intero del numero massimo di allineamento. Soddisfa le condizioni, quindi la risposta a questa domanda è 8 .
E se fossero queste due domande?
- class B
- {
- public:
- void Print()
- {
- //...
- }
- };
-
- class C
- {
- };
Possiamo scoprire che esiste una funzione vuota nella classe B e nessun membro nella classe C. Nel linguaggio C, facciamo in modo che entrambe le classi occupino 1 byte di spazio.
• DCi sono due funzioni membro, Init e Print, nella classe ate. Non c'è distinzione tra diversi oggetti nel corpo della funzione. Quindi, quando d1 chiama Init e Print.
Quando si stampa la funzione, come fa la funzione a sapere se deve accedere all'oggetto d1 o all'oggetto d2?Quindi qui vedremo cosa dà il C++
Un puntatore this implicito risolve il problema qui
• Dopo la compilazione del compilatore, le funzioni membro della classe aggiungeranno un puntatore del tipo di classe corrente, chiamato puntatore, alla prima posizione del parametro formale per impostazione predefinita. Ad esempio, il vero prototipo della classe Init of Date è void Init(Date* const this, int year,
int mese, int giorno)
• Quando si accede alle variabili membro nelle funzioni membro di una classe, l'accesso avviene essenzialmente tramite il puntatore this. Ad esempio, quando si assegna un valore a _year nella funzione Init, this-.
>_year = anno;
• Il C++ stabilisce che questo puntatore non può essere scritto esplicitamente nella posizione dei parametri effettivi e dei parametri formali (il compilatore gestirà questo durante la compilazione), ma questo puntatore può essere usato esplicitamente nel corpo della funzione.
Per esempio
- class Date
- {
- public:
- // void Init(Date* const this, int year, int month, int day)
- void Init(int year, int month, int day)
- {
- // 编译报错:error C2106: “=”: 左操作数必须为左值
- // this = nullptr;
- // this->_year = year;
- _year = year;
- this->_month = month;
- this->_day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
-
- int main()
- {
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
- // d1.Init(&d1, 2024, 3, 31);this指针
- d1.Init(2024, 3, 31);
-
- // d1.Init(&d2, 2024, 7, 5);
- d2.Init(2024, 7, 5);
- //不能自己写
- return 0;
- }
Per la scrittura dei parametri effettivi nella funzione Init, C++ aggiungerà una costante invisibile questo puntatore (che non può essere modificato) alla prima variabile della funzione, come mostrato nei commenti della funzione.
Finché il puntatore nullo non viene dereferenziato, non verrà segnalato alcun errore.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Sebbene a p sia assegnato un puntatore nullo e punti alla funzione Print, non viene dereferenziato, quindi qui non verrà riportato alcun errore.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- cout << _a << endl;//这个地方多了一句
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Rispetto al programma precedente, c'è solo una frase in più cout << _a << endl, ma in questo momento il puntatore null punta a una variabile membro e il puntatore null verrà dereferenziato, quindi il programma segnalerà un errore.
La funzione membro predefinita è una funzione membro che non viene implementata in modo esplicito dall'utente e viene generata automaticamente dal compilatore. È denominata funzione membro predefinita. Per una classe, se non la scriviamo, il compilatore genererà per impostazione predefinita le seguenti 6 funzioni membro predefinite, che sono le seguenti 6
Il costruttore è una funzione membro speciale Va notato che sebbene il nome del costruttore sia chiamato costruttore, il compito principale del costruttore non è quello di aprire spazio per creare oggetti (l'oggetto locale che usiamo spesso è lo spazio quando lo stack. frame viene creato) ), invece l'oggetto viene inizializzato quando l'oggetto viene istanziato. L'essenza è sostituire la funzione Init di inizializzazione.
1. Il nome della funzione è uguale al nome della classe.
2. Nessun valore restituito. (Non è necessario scrivere vuoto)
3. Quando viene istanziata l'oggetto, il sistema chiamerà automaticamente il costruttore corrispondente.
4. I costruttori possono essere sovraccaricati.
5. Se nella classe non è presente un costruttore definito esplicitamente, il compilatore C++ genererà automaticamente un costruttore predefinito senza parametri. Una volta che l'utente lo definisce esplicitamente, il compilatore non lo genererà più.
I primi cinque elementi sono relativamente semplici, facciamo un esempio:
- class Date
- {
- public:
- //1.⽆参构造函数,无需写void
- Date()//若不写,则构造函数会写出这一中无参构造函数
- {
- _year = 1;
- _month = 1;
- _day = 1;
- }
- //2.全缺省构造函数
- Date(int year = 1, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- private:
- int _year;
- int _month;
- int _day;
- }
-
- int main()
- {
- Date d1;//此时相当于已经自动调用了Init函数
- Date d2;
-
- return 0;
- }
Nel programma vengono scritti due costruttori, uno è un costruttore senza parametri e l'altro è un costruttore completamente predefinito. Quando non scriviamo una funzione, il programma scriverà fino a un costruttore senza parametri. Inoltre, abbiamo detto in precedenza due funzioni sono funzioni sovraccaricate, a causa dell'ambiguità della chiamata, le due funzioni non possono esistere contemporaneamente.
6. I costruttori senza parametri, i costruttori completamente predefiniti e i costruttori generati dal compilatore per impostazione predefinita quando non scriviamo un costruttore sono tutti chiamati costruttori predefiniti. Tuttavia, esiste una ed una sola di queste tre funzioni e non può esistere contemporaneamente. Sebbene il costruttore senza parametri e il costruttore completamente predefinito costituiscano un sovraccarico di funzioni, ci sarà ambiguità quando li si chiama. Va notato che molti studenti pensano che il costruttore predefinito sia il costruttore predefinito generato dal compilatore. In effetti, anche il costruttore senza parametri e il costruttore predefinito completo sono costruttori predefiniti. Per riassumere, sono costruttori che possono essere chiamati senza passare parametri. Si chiama costruzione predefinita.
La sesta caratteristica è più complicata: i costruttori senza parametri, i costruttori tutti predefiniti e i costruttori generati dal compilatore per impostazione predefinita quando non scriviamo un costruttore sono tutti chiamati costruttori predefiniti e può essercene solo uno come costruttore generato dal compilatore per impostazione predefinita!
7. Se non lo scriviamo, il costruttore generato dal compilatore per impostazione predefinita non ha requisiti per l'inizializzazione delle variabili membro di tipo built-in. In altre parole, se sia inizializzato o meno è incerto, dipende dal compilatore . Per le variabili membro di tipo personalizzato, è necessario chiamare il costruttore predefinito di questa variabile membro per inizializzarla. Se questa variabile membro non ha un costruttore predefinito, verrà segnalato un errore. Se vogliamo inizializzare questa variabile membro, dobbiamo utilizzare un elenco di inizializzazione per risolvere il problema. Spiegheremo l'elenco di inizializzazione in dettaglio nel prossimo capitolo .
La funzione del distruttore è opposta a quella del costruttore. Il distruttore non completa la distruzione dell'oggetto stesso. Ad esempio, l'oggetto locale ha uno stack frame. Quando la funzione termina e lo stack frame viene distrutto, lo sarà rilasciato. Non è necessario controllarlo. Il C++ stabilisce che gli oggetti quando vengono distrutti, il distruttore verrà automaticamente chiamato per completare la pulizia e il rilascio delle risorse nell'oggetto. La funzione del distruttore è simile alla funzione Destroy che abbiamo implementato in precedenza in Stack. Ad esempio, Date non ha Destroy, quindi non ci sono risorse da rilasciare Il distruttore è proprio come la funzione Destroy. Dovrebbe essere utilizzato per strutture dati che richiedono oggetti non locali come stack e code per richiedere risorse.
1. Il nome del distruttore è preceduto dal carattere ~ prima del nome della classe. (Simile alla negazione bit a bit nel linguaggio C)
2. Nessun parametro e nessun valore restituito. (È simile alla struttura e non è necessario aggiungere vuoti)
3. Una classe può avere un solo distruttore. Se non definito esplicitamente, il sistema genererà automaticamente un distruttore predefinito.
4. Al termine del ciclo di vita dell'oggetto, il sistema chiamerà automaticamente il distruttore.
5. Similmente al costruttore, se non scriviamo un distruttore generato automaticamente dal compilatore, non elaborerà i membri del tipo incorporato. I membri del tipo personalizzato chiameranno i loro distruttori.
6. Va inoltre notato che quando scriviamo esplicitamente il distruttore, verrà chiamato anche il distruttore del membro di tipo personalizzato, vale a dire, il distruttore del membro di tipo personalizzato verrà chiamato automaticamente indipendentemente dalla situazione.
7. Se non sono presenti risorse richieste nella classe, non è necessario scrivere il distruttore e il distruttore predefinito generato dal compilatore, ad esempio Date, può essere utilizzato direttamente. Se è possibile utilizzare il distruttore generato per impostazione predefinita, non è necessario visualizzarlo. Scrivi il distruttore, come MyQueue; ma quando è presente un'applicazione di risorse, devi scrivere tu stesso il distruttore, altrimenti causerà una perdita di risorse, come Stack.
8. Per più oggetti in un dominio locale, il C++ stabilisce che quelli definiti successivamente verranno distrutti per primi.
Come il costruttore sopra, il distruttore predefinito generato automaticamente da C++ generalmente non ci sarà di grande aiuto, ma è più conveniente in alcuni rari casi, come l'utilizzo di due stack per implementare una coda:
- public:
- Stack(int n = 4)//相当于Init
- {
- _a = (STDataType*)malloc(sizeof(STDataType) * n);
- if (nullptr == _a)
- {
- perror("malloc fail!");
- return;
- }
- _capacity = n;
- _top = 0;
- }
- ~Stack()//相当于Destroy
- {
- cout << "~Stack()" << endl;
- free(_a);
- _a = nullptr;
- _top = _capacity = 0;
- }
- private:
- STDataType* _a;
- size_t _capacity;
- size_t _top;
- };
-
- class MyQueue
- {
- public:
- //编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
- // 显⽰写析构,也会⾃动调⽤Stack的析构
-
- private:
- Stack pushst;
- Stack popst;
- };
In questo momento, il compilatore in MyQueue chiama automaticamente il costruttore (inizializzazione) e il distruttore (distruzione) dello stack.