On C + + object model

Posted by cauri on Thu, 04 Nov 2021 15:31:43 +0100

  1. Member variables and member functions are stored separately, which means that member functions do not strictly belong to classes. The structure in C language only stores variables. It is necessary to declare and define the implementation function outside the structure. C + + abstracts the idea of class and encapsulates member variables and member functions in a class. When writing C + + code, it seems that the two are indeed written together in a class, but in fact, member functions and member variables are stored separately. Member variables belong to a class, while member functions, like static member functions, are a piece of shared data. They do not belong to a class. Therefore, when different objects of the same class access the same member function, they actually access the same address space. Only non static member variables belong to objects.

The data and operations in the class are stored separately, and each non inline member function will only produce a function instance, that is, multiple objects of the same type will share a piece of code.

  1. The size of an empty class is 1. Because the class itself can also be accessed as an "instance object" (accessed directly through the class name), even if it is an empty class, it will maintain a char type pointer and maintain the address that can be accessed as an instance. The memory with the size of 1 represents the unique address of the instance abstracted by this class (similar to the baby to be born, he should also be given a unique ID card). If an int variable is added to the empty class, the int variable will find the memory address with the size of 1 and start storing data, so the memory with the size of 1 will be overwritten. Therefore, after adding an int variable, the size of the class is 4 instead of 5.

The concept of memory alignment will be introduced here. If the alignment method is not modified, the default is memory alignment. What does memory alignment mean? Next, in the above example, the size of the empty class is 1. After adding an int variable to the empty bit, the total size is 4. At this time, after adding a double variable, the size is not 12, but 16. Memory alignment is to take the maximum memory size in the variable as a benchmark value. For example, the double here is 8 and the benchmark value is 8. After adding an int to the empty class, the size is 4. For the benchmark value of 8, there is still (8 - 4) = 4 in one benchmark value. However, the remaining 4 can't fit a double, so you need to go to the next benchmark value to store the double. After adding double, it is 16 in size. The size of 16 is divided into two benchmark values. The first one is int+4 blanks, and the second one is to store double. And so on

  1. As mentioned earlier, the member functions of a class object share a piece of code. C + + distinguishes different objects by providing a special object pointer (this pointer).

The this pointer points to the object to which the called member function belongs.

#include <iostream>
using namespace std;
//Create a human
class Person {
public:
	void func() {}  //Member function
	string m_name;
	int age;
};

int main () {	
	Person p1;
	p1.func();//The compiler will secretly add a this pointer Person *this
	//In fact, void func (Person *this) is executed. This represents the object p1 to distinguish which object accesses the member function
	return 0;
}

In the above code: after creating a p1 object, when p1 calls the member function func, because there is actually only one member function func, the compiler will secretly pass in the p1 object with the help of this pointer, that is, void func (Person *this). Because it is called by p1 (p1.func();), this represents the p1 object. The same is true for other objects calling func. Although different objects share the same func function, relying on the this pointer allows func to distinguish which object is calling.

This pointer is a pointer implicit in the object member function (also in the construction copy destructor). When an object is created, each member function of the object contains an implicit this pointer automatically generated by the system to save the address of the object. That is, although we do not write this pointer in the implementation of member functions, the compiler will add it automatically at compile time. This pointer is called "pointer to this object" and always points to the current object. This pointer is implied in the non static member function of each class. The non static member function of the object does not belong to the object, so of course, this pointer does not belong to the object, so this pointer will not affect the result of sizeof (object).

This pointer is a mechanism of C + + implementation encapsulation. It connects the object with the member function calling the object. Externally, each object has its own member function. Generally, you don't need to write this pointer. The system will help you by default.

Because static member functions can only be accessed by static member variables, and static member functions share data, static member functions do not have this pointer.

  1. The this pointer points to the object ontology, so the dereference is equal to the object ontology.
class Person {
public:
	person(int age) {
		this->age = age;
	}
	int age;
public:
	Person& plusAge(Person &p) {
		this->age += p.age;
		return *this;
	}
};

int main() {
	Person p1(10);
	Person p2(10);
	p1.plusAge(p2).plusAge(p2);//Chain programming ideas can be linked all the time
	//The return value of plusAge is the object that calls it, so the return value of p1.plusAge(p2) is the p1 object after p2 age, so you can continue to call member functions.
	return 0;
}

Note: the return value of the plusAge member function is a reference, indicating that the object itself is returned. If the reference is removed, the value copy will be returned, that is, another nonexistent object with the same value as the object itself will be returned (the compiler automatically makes a shallow copy). If the reference is removed, the first call will add age to p1 successfully. However, since the p1 object itself is not returned after the first call is successful, the subsequent chain programming will not affect p1.
Whether the return value of a member function is referenced or non referenced depends on business needs.

  1. Null pointer access member function
class Person {
public:
	Person (int age) {
		this->m_age = age;
	} 
	void show1() {
		cout << "This function is not used this Pointer, so null pointers can also be accessed"<< endl;
	}
	void show2(int age) {
		if (this == NULL) return;//If you use this pointer, you need to make a judgment
		this->m_age = age;//Equivalent to null - > m-age = age; A non-existent address was accessed.
		cout << "This function uses this Pointer, so do safe handling"<< endl;
	}
	int m_age;
};

int main () {
	Person *p = NULL;
	p->show1(); //It can be accessed successfully. The this pointer is not used in show1
	p->show2(10); //If this pointer is used, the program will crash without security judgment
	return 0;
}

① If the member function does not use the this pointer, a null pointer can be used to the member function
② If the this pointer is used by a member function, the null pointer cannot access the member function (add an empty pointer to prevent this situation)

  1. Constant functions and constant objects

This pointer mentioned above, the reason why this pointer always points to its ontology object,
Because this is essentially a pointer constant Person * const this, the pointer cannot be changed.
That is, the this pointer is bound to the ontology object, and the pointer cannot be changed, but the variable of this object can be changed.

If you want the variable of the object pointed to by this pointer not to change, change it to const Person * const this
Since this pointer is automatically handled by the editor, we need to give the compiler a hint in the code, so we introduce the concept of constant function, that is, add const after the member function. When an object calls this member function, it will automatically add const modification to this pointer, and then pass it into the member function.

**Ordinary member function**
class Person {
public:
	void showAge() {
		//Ordinary member function
		this->m_age = 100;//The this pointer is a pointer constant and always points to p, but the member property of p can be changed.
	}
	int m_age;
};

int main() {
	Person p;
	p.showAge();//Here, the Person * const this pointer is passed in;
	//p.showAge(Person * const this);
	return 0;
}
**Constant function**
class Person {
public:
	void showAge() const {
		this->m_age = 100;//The operation is wrong
		//After const is added, the member function of showAge becomes a constant function. The properties of members cannot be modified in a constant function

		this->m_mutable_age = 100;//The mutable keyword can be modified
	}
	int m_age;
	mutable int m_mutable_age;
};

int main() {
	Person p;
	p.showAge();//Here, the const Person * const this pointer is passed in;
	//p.showAge(const Person * const this);
	return 0;
}
**A constant object can only call a constant function, because the member properties of a constant object cannot be changed.
However, ordinary member functions may modify member properties, while constant functions cannot modify member properties.
Therefore, ordinary objects and ordinary functions are used together.
const Person p;//Add const before the class name to become a constant object
p.m_age = 100; //Error operation. The member property of the constant object cannot be changed
p.m_mutable_age = 100;//Correct operation, the member property of a constant object cannot be changed unless the member property is mutable

Topics: C C++