Resumen de la experiencia de la entrevista en C++: Programación orientada a objetos


prefacio

El propósito de este artículo es aprender de la falta de comprensión de lo que ha aprendido durante el proceso de la entrevista de trabajo. Durante el proceso de aprendizaje, se utiliza una gran cantidad de artículos en línea como referencia. Si hay comprensiones u omisiones inapropiadas, espero puedes darme un consejo.


¿Qué es la Programación Orientada a Objetos?

Definición de Programación Orientada a Objetos

La Programación Orientada a Objetos (POO) es el paradigma de programación básico de muchos lenguajes de programación (incluidos Java, C++), que organiza el diseño de software en torno a datos u objetos en lugar de funciones y lógica. Los objetos pueden ser campos de datos con propiedades y comportamientos únicos.

La programación orientada a objetos se enfoca en los objetos que el desarrollador desea manipular, en lugar de la lógica requerida para manipular los objetos. Esta mentalidad de programación es ideal para programas grandes y complejos que se actualizan o mantienen activamente.

Orientado a procesos vs Orientado a objetos

  • Orientado a procesos es una idea de programación centrada en eventos, que enfatiza los comportamientos funcionales , generalmente toma las funciones como unidades y analiza el tema como pasos de comportamiento para resolver problemas.

  • Orientado a objetos es una idea de programación centrada en objetos, enfatizando objetos con funciones , generalmente tomando objetos/clases como unidades, y el sujeto de análisis es el ejecutor y el ejecutado en el problema.

  • El rendimiento de la orientación a procesos es mayor que la de la orientación a objetos. La orientación a objetos debe instanciarse cuando se llama a la clase, y este proceso consume más recursos.

  • La orientación a objetos es más fácil de mantener, expandir y reutilizar que la orientación a procesos. La herencia, el encapsulamiento y el polimorfismo orientados a objetos hacen posible diseñar un sistema de bajo acoplamiento.

Estructura de la Programación Orientada a Objetos

  • Clase Un tipo de datos definido por el usuario que actúa como modelo para objetos, propiedades y métodos individuales
    • Por sí misma, una clase no hace nada, es una especie de plantilla para crear objetos concretos de ese tipo.
  • objeto Una instancia de una clase creada con datos especialmente definidos
  • método Una función definida en una clase que describe el comportamiento de un objeto
    • Cada método contenido en una definición de clase comienza con una referencia al objeto de instancia
    • Las subrutinas (métodos) contenidas en un objeto se denominan métodos de instancia.
  • Las propiedades se definen en una plantilla de clase y representan el estado de un objeto
    • Los atributos de clase pertenecen a la clase misma.

Los principios fundamentales de la orientación a objetos.

  • La encapsulación oculta el estado interno y la funcionalidad de un objeto, lo que permite el acceso solo a través de un conjunto de funciones públicas
  • Abstracción Modela las propiedades e interacciones relacionadas de las entidades como clases para definir una representación abstracta del sistema
  • Herencia Habilidad para crear nuevas abstracciones basadas en abstracciones existentes
  • Polimorfismo La capacidad de implementar una propiedad o método heredado de manera diferente a través de múltiples abstracciones

Ventajas de la programación orientada a objetos

  • La encapsulación de modularidad permite que los objetos sean autónomos, lo que facilita la resolución de problemas y el desarrollo colaborativo
  • El código reutilizable se puede reutilizar a través de la herencia, lo que significa que los equipos no tienen que escribir el mismo código varias veces.
  • Mayor eficiencia Los programadores pueden crear nuevos programas más rápido mediante el uso de múltiples bibliotecas y código reutilizable
  • Fácil de actualizar y ampliar Los programadores pueden realizar funciones del sistema de forma independiente
  • Descripción de la interfaz La tecnología de paso de mensajes basada en la comunicación de objetos hace que la descripción de la interfaz externa sea muy sencilla
  • Seguridad Utilice la encapsulación y la abstracción para ocultar código complejo y facilitar el mantenimiento del software
  • El polimorfismo de flexibilidad permite que una sola función sea compatible con múltiples clases, y diferentes objetos pueden implementar funciones llamando a la misma interfaz

encapsulación

La encapsulación se refiere al uso de tipos de datos abstractos para encapsular datos y operaciones basadas en datos para formar una entidad independiente e inseparable. Los datos están protegidos dentro del tipo de datos abstractos, ocultando los detalles tanto como sea posible y solo conservando algunas interfaces externas para su uso. .para comunicarse con el mundo exterior. El usuario no necesita conocer los detalles internos del objeto y solo puede acceder al objeto a través de la interfaz externa provista

Beneficios de usar la encapsulación

  • Un buen embalaje reduce el acoplamiento
  • La estructura dentro de la clase se puede modificar libremente.
  • Permite un control más preciso sobre los miembros.
  • Ocultar información, detalles de implementación

modificador de acceso

Los modificadores de acceso se utilizan dentro del cuerpo de la clase para restringir el acceso a los miembros de la clase. Los modificadores de acceso incluyen principalmente public, private y protected (internal y protected internal se agregan en C#). Una clase puede tener varias áreas de marcado públicas, privadas o protegidas, y cada área de marcado es válida hasta que comience la siguiente área de marcado o hasta que encuentre el paréntesis de cierre del cuerpo de la clase.

  • Se puede acceder a public mediante funciones de esta clase, funciones de subclases y sus funciones amigas, y también se puede acceder mediante objetos de esta clase.
  • private solo puede ser accedido por funciones en esta clase y sus funciones amigas, y no puede ser accedido por ningún otro objeto, ni puede ser accedido por objetos de esta clase
  • Se puede acceder a protected mediante funciones de esta clase, funciones de subclases y sus funciones amigas, pero no mediante objetos de esta clase.
  • Cualquier código en el mismo ensamblado puede acceder al tipo o miembro, pero el código en otros ensamblados no puede
  • protected internal El tipo o miembro puede ser accedido por cualquier código en el ensamblado en el que se declara o una clase derivada en otro ensamblado
  • private protected El tipo o miembro puede ser accedido por tipos derivados de la clase que se declaran en sus ensamblados contenedores

asamblea

Una colección de uno o más archivos de definición de tipos y archivos de recursos

El montaje incluye

  • recurso
  • Metadatos de tipo (describe cada tipo y miembro definido en el código, en forma binaria)
  • Código IL (encapsulado en exe o dll)

El ensamblado resultante puede ser una aplicación ejecutable o una DLL

Beneficios de usar ensamblajes

  • Solo se hace referencia a los ensamblajes necesarios en el programa, lo que reduce el tamaño del programa.
  • Un ensamblaje puede encapsular algún código y solo proporcionar las interfaces de acceso necesarias
  • Fácil de expandir

funcion amigo

  • El propósito de usar funciones amigas: permitir que algunas funciones establecidas accedan a datos privados o protegidos en la clase y realicen operaciones
  • Tres implementaciones de funciones amigas
    • Funciones globales como amigos
    • clase como amigo
    • función de miembro como amigo
  • Desventajas de las funciones amigas: Destruye las características de encapsulación de la clase. Después de ser declarado como amigo externamente, todos los detalles de la clase están abiertos al amigo.
//全局函数做友元函数
class House
{
    
    
//告诉编译器全局函数visit()是house类的友元函数,可以访问house对象的私有成员
friend void visit1(House *house);	
friend void visit2(House &house);
friend void visit3(House house);
publichouse()
	{
    
    
		bedroom = "卧室";
		kitchen = "厨房";
	}
	string bedroom;
private:
	string kitchen;
};

void visit1(House *house)		//地址传递
{
    
    
	cout<<"visiting(地址传递)"<<house->bedroom<<endl;
	cout<<"visiting(地址传递)"<<house->kitchen<<endl;
}
void visit2(House &house)		//引用传递
{
    
    
	cout<<"visiting(引用传递)"<<house.bedroom<<endl;
	cout<<"visiting(引用传递)"<<house.kitchen<<endl;
}
void visit3(House house)		//值传递
{
    
    
	cout<<"visiting(值传递)"<<house.bedroom<<endl;
	cout<<"visiting(值传递)"<<house.kitchen<<endl;
}

void test()
{
    
    
	House house;
	visit1(House &house);
	visit2(House house);
	visit3(House house);
}

int main()
{
    
    
	test();
}

//输出结果
visiting(地址传递)卧室
visiting(地址传递)厨房
visiting(引用传递)卧室
visiting(引用传递)厨房
visiting(值传递)卧室
visiting(值传递)厨房

heredar

La herencia se refiere a la tecnología de usar la definición de una clase existente como base para crear una nueva clase. La definición de una nueva clase puede agregar nuevos datos o nuevas funciones, y también puede usar las funciones de la clase principal, pero no puede heredar selectivamente la clase padre.

  • La clase derivada tiene las propiedades y los métodos no privados de la clase base (de hecho, los miembros privados de la clase base también se heredan y ocupan la memoria del objeto de la clase derivada, pero son invisibles y no se pueden usar en la clase derivada). clase)
  • Las clases derivadas pueden tener sus propias propiedades y métodos, y pueden extender la clase base
  • Las clases derivadas pueden implementar métodos de la clase base a su manera.

Una clase derivada puede heredar todos los métodos de la clase base, con las siguientes excepciones:

  • Constructores, destructores y destructores de copia de clase base
  • operador sobrecargado de clase base
  • función de amigo de clase base

Tres formas de herencia

  • método de herencia pública
    • Todos los miembros públicos de la clase base son propiedades públicas de la clase derivada.
    • Todos los miembros protegidos en la clase base son propiedades protegidas en la clase derivada
    • Todos los miembros privados en la clase base no se pueden usar en la clase derivada
  • herencia protegida
    • Todos los miembros públicos en la clase base son propiedades protegidas en la clase derivada
    • Todos los miembros protegidos en la clase base son propiedades protegidas en la clase derivada
    • Todos los miembros privados en la clase base no se pueden usar en la clase derivada
  • método de herencia privada
    • Todos los miembros públicos de la clase base son propiedades privadas de la clase derivada.
    • Todos los miembros protegidos en la clase base son propiedades privadas en la clase derivada
    • Todos los miembros privados en la clase base no se pueden usar en la clase derivada

De acuerdo con los tres métodos de herencia anteriores, se puede obtener que los derechos de acceso de los miembros de la clase base en las clases derivadas no deben ser más altos que los especificados en el método de herencia. El uso de la palabra clave using puede cambiar los derechos de acceso de los miembros de la clase base en las clases derivadas
. clases (solo se puede cambiar la clase base) acceso a miembros públicos y protegidos en el

//基类People
class People {
    
    
public:
    void show();
protected:
    char *m_name;
    int m_age;
};

//派生类Student
class Student : public People {
    
    
public:
    void learning();
public:
    using People::m_name;  //将protected改为public
    using People::m_age;  //将protected改为public
    float m_score;
private:
    using People::show;  //将public改为private
};

Constructores y Destructores

Constructor

El constructor se utiliza para inicializar los miembros de datos del objeto de clase, es decir, cuando se crea la instancia (objeto) de la clase, el sistema de compilación asigna espacio para el objeto y automáticamente llama al constructor para completar la inicialización de la clase. miembros

Características del constructor

  • Sin valor de retorno, no escriba void
  • El nombre de la función es el mismo que el nombre de la clase.
  • Puede tener parámetros, puede reproducirse (una clase puede tener múltiples constructores)
  • No es necesario llamar manualmente, el sistema llama automáticamente y solo llama una vez
  • Debe definirse en público para usar

Constructores de uso común

  • constructor sin argumentos
    • Si no se declara ningún constructor en la clase, el compilador está implícitamente predeterminado en línea con el constructor predeterminado, que generalmente no tiene parámetros, pero puede tener parámetros con valores predeterminados.
    • Si se declara un constructor en una clase, no se genera automáticamente un constructor predeterminado y los constructores declarados explícitamente tampoco pueden tener parámetros.
class Student {
    
    
public:
    int m_age;
    int m_score;
    //无参构造函数
    Student() {
    
    
        m_age = 18;
        m_score = 99;
    }
};
  • Si confía en el constructor implícito del sistema, debe asegurarse de que los miembros se inicialicen en la definición de clase; de ​​lo contrario, puede haber situaciones en las que la llamada genere valores basura, etc.
  • Se puede evitar que el compilador genere definiendo el constructor implícito como eliminado. Si algún miembro de la clase no es construible por defecto, el constructor por defecto generado por el compilador se define como eliminado
  • copiar constructor
    • Un constructor de copias inicializa un objeto copiando los valores de los miembros de un objeto del mismo tipo
    • Si no declara un constructor de copia, el compilador generará un constructor de copia para el miembro; si no declara un operador de asignación de copia, el compilador generará un operador de asignación de copia para el miembro
class Student {
    
    
public:
    int m_age;
    int m_score;
    //复制构造函数
    Student(Student& s) {
    
    
        m_age = s.m_age;
        m_score = s.m_score;
    }
};

Cuando hay miembros punteros en la clase, el constructor de copia creado por el sistema de forma predeterminada tendrá el riesgo de "copia superficial", por lo que el constructor de copia debe declararse explícitamente.

  • mover constructor
    • El constructor de movimientos puede realizar el movimiento del puntero y transferir el miembro del puntero de un objeto a otro miembro. Después de transferir el miembro del puntero, el puntero del objeto original generalmente se establece en NULL para evitar que se vuelva a utilizar.
    • Un constructor de movimiento es una implementación concreta de la semántica de movimiento.

Mover la semántica, lo que significa que los objetos de clase que contienen miembros de puntero se inicializan moviéndose en lugar de copiar en profundidad

class A {
    
    
public:
	int x;
    //构造函数
	A(int x) : x(x)
	{
    
    
		cout << "Constructor" << endl;
	}
 
    //拷贝构造函数
	A(A& a) : x(a.x)
	{
    
    
		cout << "Copy Constructor" << endl;
	}
 
    //移动构造函数
	A(A&& a) : x(a.x)
	{
    
    
		cout << "Move Constructor" << endl;
	}
};

incinerador de basuras

Un destructor es una función miembro que se llama automáticamente cuando un objeto queda fuera del alcance o se destruye explícitamente al llamar a delete. Si no se define un destructor, el compilador proporcionará un destructor predeterminado. Por lo general, los destructores personalizados solo son necesarios si la clase almacena un identificador para un recurso del sistema que debe liberarse o posee un puntero a la memoria a la que apunta.

Las características del destructor.

  • Sin valor de retorno, no escriba void
  • El nombre de la función es "~" + nombre de la clase
  • No puede tener parámetros, no se puede sobrecargar
  • El programa llama automáticamente al destructor antes de que el objeto sea destruido.
  • Debe definirse en público para usar
  • no pueden ser declarados constantes, volátiles o estáticos, pero pueden ser llamados para la destrucción de objetos declarados como tales
  • Se puede declarar como virtual. Usando un destructor virtual, los objetos pueden destruirse sin saber el tipo del objeto de clase

polimorfismo

El polimorfismo se refiere al uso de la misma interfaz para representar diferentes implementaciones

Por ejemplo, supongamos que hay tres clases: bicicleta (bicicleta), coche (coche), camión (camión), y hay tres implementaciones en cada una de las tres clases: bicicleta::viaje(), coche::correr(), truck:: launch(), las tres implementaciones tienen la misma función, para hacerlas arrancar. Si no hay polimorfismo, necesitamos usar estas tres implementaciones por separado

// 实现
Bicycle bicyle = new Bicycle();
Car car = new Car();
Truck truck = new Truck();

// 使用
bicyle.Ride();
car.Run();
truck.Launch();

Si se modifica la interfaz de una de las clases, por ejemplo, se modifica la interfaz del automóvil, también se debe cambiar el código de uso correspondiente, lo que afecta en gran medida la eficiencia del trabajo. Para mejorar la eficiencia del trabajo, podemos diseñar así:

  • Bicicleta, automóvil y camión son todos vehículos, por lo que puede crear una clase base de vehículos y bicicleta, automóvil y camión son sus clases derivadas.
  • La función de inicio run() se declara en el vehículo, y este método se reescribe en las tres clases derivadas
class Vehicle {
    
           // 新增抽象类
    virtual void Run() {
    
    }
};

class Bicycle: Vehicle {
    
    
    virtual void Run() {
    
    ......}
};
class Car: Vehicle{
    
    
    virtual voie Run() {
    
    ......}
};
class Truck: Vehicle {
    
    
    virtual void Run() {
    
    ......} 
};

// 实现部分
List<Vehicle> vehicles = {
    
      new Bicycle(),
                            new Car(),
                            new Truck()  };
// 使用部分
for (v : vechicles)
  v.Run();

De esta forma, los cambios de código en la parte de implementación no afectarán el código en la parte de uso.

Para dar otro ejemplo, por ejemplo, necesitamos verificar si la cuenta correspondiente a la tarjeta bancaria existe cuando depositamos y retiramos dinero en un cajero automático, pero no necesitamos verificar la contraseña al depositar, pero necesitamos verificar la contraseña al retirar dinero Para los mismos pasos de verificación, podemos usar Está unificado en una función check_in ()

class ATM
{
    
    
	void check_in(userid){
    
    ......}			//存款检查账户
	void check_in(userid,password){
    
    ......}	//取款检查账户
};

implementación polimórfica

Sobrecarga (polimorfismo en tiempo de compilación)

El polimorfismo en tiempo de compilación, también conocido como polimorfismo estático, se basa en la realización de programación de plantillas (una característica nueva de C++ 11) y la resolución de sobrecarga de funciones.Este polimorfismo se realiza en tiempo de compilación, por lo que se denomina compilación- polimorfismo de tiempo

La sobrecarga significa que el nombre de la función es el mismo, pero al menos uno de la cantidad de parámetros, tipos de parámetros o el orden de los parámetros de la función es diferente. Los valores de retorno de las funciones pueden ser iguales o diferentes. La sobrecarga de funciones ocurre dentro de una clase y no puede cruzar el ámbito

class Animal
{
    
    
public:
    void self_introduction(int tmp)
    {
    
    
        cout << "I'm an animal -" << tmp << endl;
    }
 
    void self_introduction(const char *s)//函数的重载
    {
    
    
        cout << "(overload)I'm an animal -" << s << endl;
    }
};

volver a escribir

El polimorfismo en tiempo de ejecución, también conocido como polimorfismo dinámico, implementa funciones polimórficas basadas en el mecanismo de función virtual y tiene el mismo nombre de función en diferentes clases con relaciones de herencia. Este método de implementación también se denomina reescritura.

La reescritura, también conocida como anulación, generalmente ocurre entre la clase derivada y la relación de herencia de la clase base.La clase derivada redefine la función virtual con el mismo nombre y parámetros en la clase base.

La forma en que C++ implementa la reescritura depende del compilador. Cuando el compilador instancia una clase con funciones virtuales, generará un puntero vptr, que apunta al encabezado de la tabla de funciones virtuales, y los punteros de función de las funciones virtuales se almacenan en la tabla de funciones virtuales en el orden de declaración. Si se anula en una clase derivada, en el espacio de memoria de la clase derivada

Reescribir requiere atención:

  • La función reescrita no puede ser estática, debe ser virtual
  • Las funciones anuladas deben tener el mismo tipo, nombre y lista de parámetros
  • Los modificadores de acceso de la función anulada pueden ser diferentes
class Animal
{
    
    
public:
    void self_introduction(int tmp)
    {
    
    
        cout << "I'm an animal -" << tmp << endl;
    }
};

class Fish :public Animal
{
    
    
public:
    void self_introduction(int tmp)		//函数的重写
    {
    
    
        cout << "(override)I'm an fish -" << tmp << endl;
    }

};

redefinir

Redefinición, también conocida como ocultación, la subclase redefine la función no virtual con el mismo nombre en la clase principal (la lista de parámetros puede ser diferente), y la función asignada a la clase derivada protege la función de la clase base con el mismo nombre, lo cual puede entenderse como en la relación de herencia Ocurrió sobrecarga

Si hay una función redefinida en una clase derivada, la clase ocultará el método de su clase principal. A menos que se convierta en la clase principal al llamar, de lo contrario, las llamadas de sobrecarga similares a la subclase y la clase principal no tendrán éxito.

El principio de implementación de la redefinición está relacionado con el método de búsqueda en el árbol de herencia. Buscará la función con el mismo nombre desde el alcance de la clase del objeto actual, si no hay función, buscará hasta la clase base, y no juzgará si la lista de parámetros es la misma

La redefinición necesita atención:

  • Si la función de la clase derivada tiene el mismo nombre que la función de la clase base, pero los parámetros son diferentes, en este momento, la función de la clase base está oculta independientemente de si es virtual o no.
  • Si la función de la clase derivada tiene el mismo nombre y los mismos parámetros que la función de la clase base, pero la función de la clase base no tiene la palabra clave virtual, en este momento, la función de la clase base está oculta (si hay es virtual, se reescribirá)
class Animal
{
    
    
public:
    void self_introduction(int tmp)
    {
    
    
        cout << "I'm an animal -" << tmp << endl;
    }
};

class Fish :public Animal
{
    
    
public:
    void self_introduction(char *s)		//函数的重定义
    {
    
    
        cout << "(override)I'm an fish -" << s << endl;
    }
};

Supongo que te gusta

Origin blog.csdn.net/qq_44184756/article/details/131339341
Recomendado
Clasificación