Sword refers to offer-1.1C++

sizeof empty class problem

Question : Define an empty type without any member variables or member functions. If sizeof is performed on this type, what is the result?

Answer : 1

Question : Why not 0?

Answer : An instance of an empty type does not contain any information. Originally, sizeof should be 0, but when we declare an instance of this type, it must occupy a certain space in the memory, otherwise these instances cannot be used. As for how much memory is occupied, it is decided by the compiler. Each instance of an empty type in Visual Studio takes up 1 byte of space.

Question : If you add a constructor and destructor to the type, and then perform sizeof on the type, what will be the result?

Answer : Same as before, still 1. To call the constructor and destructor, you only need to know the address of the function. The addresses of these functions are only related to the type and have nothing to do with the instance of the type. The compiler will not add anything extra to the instance because of these two functions. Information.

Question : What if the destructor is marked as a virtual function?

Answer : Once the C++ compiler discovers a virtual function in a type, it will generate a virtual function table for the type and add a pointer to the virtual function table in each instance of the type. On a 32-bit machine, a pointer occupies 4 bytes of space, so sizeof returns 4; if it is a 64-bit machine, a pointer occupies 8 bytes of space, so sizeof returns 8.

assignment operator

Question: The following is the declaration of type CMyString. Please add an assignment operator function to this type.

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

private:
   char* m_pData;
};

Check points:
1) Whether the type of the return value is declared as a reference of the type, and the reference of the instance itself is returned before the function ends (ie *this). Continuous assignments are only allowed if a reference is returned. Otherwise, if the return value of the function is void, continuous assignment will not be possible using this assignment operator. Suppose there are three CMyString objects: str1, str2 and str3. The statement str1=str2=str3 in the program will not be compiled.
2) Whether to declare the type of the incoming parameter as a constant reference. If the parameter passed in is not a reference but an instance, the copy constructor will be called once from the formal parameters to the actual parameters. Declaring parameters as references can avoid such unnecessary consumption and improve the efficiency of the code. At the same time, we will not change the state of the instance passed in within the assignment operator function, so the const keyword should be added to the reference parameter passed in.
3) Whether to release the existing memory of the instance itself. If we forget to free up our own space before allocating new memory, the program will leak memory.
4) Whether to determine whether the incoming parameter and the current instance ( *this) are the same instance. If it is the same, no assignment operation is performed and it is returned directly. If assignment is made without judgment in advance, serious problems will occur when the instance's own memory is released: when it *thisis the same instance as the passed-in parameter, then - once its own memory is released, the passed-in parameter's The memory is also released at the same time, so the content that needs to be assigned can no longer be found.

Classic solution - junior programmer

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

Solutions that consider exception safety - Senior Programmer

In the previous function, we used delete to release the memory of the instance m_pData before allocating the memory. If there is insufficient memory at this time and new char throws an exception, m_pData will be a null pointer, which can easily cause the program to crash. That is to say, once an exception is thrown inside the assignment operator function, the instance of CMyString no longer maintains a valid state, which violates the principle of Exception Safety.

To implement exception safety in assignment operator functions, we have two approaches. A simple way is to use new to allocate new content and then use delete to release the existing content. In this way, the original content is released only after the allocation of the content is successful. That is, we can ensure that the instance of CMyString will not be modified when the memory allocation fails. A better way is to create a temporary instance first, and then exchange the temporary instance with the original instance. The following is the reference code for this 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;
}

In this function, we first create a temporary instance strTemp, and then exchange strTemp.m_pData with the m_pData of the instance itself. Since strTemp is a local variable, but when the program runs outside the if, it will go out of the scope of the variable, and the destructor of strTemp will be automatically called to release the memory pointed to by strTemp.mpData. Since the memory pointed to by strTemp.m_pData is the memory of m_pData before the instance, this is equivalent to automatically calling the destructor to release the memory of the instance.

In the new code, we use new to allocate memory in the CMyString constructor. If an exception such as bad_alloc is thrown due to insufficient memory, we have not modified the state of the original instance, so the state of the instance is still valid, which ensures exception safety.

code example

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

Insert image description here

Guess you like

Origin blog.csdn.net/qq_36314864/article/details/132224202