Compartir tecnología

Clases y Objetos (Parte 1)

2024-07-12

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

En el capítulo anterior, mencionamos brevemente algunos conceptos de clases y objetos. Ahora, repasémoslos juntos.

Definición de clase

Formato de definición de clase

clase es la palabra clave que define la clase, seguida del nombre de la clase que define, {} es el cuerpo principal de la clase, seguido de ";", y las funciones miembro definidas en la clase se expanden de forma predeterminada (en línea)

calificador de acceso

Un formato de encapsulación, dividido en tres tipos: público, privado y protegido. El alcance de los permisos de acceso comienza desde la posición donde aparece el calificador de acceso hasta que aparece el siguiente calificador de acceso. Si no hay un calificador de acceso posterior, se utilizará el dominio. termina en } es decir, clase.

dominio de clase

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

El siguiente es el largometraje///

Crear una instancia

concepto

• El proceso de crear un objeto en la memoria física usando un tipo de clase se llama creación de instancias de clase.
• Una clase es una descripción abstracta de un objeto. Es como un modelo. Limita las variables miembro de la clase únicamente.
Es una declaración y no se asigna espacio. Se asignará espacio solo cuando se cree una instancia de un objeto utilizando una clase.
• Una clase puede crear instancias de múltiples objetos. Los objetos instanciados ocupan espacio físico real y almacenan variables miembro de la clase.

Para resumirlo en una oración: solo cuando los objetos se instancian con clases asignarán espacio (al igual que construir una casa con dibujos ocupará espacio).

  1. class Date
  2. {
  3. public:
  4. void Init(int year, int month, int day)
  5. {
  6. _year = year;
  7. _month = month;
  8. _day = day;
  9. }
  10. void Print()
  11. {
  12. cout << _year << "/" << _month << "/" << _day << endl;
  13. }
  14. private:
  15. // 这⾥只是声明,没有开空间
  16. int _year;
  17. int _month;
  18. int _day;
  19. };

En el código anterior, las variables miembro en privado solo se definen y no se abre ningún espacio nuevo;

  1. // Date类实例化出对象d1和d2
  2. Date d1;
  3. Date d2;

Luego se crean instancias de dos nuevos objetos, por lo que se abre un nuevo espacio.

Reglas de alineación de la memoria

definición

• El primer miembro está en la dirección desplazada 0 de la estructura.
• Otras variables miembro deben asignarse a direcciones que sean múltiplos enteros de un determinado número (logaritmo).
• Nota: logaritmo = el menor entre el logaritmo predeterminado del compilador y el tamaño del miembro.
• El logaritmo predeterminado en VS es 8
• El tamaño total de la estructura es: un múltiplo entero del número máximo de pares (el mayor de todos los tipos de variables y el parámetro de emparejamiento predeterminado más pequeño).
• Si una estructura está anidada, la estructura anidada se alinea con un múltiplo entero de su logaritmo máximo y el tamaño total de la estructura.
Es un múltiplo entero de todos los logaritmos más grandes (incluidos los logaritmos de estructuras anidadas).

Esto es lo que sabemos en lenguaje C. Tome las siguientes preguntas como ejemplo:

  1. // 计算⼀下A实例化的对象是多⼤?
  2. class A
  3. {
  4. public:
  5. void Print()
  6. {
  7. cout << _ch << endl;
  8. }
  9. private:
  10. char _ch;
  11. int _i;
  12. };

No se llama a ninguna función, así que mire las variables miembro. _ch es un tipo de carácter char, con un tamaño de 1 byte; _i es un tipo int, con un tamaño de 4 bytes. número de alineación, por lo que es _ Coloque ch en la posición con el subíndice 0, deje tres espacios en blanco y luego coloque _i. Ocupa un total de 8 bytes de espacio del 0 al 7, que es un múltiplo entero del número máximo de alineación. Cumple las condiciones, por lo que la respuesta a esta pregunta es 8.

¿Y si son estas dos preguntas?

  1. class B
  2. {
  3. public:
  4. void Print()
  5. {
  6. //...
  7. }
  8. };
  9. class C
  10. {
  11. };

Podemos encontrar que hay una función vacía en la clase B y ningún miembro en la clase C. En lenguaje C, hacemos que ambas clases ocupen 1 byte de espacio.

este puntero

concepto

DHay dos funciones miembro, Init e Print, en la clase ate. No hay distinción entre diferentes objetos en el cuerpo de la función. Luego, cuando d1 llama a Init e Print,
Al imprimir la función, ¿cómo sabe la función si debe acceder al objeto d1 o al objeto d2?Entonces aquí veremos que C++ da
Un puntero this implícito resuelve el problema aquí.
• Después de que el compilador compila, las funciones miembro de la clase agregarán un puntero del tipo de clase actual, llamado este puntero, a la primera posición del parámetro formal de forma predeterminada. Por ejemplo, el prototipo real de la clase Init of Date es void Init(Date* const this, int año,
int mes, int día)
• Al acceder a variables miembro en las funciones miembro de una clase, esencialmente se accede a ellas a través del puntero this. Por ejemplo, al asignar un valor a _year en la función Init, esto.
&gt;_año = año;
• C++ estipula que este puntero no se puede escribir explícitamente en la posición de los parámetros reales y los parámetros formales (el compilador manejará esto durante la compilación), pero este puntero se puede usar explícitamente en el cuerpo de la función.

Por ejemplo

  1. class Date
  2. {
  3. public:
  4. // void Init(Date* const this, int year, int month, int day)
  5. void Init(int year, int month, int day)
  6. {
  7. // 编译报错:error C2106: “=”: 左操作数必须为左值
  8. // this = nullptr;
  9. // this->_year = year;
  10. _year = year;
  11. this->_month = month;
  12. this->_day = day;
  13. }
  14. void Print()
  15. {
  16. cout << _year << "/" << _month << "/" << _day << endl;
  17. }
  18. private:
  19. // 这⾥只是声明,没有开空间
  20. int _year;
  21. int _month;
  22. int _day;
  23. };
  24. int main()
  25. {
  26. // Date类实例化出对象d1和d2
  27. Date d1;
  28. Date d2;
  29. // d1.Init(&d1, 2024, 3, 31);this指针
  30. d1.Init(2024, 3, 31);
  31. // d1.Init(&d2, 2024, 7, 5);
  32. d2.Init(2024, 7, 5);
  33. //不能自己写
  34. return 0;
  35. }

Para escribir parámetros reales en la función Init, C++ agregará una constante invisible, este puntero (que no se puede modificar) a la primera variable de la función, como se muestra en los comentarios de la función.

Aviso:

Mientras no se elimine la referencia al puntero nulo, no se informará ningún error.

  1. class A
  2. {
  3. public:
  4. void Print()
  5. {
  6. cout << "A::Print()" << endl;
  7. }
  8. private:
  9. int _a;
  10. };
  11. int main()
  12. {
  13. A* p = nullptr;
  14. p->Print();
  15. return 0;
  16. }

Aunque a p se le asigna un puntero nulo y apunta a la función Imprimir, no se elimina la referencia, por lo que no se informará ningún error aquí. Y este programa:

  1. class A
  2. {
  3. public:
  4. void Print()
  5. {
  6. cout << "A::Print()" << endl;
  7. cout << _a << endl;//这个地方多了一句
  8. }
  9. private:
  10. int _a;
  11. };
  12. int main()
  13. {
  14. A* p = nullptr;
  15. p->Print();
  16. return 0;
  17. }

En comparación con el programa anterior, solo hay una oración más cout &lt;&lt; _a &lt;&lt; endl, pero en este momento el puntero nulo apunta a una variable miembro y se desreferenciará el puntero nulo, por lo que el programa informará un error.

Función miembro predeterminada de la clase

concepto

La función miembro predeterminada es una función miembro que el usuario no implementa explícitamente y el compilador la genera automáticamente. Se denomina función miembro predeterminada. Para una clase, si no la escribimos, el compilador generará las siguientes 6 funciones miembro predeterminadas de forma predeterminada, que son las siguientes 6

Constructor

definición

El constructor es una función miembro especial. Cabe señalar que, aunque el nombre del constructor se llama constructor, la tarea principal del constructor no es abrir espacio para crear objetos (el objeto local que usamos a menudo es el espacio en la pila). se crea el marco)), en lugar de eso, el objeto se inicializa cuando se crea una instancia del objeto. La esencia es reemplazar la función Init de inicialización.

Características

Cinco características principales

1. El nombre de la función es el mismo que el nombre de la clase.
2. Sin valor de retorno. (No es necesario escribir nulo)
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 definido explícitamente en la clase, el compilador de C ++ generará automáticamente un constructor predeterminado sin parámetros. Una vez que el usuario lo define explícitamente, el compilador ya no lo generará.

Los primeros cinco elementos son relativamente simples, demos un ejemplo:

  1. class Date
  2. {
  3. public:
  4. //1.⽆参构造函数,无需写void
  5. Date()//若不写,则构造函数会写出这一中无参构造函数
  6. {
  7. _year = 1;
  8. _month = 1;
  9. _day = 1;
  10. }
  11. //2.全缺省构造函数
  12. Date(int year = 1, int month = 1, int day = 1)
  13. {
  14. _year = year;
  15. _month = month;
  16. _day = day;
  17. }
  18. private:
  19. int _year;
  20. int _month;
  21. int _day;
  22. }
  23. int main()
  24. {
  25. Date d1;//此时相当于已经自动调用了Init函数
  26. Date d2;
  27. return 0;
  28. }

Se escriben dos constructores en el programa, uno es un constructor sin parámetros y el otro es un constructor predeterminado completo. Cuando no escribimos una función, el programa escribirá hasta un constructor sin parámetros. Además, dijimos antes Sin embargo, aunque estos. Dos funciones son funciones sobrecargadas, debido a la ambigüedad de las llamadas, las dos funciones no pueden existir al mismo tiempo.

Las dos últimas características

6. Los constructores sin parámetros, los constructores predeterminados completos y los constructores generados por el compilador de forma predeterminada cuando no escribimos un constructor se denominan constructores predeterminados. Sin embargo, una y sólo una de estas tres funciones existe y no puede existir al mismo tiempo. Aunque el constructor sin parámetros y el constructor predeterminado completo constituyen una sobrecarga de funciones, habrá ambigüedad al llamarlos. Cabe señalar que muchos estudiantes piensan que el constructor predeterminado es el constructor predeterminado generado por el compilador. De hecho, el constructor sin parámetros y el constructor predeterminado completo también son constructores predeterminados. En resumen, son constructores que se pueden llamar sin pasar el constructor predeterminado. parámetros.Se llama construcción predeterminada.

6

La sexta característica es más complicada: los constructores sin parámetros, los constructores predeterminados y los constructores generados por el compilador de forma predeterminada cuando no escribimos un constructor se denominan constructores predeterminados y solo puede haber uno. No debe comprender el constructor predeterminado. ¡como el constructor generado por el compilador de forma predeterminada!

7


7. Si no lo escribimos, el constructor generado por el compilador de forma predeterminada no tiene requisitos para la inicialización de variables miembro de tipo integradas. En otras palabras, si se inicializa o no es incierto, depende del compilador. . 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 resolver el problema. Explicaremos la lista de inicialización en detalle en el próximo capítulo. .

incinerador de basuras

concepto

La función del destructor es opuesta a la del constructor. El destructor no completa la destrucción del objeto en sí. Por ejemplo, el objeto local tiene un marco de pila. Cuando finaliza la función y se destruye el marco de la pila. liberado No necesitamos controlarlo. C ++ estipula que los objetos Cuando se destruyen, se llamará automáticamente al destructor para completar la limpieza y liberación de recursos en el objeto. La función del destructor es similar a la función Destroy que implementamos antes en Stack. Por ejemplo, Date no tiene Destroy. De hecho, no hay recursos para liberar. Por lo tanto, estrictamente hablando, Date no necesita un destructor. destructor es como la función Destroy. Debe usarse para estructuras de datos que requieren objetos no locales, como pilas y colas, para solicitar recursos.

Características

1. El nombre del destructor va precedido del carácter ~ antes del nombre de la clase. (Similar a la negación bit a bit en lenguaje C)
2. Sin parámetros ni valor de retorno. (Esto es similar a la estructura y no es necesario agregar vacío)
3. Una clase sólo puede tener un destructor. Si no se define explícitamente, el sistema generará automáticamente un destructor predeterminado.
4. Cuando finalice el ciclo de vida del objeto, el sistema llamará automáticamente al destructor.
5. De manera similar al constructor, si no escribimos un destructor generado automáticamente por el compilador, no procesará los miembros de tipo integrados que llamarán a sus destructores.
6. También debe tenerse en cuenta que cuando escribimos explícitamente el destructor, también se llamará al destructor del miembro de tipo personalizado, es decir, el destructor del miembro de tipo personalizado se llamará automáticamente sin importar cuál sea la situación.
7. Si no hay recursos solicitados en la clase, no es necesario escribir el destructor y el destructor predeterminado generado por el compilador, como Fecha, se puede usar directamente. no es necesario mostrarlo. Escriba el destructor, como MyQueue; pero cuando hay una aplicación de recursos, debe escribir el destructor usted mismo; de lo contrario, provocará una pérdida de recursos, como Stack.
8. Para varios objetos en un dominio local, C ++ estipula que los definidos más adelante se destruirán primero.

Uso de destructor

Al igual que el constructor anterior, el destructor predeterminado generado automáticamente por C++ generalmente no nos será de gran ayuda, pero es más conveniente en algunos casos raros, como el uso de dos pilas para implementar una cola:

  1. public:
  2. Stack(int n = 4)//相当于Init
  3. {
  4. _a = (STDataType*)malloc(sizeof(STDataType) * n);
  5. if (nullptr == _a)
  6. {
  7. perror("malloc fail!");
  8. return;
  9. }
  10. _capacity = n;
  11. _top = 0;
  12. }
  13. ~Stack()//相当于Destroy
  14. {
  15. cout << "~Stack()" << endl;
  16. free(_a);
  17. _a = nullptr;
  18. _top = _capacity = 0;
  19. }
  20. private:
  21. STDataType* _a;
  22. size_t _capacity;
  23. size_t _top;
  24. };
  25. class MyQueue
  26. {
  27. public:
  28. //编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
  29. // 显⽰写析构,也会⾃动调⽤Stack的析构
  30. private:
  31. Stack pushst;
  32. Stack popst;
  33. };

En este momento, el compilador en MyQueue llama automáticamente al constructor (inicialización) y al destructor (destrucción) de la pila.