176-C + + important knowledge points 7

Posted by woza_uk on Fri, 28 Jan 2022 17:57:48 +0100

1. Slicing in C + +

class Object
{
	int value;
public:
	Object(int x = 0):value(x) {}
};
class Base : public Object
{
	int num;
public:
	Base(int x = 0):Object(x + 10),num(x) {}
};
int main()
{
	Object obja;
	Base base(10);
	return 0;
}

The slicing problem is an inheritance relationship, which must be public inheritance. The slicing problem exists only when the child object is assigned to the parent object. The obja object member has only one value, which is 4 bytes. For the base object, it inherits a hidden parent object, which is a total of 8 bytes (value and num). The value is 20 and the value of num is 10. The child object can be assigned to the parent object, However, the parent object cannot be assigned to the child object. When assigning a value, the value value of the child object is assigned to the value value of the parent object

2. What is the memory distribution of the following programs?

#define SEQ_INIT_SIZE 10
class SeqList
{
	int data[SEQ_INIT_SIZE];
	int maxsize;
	int cursize;
public:
	SeqList() :maxsize(SEQ_INIT_SIZE),cursize(0) {}
	~SeqList() {}
};
int main()
{
	SeqList seqa;
	return 0;
}

When entering the main function, space should be allocated to seqa and 48 bytes should be allocated directly

Write the default copy constructor and default assignment operator overload function of the above program

//copy constructor 
SeqList(const SeqList& src) :maxsize(src.maxsize), cursize(src.cursize)
{
    //memcpy(data,src.data,sizeof(data));//yes
    memcpy(data,src.data,sizeof(int) * cursize);//yes
}
//Assignment operator overloaded function
SeqList operator=(const SeqList& src) 
{
	if(this != &src)
	{
		maxsize = src.maxsize;
   		cursize = src.cursize;
   		//memcpy(data,src.data,sizeof(data));//yes
    	memcpy(data,src.data,sizeof(int) * cursize);//yes
	}	  
    return *this;
}

In order to make the copy constructor have good generality, a const is added, which can copy the structure with either ordinary objects or constant objects. In order to prevent dead recursion, the copy constructor must pass references

The initialization list can only appear in constructors and copy constructors, but not in other member functions. The reason is to prevent the repeated construction of objects, such as SeqList seqa;SeqList seqb; seqb = seqa; When the first two sentences are executed, SeqA and seqb have been constructed. If the overload function of the assignment operator has an initialization list, seqb will be constructed repeatedly. The object can only be constructed once, and the data members in the object can only be constructed once

3. What if you don't want to initialize another object with one object? Make the copy constructor private

If I design the main function as a friend function of a class, I can still initialize another object with one object, what can I do to prohibit it? After I design the copy constructor to be private, I don't want the function body, leaving only a declaration (seqlist (const seqlist & SEQ) 😉, The advantage of this is that only one declaration is given to tell the compiler that I have given the copy constructor, and you should not generate the copy constructor again. The absence of function body means that I prohibit all methods that can carry out copy construction. At this time, even friend functions can not carry out copy constructor, because there is no function body, in c11 standard, You can change the declaration to seqlist (const seqlist & SEQ) = delete; Tell the compiler that we will delete the copy constructor. I don't provide the copy constructor, and I don't want the compiler to provide you with the default copy constructor, so I can hinder the copy construction of objects

The same is true for other functions. If we don't want to assign a value to another object with an object, we will put the overloaded function of the assignment operator into private, leaving only one function body, that is, seqlist & operator = (const seqlist & SRC) = delete// C11 standard

If we define the constructor as private, there is no way for the outside world to construct the object directly. What should we do to make the outside world construct the object?

In general, destructors cannot be defined as private. If they are defined as private, once the object is created, there is no way to destroy it

4. What is the memory distribution of the following programs?

#define SEQ_INIT_SIZE 10
#define SEQ_INC_SIZE 2	
class Vector
{
	int* first;
	int* last;
	int* end;
public:
	Vector():_first(NULL),_last(NULL),_end(NULL)
	{
		_first = (int *)malloc(sizeof(int) * SEQ_INIT_SIZE);
		_last = _first;
		_end = _first + SEQ_INIT_SIZE;
	}
	~Vector()
	{
		free(_first);
		_first = _last = _end;
	}
};
int main()
{
	Vector vec;
	return 0;
}


vec._last - vec._first; Its difference is not only the number of elements, but also the current number of elements (cursize)
vec._end - vec._fist; Its difference is the size of the capacity, that is, maxsize

5. Write the default copy constructor and default assignment operator overload function of the following program

#define SEQ_INIT_SIZE 10
class SeqList
{
	int* data;
	int maxsize;
	int cursize;
public:
	SeqList() :data(NULL),maxsize(SEQ_INIT_SIZE),cursize(0)
	{
	 	data = (int *)malloc(sizeof(int) * maxsize);
	}
	~SeqList()
	{
		free(data);
		data = NULL;
	}
	//Default copy constructor
	SeqList(const SeqList& src):maxsize(src.maxsize),cursize(src.cursize)
	{
		data = seq.data;
	}
	//Default assignment operator overloaded function
	SeqList operator=(const SeqList& src)
	{
		if(this != &seq)
		{
			maxsize = src.maxsize;
			cursize = src.cursize;
			data = seq.data;
		}
		return *this;
	}
};
int main()
{
	SeqList seqa;
	SeqList seqb(seqa);
	
	return 0;
}

So what are the problems with the above program?
Since the data pointers of seqa and seqb point to a space in the heap area, when the second deconstruction is to deconstruct seqa, a space will be repeatedly deconstructed and the program will crash

If the main function is changed to the following program, what is the problem?

int main()
{
	SeqList seqa;
	SeqList seqb;
	seqb = seqa;
	return 0;
}

First construct the object seqa, and then construct the object seqb. At this time, seqa points to a heap space, and seqb points to a heap space. When seqb = seqa, seqb Data will point to seqa Data, resulting in the loss of heap space pointed to by seqb. â‘  it will cause memory leakage. Secondly, after seqb re destructs, seqa will be released repeatedly when seqa is destructed The space pointed to by data â‘¡ will cause the program to crash

The question arises: under what circumstances is the default construction and default assignment sufficient, and under what circumstances do you have to write your own copy constructor and assignment operator overloaded function?
In the process of class design, if there is a pointer (pointing to heap or file pointer) or design thread id number, you must override your copy construction and assignment operator overload function

So how to modify the copy structure of the above program?

	//copy constructor 
	SeqList(const SeqList& src):maxsize(src.maxsize),cursize(src.cursize)
	{
		data = (int *)malloc(sizeof(int) * src.maxsize);
		memcpy(data,src.data,sizeof(int) * src.cursize);
	}

If the above copy constructor is a deep copy, it will not cause the program to crash, SeqA Data points to a heap space, SeqA maxsize = 10,seqa.cursize = 0,seqb.data also points to a heap space, seqb maxsize = 10,seqb.cursize = 0. When destructing, seqb destructs the heap space it points to and SeqA destructs the heap space it points to. The two objects do not conflict with each other

So how to modify the assignment statement of the above program?

	//Assignment operator overloaded function
	SeqList operator=(const SeqList& src)
	{
		if(this != &seq)
		{
			maxsize = src.maxsize;
			cursize = src.cursize;
			//Release the original space first
			free(data);
			data = (int *)malloc(sizeof(int) * src.maxsize);
			memcpy(data,src.data,sizeof(int) * src.cursize);
		}
		return *this;
	}

The above assignment is a deep assignment, SeqA Data points to a heap space, SeqA maxsize = 10,seqa.cursize = 0,seqb.data points to another heap space, seqb maxsize = 10,seqa.cursize = 0, when seqb = seqa is executed; First, free the heap space pointed to by seqb, then re apply for a piece of heap space, and then copy the data of SeqA to seqb

6.String class

class String
{
private:
	char* str;
	String(char *p,int)
	{
		str = p;
	}
public:
	String(const char* p = NULL):str(NULL)
	{
		if(p != NULL)
		{
			str = new char[strlen(p) + 1];
			strcpy(str,p);
		}
		else
		{
			str = new char[1];//Why open up only one space instead of str = NULL;?
			*str = '\0'';
		}
	}
	~String()
	{
		if(str != NULL)
		{
			delete[] str;
		}
		str = NULL;
	}
	ostream& operator<<(ostream& out) const
	{
		if(str != NULL)
		{
			out << str;
		}
		return out;
	}
	String(const String& s)
	{
		//str = s.str;//error is a shallow copy. Both STRs point to the same space
		str =  new char[strlen(s.str) + 1];//Deep copy
		strcpy(str,s.str);
	}
	String& operator=(const String& s)
	{
		if(this != &s)
		{
			delete[] str;
			str = new char[strlen(s.str) + 1];
			strcpy(s,s.str);
		}
		return *this;
	}
	String operator+(const String& s) const
	{
		char *p = new char[strlen(this->str) + strlen(s.str) + 1];
		strcpy(p,this->str);
		strcat(p,s.str);
		return String(p,1);//To transfer the private constructor, you can solve the memory leak problem of p
	}
	String operator+(const char *p) const
	{
		char *p = new char[strlen(this->str) + strlen(s) + 1];
		strcpy(p,this->str);
		strcat(p,s);
		return String(p,1);//To transfer the private constructor, you can solve the memory leak problem of p
	}
};
ostream& operator<<(ostream& out,const String& s)
{
	s << out;
	return out;
}
String operator+(const char *p,const String& s)
{
	return String(p) + s;
}
int main()
{
	String s1("sunpeng");
	cout << s1 << endl;
	operator<<(cout,s1);
	String s2("hello");
	String s3;
	s3 = s1 + s2;
	s3 = s1 + "newdata";
	s3 = "newdata" + s1;
	return 0;
}

s1 << cout; Equivalent to S1 operator<<(cout); Equivalent to operator (& S1, cout);
Ostream & operator < < (ostream & out) const is equivalent to ostream & operator < < (const string * const this, ostream & out)

Ostream & operator < < (ostream & out) ostream & out in const formal parameter list must be a reference. The reason is that the copy constructor of cout is defined as a protection type, so cout cannot be used to initialize out. Because the copy constructor is a protection type, reference must be used. Reference does not build an object, and there is no need to transfer the copy constructor

The purpose of move construction and move assignment is to new and delete the heap area as little as possible

String(String&& s);
String& operator=(String&& s);

Lvalue: lvalue
xvalue: the value will be lost
rvalue: right value
prvalue: double right value

Generally, when the return value needs to continue to be processed, the reference is returned. The operators returned as reference by operator overload include: [], =, input > > output < < operator, self addition + + self subtraction – operator

Topics: C++