Espada se refiere a oferta-1.1C++

problema de tamaño de clase vacía

Pregunta : Defina un tipo vacío sin variables miembro ni funciones miembro. Si se realiza sizeof en este tipo, ¿cuál es el resultado?

Respuesta : 1

Pregunta : ¿Por qué no 0?

Respuesta : Una instancia de un tipo vacío no contiene ninguna información, originalmente sizeof debería ser 0, pero cuando declaramos una instancia de este tipo debe ocupar un cierto espacio en la memoria, de lo contrario estas instancias no se pueden usar. En cuanto a cuánta memoria está ocupada, la decide el compilador. Cada instancia de un tipo vacío en Visual Studio ocupa 1 byte de espacio.

Pregunta : Si agrega un constructor y un destructor al tipo y luego realiza sizeof en el tipo, ¿cuál será el resultado?

Respuesta : Igual que antes, todavía 1. Para llamar al constructor y al destructor, solo necesita saber la dirección de la función. Las direcciones de estas funciones solo están relacionadas con el tipo y no tienen nada que ver con la instancia del tipo. El compilador no agregará nada adicional al ejemplo debido a estas dos funciones: Información.

Pregunta : ¿Qué pasa si el destructor está marcado como una función virtual?

Respuesta : Una vez que el compilador de C++ descubre una función virtual en un tipo, generará una tabla de funciones virtuales para el tipo y agregará un puntero a la tabla de funciones virtuales en cada instancia del tipo. En una máquina de 32 bits, un puntero ocupa 4 bytes de espacio, por lo que sizeof devuelve 4; si es una máquina de 64 bits, un puntero ocupa 8 bytes de espacio, por lo que sizeof devuelve 8.

operador de asignación

Pregunta: La siguiente es la declaración del tipo CMyString. Agregue una función de operador de asignación a este tipo.

class CMyString
{
    
    
public:
   CMyString(char* pData = NULL);
   CMyString(const CMyString& str);
   ~CMyString(void);

private:
   char* m_pData;
};

Puntos de verificación:
1) Si el tipo del valor de retorno se declara como una referencia del tipo y si la referencia de la instancia en sí se devuelve antes de que finalice la función (es decir *this). Las asignaciones continuas solo se permiten si se devuelve una referencia. De lo contrario, si el valor de retorno de la función es nulo, la asignación continua no será posible utilizando este operador de asignación. Supongamos que hay tres objetos CMyString: str1, str2 y str3. La declaración str1 = str2 = str3 en el programa no se compilará.
2) Si se declara el tipo de parámetro entrante como referencia constante. Si el parámetro pasado no es una referencia sino una instancia, se llamará al constructor de copia una vez desde los parámetros formales hasta los parámetros reales. Declarar parámetros como referencias puede evitar ese consumo innecesario y mejorar la eficiencia del código. Al mismo tiempo, no cambiaremos el estado de la instancia pasada dentro de la función del operador de asignación, por lo que la palabra clave const debe agregarse al parámetro de referencia pasado.
3) Si se debe liberar la memoria existente de la propia instancia. Si nos olvidamos de liberar nuestro propio espacio antes de asignar nueva memoria, el programa perderá memoria.
4) Si se debe determinar si el parámetro entrante y la instancia actual ( *this) son la misma instancia. Si es igual, no se realiza ninguna operación de asignación y se devuelve directamente. Si la asignación se realiza sin un juicio previo, se producirán problemas graves cuando se libere la propia memoria de la instancia: cuando es *thisla misma instancia que el parámetro pasado, entonces, una vez que se libere su propia memoria, la memoria del parámetro pasado También se lanza al mismo tiempo, por lo que ya no se puede encontrar el contenido que debe asignarse.

Solución clásica: programador junior

CMyString& CMyString::operator =(const CMyString &str)
{
    
    
     if(this == &str)
        return *this;
     delete []m_pData;
     m_pData = NULL;
     m_pData = new char[strlen(str.m_pData) + 1];
     strcpy(m_pData, str.m_pData);
     return *this;
}

Soluciones que consideran seguridad excepcional - Programador Senior

En la función anterior, usamos eliminar para liberar la memoria de la instancia m_pData antes de asignar la memoria. Si no hay memoria suficiente en este momento y el nuevo carácter genera una excepción, m_pData será un puntero nulo, lo que puede provocar que el programa se bloquee fácilmente. Es decir, una vez que se produce una excepción dentro de la función del operador de asignación, la instancia de CMyString ya no mantiene un estado válido, lo que viola el principio de seguridad de excepciones.

Para implementar seguridad de excepción en las funciones del operador de asignación, tenemos dos enfoques. Una forma sencilla es utilizar nuevo para asignar contenido nuevo y luego utilizar eliminar para liberar el contenido existente. De esta manera, el contenido original se publica solo después de que la asignación del contenido sea exitosa, es decir, cuando falla la asignación de memoria, podemos garantizar que la instancia de CMyString no se modificará. Una mejor manera es crear primero una instancia temporal y luego intercambiar la instancia temporal con la instancia original. El siguiente es el código de referencia para esta idea:

CMyString& CMyString::operator =(const CMyString &str)
{
    
    
    if(this != &str)
    {
    
    
        CMyString strTemp(str);
        char* pTemp = strTemp.m_pData;
        strTemp.m_pData = m_pData;
        m_pData = pTemp;
    }
    return *this;
}

En esta función, primero creamos una instancia temporal strTemp y luego intercambiamos strTemp.m_pData con el m_pData de la propia instancia. Dado que strTemp es una variable local, pero cuando el programa se ejecuta fuera de if, saldrá del alcance de la variable y se llamará automáticamente al destructor de strTemp para liberar la memoria señalada por strTemp.mpData. Dado que la memoria a la que apunta strTemp.m_pData es la memoria de m_pData antes de la instancia, esto equivale a llamar automáticamente al destructor para liberar la memoria de la instancia.

En el nuevo código, usamos new para asignar memoria en el constructor CMyString. Si se lanza una excepción como bad_alloc debido a memoria insuficiente, no hemos modificado el estado de la instancia original, por lo que el estado de la instancia sigue siendo válido, lo que garantiza la seguridad de las excepciones.

ejemplo de código

#include<cstring>
#include<cstdio>

class CMyString
{
    
    
public:
	CMyString(const char* pData = nullptr);
	CMyString(const CMyString& str);
	~CMyString(void);

	CMyString& operator = (const CMyString& str);

	void Print();

private:
	char* m_pData;
};

CMyString::CMyString(const char* pData)
{
    
    
	if (pData == nullptr)
	{
    
    
		m_pData = new char[1];
		m_pData[0] = '\0';
	}
	else
	{
    
    
		size_t length = strlen(pData);
		m_pData = new char[length + 1];
		strcpy(m_pData, pData);
	}
}

CMyString::CMyString(const CMyString& str)
{
    
    
	size_t length = strlen(str.m_pData);
	m_pData = new char[length + 1];
	strcpy(m_pData, str.m_pData);
}

CMyString::~CMyString()
{
    
    
	delete[] m_pData;
}
/*
//初级用法
CMyString& CMyString::operator = (const CMyString& str)
{
	if (this == &str)
		return *this;

	delete[]m_pData;
	m_pData = nullptr;

	m_pData = new char[strlen(str.m_pData) + 1];
	strcpy(m_pData, str.m_pData);

	return *this;
}
*/
//高级用法
CMyString& CMyString::operator =(const CMyString& str)
{
    
    
	if (this != &str)
	{
    
    
		CMyString strTemp(str);
		char* pTemp = strTemp.m_pData;
		strTemp.m_pData = m_pData;
		m_pData = pTemp;
	}
	return *this;
}

// ====================测试代码====================
void CMyString::Print()
{
    
    
	printf("%s", m_pData);
}

void Test1()
{
    
    
	printf("Test1 begins:\n");

	const char* text = "Hello world";

	CMyString str1(text);
	CMyString str2;
	str2 = str1;

	printf("The expected result is: %s.\n", text);

	printf("The actual result is: ");
	str2.Print();
	printf(".\n");
}

// 赋值给自己
void Test2()
{
    
    
	printf("Test2 begins:\n");

	const char* text = "Hello world";

	CMyString str1(text);
	str1 = str1;

	printf("The expected result is: %s.\n", text);

	printf("The actual result is: ");
	str1.Print();
	printf(".\n");
}

// 连续赋值
void Test3()
{
    
    
	printf("Test3 begins:\n");

	const char* text = "Hello world";

	CMyString str1(text);
	CMyString str2, str3;
	str3 = str2 = str1;

	printf("The expected result is: %s.\n", text);

	printf("The actual result is: ");
	str2.Print();
	printf(".\n");

	printf("The expected result is: %s.\n", text);

	printf("The actual result is: ");
	str3.Print();
	printf(".\n");
}

int main(int argc, char* argv[])
{
    
    
	Test1();
	Test2();
	Test3();

	return 0;
}

Insertar descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_36314864/article/details/132224202
Recomendado
Clasificación