Condivisione della tecnologia

Nozioni di base su C (2)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Sommario

1. Classi e oggetti

1.1 Definizione di classe

1.2 Qualificatori di accesso

1.3 Dominio di classe

2. Istanziazione

2.1 Il concetto di istanziazione

2.2 Dimensioni dell'oggetto

3.questo puntatore

4.Funzioni membro predefinite delle classi

4.1Costruttore

4.2 Distruttore

4.5 Sovraccarico degli operatori


1. Classi e oggetti

1.1 Definizione di classe

Formato di definizione della classe

class è la parola chiave che definisce la classe, Stack è il nome della classe e {} è il corpo della classe. Si noti che il punto e virgola alla fine della definizione non viene omesso. I contenuti nel corpo della classe sono chiamati membri della classe: le variabili nella classe sono chiamate attributi o variabili membro della classe sono chiamate metodi o funzioni membro della classe;

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. //成员变量
  7. int* a;
  8. int top;
  9. int capacity;
  10. //成员函数
  11. void Push()
  12. {
  13. }
  14. void Pop()
  15. {
  16. }
  17. };//分号不能省略
  18. int main()
  19. {
  20. return 0;
  21. }

  • Per distinguere le variabili membro, è generalmente consuetudine aggiungere un identificatore speciale alla variabile membro, ad esempio iniziare con _ o m_ prima o dopo la variabile membro.Non esistono normative su questa sintassi C++, è solo una questione di preferenze personali o aziendali.
  1. //为区分成员变量,一般前面加_
  2. //成员变量
  3. int* _a;
  4. int _top;
  5. int _capacity;
  • Struct in C++ può anche definire classi. C++ è compatibile con l'uso di struct in C. Allo stesso tempo, struct è stato aggiornato a una classe consiglio comunque di usare class per definire le classi.

  • Per impostazione predefinita, i membri definiti in una classe sono in linea

1.2 Qualificatori di accesso

C++ è un modo per implementare l'incapsulamento, utilizzando classi per combinare le proprietà e i metodi di un oggetto per rendere l'oggetto più completo e fornire selettivamente la sua interfaccia agli utenti esterni tramite diritti di accesso.

  • I membri modificati da public (public) sono accessibili direttamente all'esterno della classe, i membri modificati da protected (protetto) e private (private) non sono accessibili direttamente all'esterno della classe, protected e private sono la stessa cosa
  • L'ambito dell'autorizzazione di accesso inizia dalla posizione in cui viene visualizzata l'autorizzazione di accesso fino a quando non viene visualizzato il qualificatore di accesso successivo. Se non è presente alcun qualificatore di accesso successivo, l'ambito termina con }, che è la fine della classe.
  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. ///
  7. void Push()
  8. {
  9. }
  10. //Push 没给限定符 class默认私有 private
  11. ///
  12. public:
  13. void Pop()
  14. {
  15. }
  16. int Swap()
  17. {
  18. }
  19. //Pop和Swap 被public修饰,直到下一个限定符出现之前都为公有
  20. ///
  21. protected:
  22. int add();
  23. //add 被public修饰,直到下一个限定符出现之前都为保护
  24. /// /
  25. private:
  26. int* _a;
  27. int _top;
  28. int _capacity;
  29. //成员变量被private修饰,直到}结束都为私有
  30. };
  31. int main()
  32. {
  33. Stack st;
  34. //公有可以访问
  35. st.Pop();
  36. st.Swap();
  37. //私有不可访问
  38. st._top;
  39. return 0;
  40. }

Note aggiuntive:

  1. Quando i membri della definizione di classe non vengono modificati dai qualificatori di accesso, diventano privati ​​per impostazione predefinita e la struttura diventa pubblica per impostazione predefinita.
  2. Generalmente, le variabili membro saranno limitate a private/protette e le funzioni membro che devono essere utilizzate da altri verranno inserite come pubbliche.

1.3 Dominio di classe

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 ::scope per indicare a quale ambito della classe appartiene il membro.

Il dominio della classe influisce sulle regole di ricerca della compilazione. Se Init nel programma seguente non specifica il dominio della classe Stack, il compilatore tratterà Init come una funzione globale. Se non riesce a trovare membri come _top durante la compilazione, andrà a dominio di classe per trovarli.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. public:
  7. void Init(int x, int y);
  8. };
  9. void Stack::Init(int x, int y)
  10. {
  11. _top = x;
  12. _capacity = y;
  13. }
  14. int main()
  15. {
  16. return 0;
  17. }

Avviso:

  1. Le dichiarazioni e le definizioni delle funzioni nella classe vengono separate Dopo la creazione della classe, viene formato un nuovo dominio della classe. Il dominio della classe deve essere specificato, altrimenti sarà inaccessibile.

2. Istanziazione

2.1 Il concetto di istanziazione

  • Il processo di creazione di un tipo nella memoria fisica è chiamato istanziazione della classe.
  • Una classe è una descrizione astratta di un oggetto. È qualcosa come un modello. Limita le variabili membro della classe solo dichiarate e non viene allocato spazio quando un oggetto viene istanziato con una classe.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. //声明
  7. int* _a;
  8. int _top;
  9. int _capacity;
  10. };
  11. int main()
  12. {
  13. Stack::_top = 2024;
  14. //编译器报错,_top只是声明,并未实例化
  15. return 0;
  16. }
  • Una classe può istanziare più oggetti e gli oggetti istanziati occupano lo spazio fisico effettivo e memorizzano le variabili membro. Ad esempio: istanziare oggetti da una classe è come utilizzare disegni di progettazione architettonica per costruire una casa nella realtà. Le classi sono come disegni di progettazione che pianificano il numero di stanze, le dimensioni della casa, ecc., ma non esiste un fisico edificio, e non può vivere nelle persone, solo quando una casa viene costruita utilizzando i disegni di progetto le persone possono vivere nella casa.La stessa classe è come un disegno di progettazione. Indica semplicemente al compilatore la quantità di memoria che verrà aperta, ma non apre la memoria. Solo agli oggetti istanziati viene allocata memoria fisica per archiviare i dati.
    1. //text.cpp
    2. #include<iostream>
    3. using namespace std;
    4. class Stack
    5. {
    6. //声明
    7. int* _a;
    8. int _top;
    9. int _capacity;
    10. };
    11. int main()
    12. {
    13. Stack st;
    14. st._top=2024;
    15. //Stack实例化出st,系统已经给st分配内存了,可以存储数据,编译通过
    16. return 0;
    17. }

    2.2 Dimensioni dell'oggetto

  • Analizzare quali membri ha l'oggetto della classe? Ogni oggetto istanziato da una classe ha uno spazio dati indipendente, quindi l'oggetto deve contenere variabili membro. Quindi sono incluse le funzioni membro? Innanzitutto, dopo la compilazione, la funzione è una sezione di istruzioni che non può essere memorizzata nell'oggetto. Queste istruzioni vengono memorizzate in un'area separata (segmento di codice). Se l'oggetto deve essere memorizzato, può essere solo un puntatore una funzione membro. È necessario memorizzare i puntatori nell'oggetto? Date istanzia due oggetti d1 e d2 Sia di che d2 hanno variabili membro indipendenti _year/_month/_day per memorizzare i propri dati, ma le funzioni membro Init di d1 e d2 Il puntatore /Print. è lo stesso e viene sprecato se archiviato nell'oggetto. Se si utilizza Date per creare un'istanza di 100 oggetti, il puntatore alla funzione membro verrà archiviato ripetutamente 100 volte, il che è troppo dispendioso. In effetti, non è necessario memorizzare il puntatore alla funzione. Il puntatore alla funzione è un indirizzo. La funzione chiamante viene compilata in un'istruzione assembly [indirizzo di chiamata]. Infatti, il compilatore deve trovare l'indirizzo della funzione durante la compilazione e il collegamento Non viene trovato in fase di esecuzione. Può essere trovato solo dinamicamente in fase di esecuzione, quindi l'indirizzo della funzione deve essere memorizzato.

Regole di allineamento della memoria

  • Il primo membro si trova all'indirizzo offset 0 dalla struttura
  • Le altre variabili membro devono essere allineate a indirizzi multipli del numero di allineamento.
  • Numero di allineamento = Il più piccolo tra il numero di allineamento predefinito del compilatore e la dimensione del membro
  • Il numero di allineamento predefinito della piattaforma VS x64 è 4 e il numero di allineamento predefinito di x86 è 8
  • La dimensione totale della struttura è: un multiplo intero del numero massimo di allineamento (la più grande di tutte le variabili di tipo e il numero di allineamento predefinito più piccolo)
  • Se le strutture sono annidate, la struttura annidata è allineata a un multiplo intero del proprio numero di allineamento massimo e la dimensione complessiva della struttura è un multiplo intero di tutti i numeri di allineamento massimo (incluso il numero di allineamento delle strutture annidate)
  1. class A
  2. {
  3. public:
  4. void Print()
  5. {
  6. cout << _ch << endl;
  7. }
  8. private:
  9. char _ch;
  10. int _i;
  11. };
  12. //_ch 是一个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存在_ch
  13. // _i是4个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存储_i
  14. class B
  15. {
  16. public:
  17. void Print()
  18. {
  19. //。。。
  20. }
  21. };
  22. class B
  23. {
  24. };
  25. //B和C里面没有存储任何成员变量,只有一个函数,可成员函数不存对象里面
  26. // 按理来说是0,但是结构体怎么会没大小,为表示对象存在C++对这种规定大小为1,为了占位标识对象存在

3.questo puntatore

Dopo la compilazione del compilatore, le funzioni membro della classe aggiungeranno per impostazione predefinita un puntatore alla classe corrente nella prima posizione del parametro formale, chiamato questo puntatore.

Ad esempio, il prototipo Init nella classe Date è void Init (Date * const this, int anno, int mese, int giorno) Quando si accede alle variabili membro nelle funzioni membro della classe, si accede all'essenza tramite il puntatore this. come _year nella funzione Init Assignment, this-&gt;_year=year

prototipo:

  1. class Date
  2. {
  3. void Print()
  4. {
  5. cout << _year << "n" << _month << "n" << _day << endl;
  6. }
  7. void Init( int year, int month,int day)
  8. {
  9. _year = year;
  10. _month = month;
  11. _day = day;
  12. }
  13. private:
  14. int _year;
  15. int _month;
  16. int _day;
  17. };

  1. Date d1;
  2. d1.Init(2024,7,10);
  3. d1.Print();
  4. Date d2;
  5. d2.Init(2024, 7, 9);
  6. d2.Print();

prototipo reale

  1. class Date
  2. {
  3. void Init(Date* const this,int year, int month,int day)
  4. {
  5. this->_year = year;
  6. this->_month = month;
  7. this->_day = day;
  8. }
  9. void Printf(Date* const this)
  10. {
  11. cout << this->_year << "n" <<this-> _month << "n" << this->_day << endl;
  12. }
  13. private:
  14. int _year;
  15. int _month;
  16. int _day;
  17. };

  1. Date d1;
  2. d1.Init(&d1,2024,7,10);
  3. d1.Print(&d1);
  4. Date d2;
  5. d2.Init(&d2,2024, 7, 9);
  6. d2.Print();

C++ stabilisce che non è consentito scrivere questo puntatore nella posizione dei parametri effettivi e dei parametri formali (il compilatore lo gestirà durante la compilazione), ma questo puntatore può essere utilizzato esplicitamente nel corpo della funzione. Questo puntatore non può essere modificato, ma il il contenuto indicato da questo puntatore può

questo puntatore è memorizzato nello stack

4.Funzioni membro predefinite delle classi

La funzione membro predefinita è una funzione membro che non è definita in modo esplicito dall'utente e viene generata automaticamente dal compilatore. È denominata funzione membro predefinita.

4.1Costruttore

Il costruttore è una funzione membro speciale Va notato che sebbene il costruttore sia chiamato costruttore, il contenuto principale del costruttore non è quello di aprire spazio per creare oggetti (l'oggetto locale che usiamo solitamente è lo spazio che viene aperto quando. viene creato lo stack frame) ), ma l'oggetto viene inizializzato quando viene istanziata l'oggetto. L'essenza del costruttore è sostituire la funzione della funzione Init che abbiamo scritto prima nelle classi Stack e Date. La chiamata automatica del costruttore sostituisce perfettamente la funzione Init.

Caratteristiche del costruttore:

  1. Il nome della funzione è lo stesso del nome della classe
  2. Nessun valore restituito (non è necessario fornire nulla come valore restituito e non scrivere void. Questa è la regola del C++)
  3. Quando l'oggetto viene istanziato, il sistema chiamerà automaticamente il costruttore corrispondente.
  4. I costruttori possono essere sovraccaricati
  5. Se nella classe non è definito un costruttore esplicito, il compilatore C++ genererà automaticamente un costruttore predefinito senza parametri. Una volta che l'utente definisce esplicitamente il costruttore, il compilatore non lo genererà più.

  1. class Date
  2. {public:
  3. //1.无参构造函数
  4. Date()
  5. {
  6. _year = 1;
  7. _month = 1;
  8. _day = 1;
  9. }
  10. //2.带参构造函数
  11. Date(int year, int month, int day)
  12. {
  13. _year = year;
  14. _month = month;
  15. _day = day;
  16. }
  17. //3.全缺省构造函数
  18. Date(int year = 1, int month = 1, int day = 1)
  19. {
  20. _year = year;
  21. _month = month;
  22. _day = day;
  23. }
  24. private:
  25. int _year;
  26. int _month;
  27. int _day;
  28. };

Il costruttore senza argomenti, il costruttore tutto predefinito e il costruttore generato dal compilatore per impostazione predefinita quando non scriviamo un costruttore sono tutti chiamati costruttori predefiniti. Ma solo uno di questi tre può esistere, non contemporaneamente. Sebbene il costruttore senza parametri e il costruttore completamente predefinito costituiscano un sovraccarico di funzioni, ci sarà ambiguità quando li si chiama.Si noti che non solo il costruttore predefinito è quello generato dal compilatore per impostazione predefinita, è il costruttore, il costruttore senza parametri e il costruttore predefinito completo è anche il costruttore predefinito. Per riassumere, può essere chiamato senza passare parametri.

Non lo scriviamo. Il costrutto generato dal compilatore per impostazione predefinita non ha requisiti per l'inizializzazione delle variabili membro di tipo integrate. Vale a dire, se è inizializzato o meno è incerto, dipende dal compilatore.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. typedef int STDataType;
  5. class Stack
  6. {
  7. public:
  8. Stack(int n = 4)
  9. {
  10. _a = (STDataType*)malloc(sizeof(STDataType) * n);
  11. if (nullptr == _a)
  12. {
  13. perror("malloc申请失败");
  14. }
  15. _capacity = n;
  16. _top = 0;
  17. }
  18. private:
  19. STDataType* _a;
  20. size_t _capacity;
  21. size_t _top;
  22. };
  23. //两个Stack实现队列
  24. class MyQueue
  25. {
  26. private:
  27. int size;
  28. Stack pushst;
  29. Stack popst;
  30. };
  31. int main()
  32. {
  33. MyQueue my;
  34. return 0;
  35. }

Il C++ divide i tipi in tipi personalizzati e tipi incorporati (tipi di base). I tipi incorporati sono i tipi di dati nativi forniti dal linguaggio, come int/char/double/pointer, ecc. I tipi personalizzati sono tipi che definiamo noi stessi utilizzando parole chiave come class/struct.Il costruttore qui viene inizializzato automaticamente e VS inizializza anche la dimensione del tipo incorporato Diversi compilatori hanno valori di inizializzazione diversi e C++ non li specifica.

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 risolverlo.

Riepilogo: nella maggior parte dei casi, dobbiamo implementare noi stessi il costruttore. In alcuni casi, è simile a MyQueue e quando Stack ha un costruttore predefinito, MyQueue può essere generato e utilizzato automaticamente.

4.2 Distruttore

  1. ~Stack()
  2. {
  3. free(_a);
  4. _a = nullptr;
  5. _top = _capacity = 0;
  6. }

Caratteristiche del distruttore:

1. Il nome del distruttore è preceduto dai caratteri~

2. Nessun parametro e nessun valore restituito (coerente con il costruttore)

3. Una classe può avere un solo distruttore.Se la definizione non viene visualizzata, il sistema genererà automaticamente un distruttore predefinito.

4. Al termine del ciclo di dichiarazione dell'oggetto, il sistema chiamerà automaticamente il distruttore.

5. Analogamente al costruttore, non scriviamo il distruttore generato automaticamente dal compilatore e non elaboriamo i membri di tipo incorporati. I membri di tipo personalizzato chiameranno altri distruttori.

6. Va inoltre notato che quando visualizziamo il distruttore, verrà chiamato anche il distruttore del membro di tipo personalizzato, il che significa che il distruttore del membro di tipo personalizzato verrà chiamato automaticamente indipendentemente dalla situazione.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. typedef int STDataType;
  5. class Stack
  6. {
  7. public:
  8. Stack(int n = 4)
  9. {
  10. _a = (STDataType*)malloc(sizeof(STDataType) * n);
  11. if (nullptr == _a)
  12. {
  13. perror("malloc申请失败");
  14. }
  15. _capacity = n;
  16. _top = 0;
  17. }
  18. ~Stack()
  19. {
  20. free(_a);
  21. _a = nullptr;
  22. _top=_capacity=0;
  23. }
  24. private:
  25. STDataType* _a;
  26. size_t _capacity;
  27. size_t _top;
  28. };
  29. //两个Stack实现队列
  30. class MyQueue
  31. {public:
  32. //编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化
  33. //编译器默认生成MyQueue的析构函数调用了Stack的析构,释放了Stack内部的资源
  34. //显示写析构也会调用Stack的析构
  35. ~MyQueue()
  36. {
  37. cout << "~MyQueue" << endl;
  38. }
  39. private:
  40. Stack pushst;
  41. Stack popst;
  42. };
  43. int main()
  44. {
  45. MyQueue my;
  46. return 0;
  47. }

Il distruttore in MyQueue non fa nulla, ma C++ stabilisce che verranno chiamati altri distruttori per liberare la memoria.

Se non vengono richieste risorse, non è necessario scrivere il distruttore e il distruttore predefinito generato dal compilatore può essere utilizzato direttamente, ad esempio Date. Se è possibile utilizzare il distruttore predefinito generato, non è necessario scrivere in modo esplicito il distruttore come MyQueue, ma esiste un'applicazione di risorse Durante la distruzione, assicurati di scrivere direttamente il distruttore, altrimenti causerà una perdita di risorse come Stack

4.5 Sovraccarico degli operatori

  • Quando gli operatori vengono utilizzati su oggetti tipizzati, il linguaggio C++ ci consente di specificare nuovi significati sotto forma di sovraccarico degli operatori. C++ stabilisce che quando un oggetto di tipo classe utilizza un operatore, deve essere convertito in una chiamata all'overload dell'operatore corrispondente. In caso contrario, il compilatore segnalerà un errore.
  • L'overload dell'operatore è una funzione con un nome specifico. Il suo nome è composto da operator e dall'operatore da definire in seguito.Come altre funzioni, ha anche il proprio tipo restituito e un elenco di parametri, nonché un corpo della funzione
  1. bool operator<(Date d1, Date d2)
  2. {
  3. }
  4. bool operator==(Date d1,Date d2)
  5. {
  6. return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
  7. }
  • Una funzione operatore sovraccaricata accetta tanti parametri quanti sono i parametri su cui agisce l'operatore.Il comfort di trasporto unario ha un parametro e l'operatore binario ha due parametri L'operando sinistro dell'operatore binario viene passato al primo parametro e l'operando destro viene passato al secondo parametro.
  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Date
  5. {
  6. public:
  7. Date(int year, int month, int day)
  8. {
  9. _year= year;
  10. _month = month;
  11. _day = day;
  12. }
  13. int _year;
  14. int _month;
  15. int _day;
  16. };
  17. bool operator<(Date d1, Date d2)
  18. {
  19. }
  20. bool operator==(Date d1,Date d2)
  21. {
  22. return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
  23. }
  24. int main()
  25. {
  26. Date d1(2024, 7, 10);
  27. Date d2(2024,7,9);
  28. //两种用法都可以
  29. d1 == d2;
  30. operator==(d1 , d2);
  31. return 0;
  32. }
  • Se una funzione dell'operatore sovraccaricato è una funzione membro, il suo primo operando viene passato al puntatore this implicito per impostazione predefinita. Pertanto, quando l'operatore viene sovraccaricato come funzione membro, ha un parametro in meno rispetto all'operando.
  • Dopo che un operatore è stato sovraccaricato, la sua precedenza e associatività rimangono coerenti con le operazioni di tipo predefinite.
  • Non è possibile creare un operatore sessuale concatenando corrispondenze che non esistono nella sintassi: ad es.