Compartir tecnología

Conceptos básicos de C (2)

2024-07-12

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

Tabla de contenido

1. Clases y Objetos

1.1 Definición de clase

1.2 Calificadores de acceso

1.3 Dominio de clase

2. Creación de instancias

2.1 Concepto de instanciación

2.2 Tamaño del objeto

3.este puntero

4.Funciones miembro predeterminadas de las clases.

4.1Constructor

4.2 Destructor

4.5 Sobrecarga del operador


1. Clases y Objetos

1.1 Definición de clase

Formato de definición de clase

clase es la palabra clave que define la clase, Pila es el nombre de la clase y {} es el cuerpo de la clase. Tenga en cuenta que el punto y coma al final de la definición no se omite. Los contenidos del cuerpo de la clase se denominan miembros de la clase: las variables de la clase se denominan atributos o las variables miembro de la clase las funciones de la clase se denominan métodos o funciones miembro de la clase.

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

  • Para distinguir las variables miembro, generalmente se acostumbra agregar un identificador especial a la variable miembro, como comenzar con _ o m_ antes o después de la variable miembro.No existen regulaciones sobre esta sintaxis de C++, es solo una cuestión de preferencia personal o de la empresa.
  1. //为区分成员变量,一般前面加_
  2. //成员变量
  3. int* _a;
  4. int _top;
  5. int _capacity;
  • Struct en C++ también puede definir clases. C++ es compatible con el uso de struct en C. Al mismo tiempo, struct se ha actualizado a una clase. El cambio obvio es que las funciones también se pueden definir en struct. Todavía recomiendo usar class para definir clases.

  • Los miembros definidos en una clase están predeterminados en línea

1.2 Calificadores de acceso

C ++ es una forma de implementar la encapsulación: utiliza clases para combinar las propiedades y métodos de un objeto para hacerlo más completo y proporciona selectivamente su interfaz a usuarios externos a través de derechos de acceso.

  • Se puede acceder directamente a los miembros modificados por público (público) fuera de la clase, no se puede acceder directamente a los miembros modificados por protegido (protegido) y privado (privado) fuera de la clase, protegido y privado son lo mismo
  • El alcance del permiso de acceso comienza desde la posición donde aparece el permiso de acceso hasta que aparece el siguiente calificador de acceso. Si no hay un calificador de acceso posterior, el alcance termina en }, que es el final de la clase.
  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. }

Notas adicionales:

  1. Cuando los calificadores de acceso no modifican los miembros de la definición de clase, su valor predeterminado es privado y el valor predeterminado de estructura es público.
  2. Generalmente, las variables miembro estarán restringidas a privadas/protegidas, y las funciones miembro que otros deben utilizar se colocarán como públicas.

1.3 Dominio de clase

La clase define un nuevo ámbito. Todos los miembros de la clase están en el ámbito de la clase. Al definir miembros fuera de la clase, debe utilizar el operador ::scope para indicar a qué ámbito de clase pertenece el miembro.

El dominio de clase afecta las reglas de búsqueda de compilación. Si Init en el siguiente programa no especifica la pila de dominio de clase, el compilador tratará a Init como una función global. Si no puede encontrar miembros como _top durante la compilación, irá al. dominio de clase para encontrarlos.

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

Aviso:

  1. Las declaraciones y definiciones de funciones en la clase se separan. Una vez creada la clase, se forma un nuevo dominio de clase. Es necesario especificar el dominio de clase; de ​​lo contrario, será inaccesible.

2. Creación de instancias

2.1 Concepto de instanciación

  • El proceso de crear un tipo en la memoria física se llama creación de instancias de clase.
  • Una clase es una descripción abstracta de un objeto. Es algo así como un modelo. Limita las variables miembro de la clase. Estas variables miembro solo se declaran y no se asigna espacio cuando se crea una instancia de un objeto.

  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 clase puede crear instancias de múltiples objetos, y los objetos instanciados ocupan espacio físico real y almacenan variables miembro. Por ejemplo: crear instancias de objetos de una clase es como usar dibujos de diseño arquitectónico para construir una casa en la realidad. Las clases son como dibujos de diseño. Los dibujos de diseño planifican el número de habitaciones, el tamaño de la casa, etc., pero no hay elementos físicos. edificio, y no puede vivir en la gente, sólo cuando una casa se construye usando los dibujos de diseño la gente puede vivir en la casa.La misma clase es como un dibujo de diseño. Simplemente le dice al compilador cuánta memoria se va a abrir, pero no abre la memoria. Solo a los objetos instanciados se les asigna memoria física para almacenar datos.
    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 Tamaño del objeto

  • ¿Analizar qué miembros tiene el objeto de clase? Cada objeto instanciado por una clase tiene un espacio de datos independiente, por lo que el objeto debe contener variables miembro. Entonces, ¿se incluyen las funciones miembro? Primero, una vez compilada la función, es una sección de instrucciones que no se puede almacenar en el objeto. Estas instrucciones se almacenan en un área separada (segmento de código). Si el objeto debe almacenarse, solo puede ser un puntero. una función miembro. ¿Es necesario almacenar punteros en el objeto? La fecha crea una instancia de dos objetos d1 y d2. Tanto di como d2 tienen variables miembro independientes _año/_mes/_día para almacenar sus propios datos, pero las funciones miembro Init de d1 y d2 son el puntero /Imprimir. es el mismo y se desperdicia si se almacena en el objeto. Si usa Date para crear instancias de 100 objetos, entonces el puntero de la función miembro se almacenará repetidamente 100 veces, lo cual es un desperdicio. De hecho, no es necesario almacenar el puntero de función. El puntero de función es una dirección. La función de llamada se compila en una instrucción de ensamblaje [dirección de llamada]. No se encuentra en tiempo de ejecución. Solo se puede encontrar dinámicamente. El estado se encuentra en tiempo de ejecución, por lo que es necesario almacenar la dirección de la función.

Reglas de alineación de la memoria

  • El primer miembro está en la dirección desplazada 0 de la estructura.
  • Otras variables miembro deben estar alineadas en direcciones que sean múltiplos del número de alineación.
  • Número de alineación = El menor entre el número de alineación predeterminado del compilador y el tamaño del miembro
  • El número de alineación predeterminado de la plataforma VS x64 es 4 y el número de alineación predeterminado de x86 es 8
  • El tamaño total de la estructura es: un múltiplo entero del número de alineación máximo (la mayor de todas las variables de tipo y el número de alineación predeterminado más pequeño)
  • Si las estructuras están anidadas, la estructura anidada se alinea con un múltiplo entero de su propio número de alineación máximo, y el tamaño total de la estructura es un múltiplo entero de todos los números de alineación máximos (incluido el número de alineación de las estructuras anidadas)
  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.este puntero

Después de que el compilador compila, las funciones miembro de la clase agregarán un puntero a la clase actual en la primera posición del parámetro formal de forma predeterminada, llamado este puntero.

Por ejemplo, el prototipo de Init en la clase Date es void Init (Fecha * const this, int año, int mes, int día). Al acceder a variables miembro en las funciones miembro de la clase, se accede a la esencia a través de este puntero. como _año en la función Iniciar, este-&gt;_año=año.

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 real

  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 ++ estipula que no está permitido escribir este puntero en la posición de los parámetros reales y los parámetros formales (el compilador lo manejará al compilar), pero este puntero se puede usar explícitamente en el cuerpo de la función. Este puntero no se puede modificar, pero el. El contenido al que apunta este puntero puede

este puntero se almacena en la pila

4.Funciones miembro predeterminadas de las clases.

La función miembro predeterminada es una función miembro que el usuario no define explícitamente y el compilador la genera automáticamente. Se denomina función miembro predeterminada.

4.1Constructor

El constructor es una función miembro especial. Cabe señalar que aunque el constructor se llama constructor, el contenido principal del constructor no es abrir espacio para crear objetos (el objeto local que usualmente usamos es el espacio que se abre cuando se abre). se crea el marco de pila)), pero el objeto se inicializa cuando se crea una instancia del objeto. La esencia del constructor es reemplazar la función Init que escribimos antes en las clases Stack y Date. La llamada automática del constructor reemplaza perfectamente la función Init.

Características del constructor:

  1. El nombre de la función es el mismo que el nombre de la clase.
  2. Sin valor de retorno (no es necesario que proporciones nada como valor de retorno y no escribas void. Esta es la regla de C++)
  3. Cuando se crea una instancia del objeto, el sistema llamará automáticamente al constructor correspondiente.
  4. Los constructores pueden estar sobrecargados.
  5. Si no hay un constructor explícito definido en la clase, el compilador de C++ generará automáticamente un constructor predeterminado sin parámetros. Una vez que el usuario define explícitamente el constructor, el compilador ya no lo generará.

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

El constructor sin parámetros, el constructor predeterminado y el constructor generado por el compilador de forma predeterminada cuando no escribimos un constructor se denominan constructores predeterminados. Pero sólo uno de estos tres puede existir, no al mismo tiempo. Aunque el constructor sin parámetros y el constructor predeterminado completo constituyen una sobrecarga de funciones, habrá ambigüedad al llamarlos.Tenga en cuenta que no solo el constructor predeterminado es el que genera el compilador de forma predeterminada, sino que es el constructor, el constructor sin parámetros, y el constructor predeterminado completo también es el constructor predeterminado. En resumen, se puede llamar sin pasar parámetros.

No lo escribimos. La construcción generada por el compilador de forma predeterminada no tiene requisitos para la inicialización de variables miembro de tipo integradas, es decir, si se inicializa o no, depende del compilador.

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

C++ divide los tipos en tipos personalizados y tipos integrados (tipos básicos). Los tipos integrados son los tipos de datos nativos proporcionados por el lenguaje, como int/char/double/pointer, etc. Los tipos personalizados son tipos que definimos nosotros mismos usando palabras clave como class/struct.El constructor aquí se inicializa automáticamente y VS también inicializa el tamaño de tipo incorporado. Los diferentes compiladores tienen diferentes valores de inicialización y C ++ no los especifica.

Para variables miembro de tipo personalizado, es necesario llamar al constructor predeterminado de esta variable miembro para inicializarla.Si esta variable miembro no tiene un constructor predeterminado, se informará un error. Si queremos inicializar esta variable miembro, necesitamos usar una lista de inicialización para resolverlo.

Resumen: en la mayoría de los casos, necesitamos implementar el constructor nosotros mismos. En algunos casos, es similar a MyQueue y cuando Stack tiene un constructor predeterminado, MyQueue se puede generar y usar automáticamente.

4.2 Destructor

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

Características del destructor:

1. El nombre del destructor está precedido de caracteres ~

2. Sin parámetros ni valor de retorno (consistente con el constructor)

3. Una clase sólo puede tener un destructor.Si no se muestra la definición, el sistema generará automáticamente un destructor predeterminado.

4. Cuando finalice el ciclo de declaración de objetos, el sistema llamará automáticamente al destructor.

5. Al igual que el constructor, no escribimos el destructor generado automáticamente por el compilador y no procesamos los miembros de tipo integrados. Los miembros de tipo personalizado llamarán a otros destructores.

6. También debe tenerse en cuenta que cuando mostramos el destructor, también se llamará al destructor del miembro de tipo personalizado, lo que significa que el destructor del miembro de tipo personalizado se llamará automáticamente sin importar la situación.

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

El destructor en MyQueue no hace nada, pero C ++ estipula que se llamará a otros destructores para liberar la memoria.

Si no se solicitan recursos, no es necesario escribir el destructor y el destructor predeterminado generado por el compilador se puede usar directamente, como Fecha. Si se puede usar el destructor predeterminado generado, no es necesario escribir explícitamente el destructor. como MyQueue, pero hay una aplicación de recursos al destruir, asegúrese de escribir el destructor directamente, de lo contrario provocará una pérdida de recursos como Stack.

4.5 Sobrecarga del operador

  • Cuando se utilizan operadores en objetos escritos, el lenguaje C++ nos permite especificar nuevos significados en forma de sobrecarga de operadores. C ++ estipula que cuando un objeto de tipo de clase usa un operador, debe convertirse en una llamada al operador correspondiente. De lo contrario, el compilador informará un error.
  • La sobrecarga de operadores es una función con un nombre específico. Su nombre se compone de operador y el operador que se definirá más adelante.Al igual que otras funciones, también tiene su tipo de retorno y lista de parámetros, así como un cuerpo de función.
  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 función de operador sobrecargada toma tantos parámetros como parámetros sobre los que actúa el operador.La comodidad de transporte unario tiene un parámetro y el operador binario tiene dos parámetros. El operando izquierdo del operador binario se pasa al primer parámetro y el operando derecho se pasa al segundo parámetro.
  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. }
  • Si una función de operador sobrecargada es una función miembro, su primer operando se pasa al puntero this implícito de forma predeterminada. Por lo tanto, cuando el operador está sobrecargado como función miembro, tiene un parámetro menos que el operando.
  • Después de sobrecargar un operador, su precedencia y asociatividad siguen siendo consistentes con las operaciones de tipo integradas.
  • No se puede crear un operador sexual concatenando coincidencias que no existen en la sintaxis: por ejemplo operador@.