C++ classes and objects - the basics of classes

Introduction of classes

In C language, only variables can be defined in a structure.
But in C++, a structure can not only define variables, but also functions.

The following is a structure in C++:

struct Stack
{
    
    
	void init(int capacity = 4)
	{
    
    
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
    
    
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}

	void destroy()
	{
    
    
		free(_a);
		_top = _capacity = 0;
		_a = nullptr;
	}

	int* _a;
	int _top;
	int _capacity;
};

It can be seen that in the structure in C++, there can be variables and functions.
C++ is compatible with the C language. The previous usage of the structure can still be used in C++, but in C++ structit is actually upgraded to a class.

In C++, prefer to use classthe alternativestruct


Class definition

The contents of the class become members of the class: variables in the class become member variables , and functions in the class are called member functions or class methods.

There are 2 ways to define a class:

It is worth noting that: the member variable itself can only be declared in the class and has no definition, the declaration of the member function must be in the class, and its definition can be outside the class or in the class

  1. The declaration and definition of members are in the class body. When a member function is defined in the class, the compiler may treat the member function as an inline function (if the member function is too long, the compiler will still treat the member function as an ordinary function. whether it is inlined or not depends on compilation)
class Stack
{
    
    
public:
	//成员函数在类中定义
	void init(int capacity = 4)
	{
    
    
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
    
    
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}

private:
	int* _a;
	int _top;
	int _capacity;
};
  1. You can also define member functions outside the class, and you need to use ::the scope operator to indicate which class the function belongs to.
class Stack
{
    
    
public:
	//成员函数在类外定义
	void init(int capacity);


private:
	int* _a;
	int _top;
	int _capacity;
};

void  Stack::init(int capacity)
{
    
    
	_a = (int*)malloc(sizeof(int) * capacity);
	if (_a == nullptr)
	{
    
    
		perror("malloc fail");
		return;
	}
	_capacity = capacity;
	_top = 0;
}

You need to use the scope operator because: when you define a class, the class defines a new scope.
There may be functions with the same name in different scopes, so when the function is defined outside the class, you must specify ::the function. Which category does it belong to?


Class Access Qualifiers and Encapsulation

The way C++ implements encapsulation: Use classes to combine the properties and methods of objects to make the objects more complete, and selectively
provide their interfaces to external users through access rights.

There are three access qualifiers: public public, private private, protectedprotected

  • publicModified members can be accessed directly outside the class
  • protectedand privatemodified members cannot be directly accessed outside the class
  • The scope of access rights starts from the position where the access qualifier appears until the next access qualifier appears. If there is no access qualifier behind, the scope ends at }, which is the end of the class.
  • The default access permission of class is private, and struct is public (because struct is compatible with C)

Under normal circumstances, when we define a class, we set the member function as public and the member variable as private. At this time, the member variable cannot be directly accessed outside the class, and the member variable can only be accessed in the member function by accessing the member function. Perform operations. This not only protects the member variables, but also uses member functions to ensure that the member variables perform a series of operations according to the ideas we have set.

This is also the basic idea of ​​encapsulation

The three major characteristics of object-oriented: encapsulation, inheritance, and polymorphism.

Encapsulation: organically combine the data and the method of manipulating the data, hide the properties and implementation details of the object, and only expose the interface to interact with the object.

Encapsulation is essentially a kind of management, which makes it easier for users to use classes
. Just like for a computer, we users don't need to care about how its internal components are arranged, and we don't need to care about how the CPU, GPU, memory, etc. work. Yes, the manufacturer has packaged them in a shell. For us, we can only operate the computer through the keyboard, mouse, and interface provided by the manufacturer.

The same is true for encapsulation in C++. Users don't have to manage the internal logic of member functions, nor can they manipulate member variables. Users only need to be able to call member functions.

Organically combine data and methods of manipulating data through classes, hide internal implementation details of objects through access rights, and control which methods can be used directly outside the class.


instantiation of objects

The process of creating an object from a class is called instantiation of the class

For variables, the difference between declaration and definition is whether to open up memory space, so the member variables in the class definition are declared rather than defined.

Instantiation of class:

class Stack
{
    
    
public:
	void init(int capacity);
private:
	int* _a;
	int _top;
	int _capacity;
};

void  Stack::init(int capacity)
{
    
    
	_a = (int*)malloc(sizeof(int) * capacity);
	if (_a == nullptr)
	{
    
    
		perror("malloc fail");
		return;
	}
	_capacity = capacity;
	_top = 0;
}


int main()
{
    
    
	//Stack类的实例化
	Stack s1;
	Stack s2;
}

A class has no space, only the objects it instantiates have physical space

The relationship between class objects is like the relationship between drawings and real objects. We can create many same objects according to the same drawing. These objects have their own volume, while drawings do not.


class object size

As mentioned before, a class has no size, only the objects it instantiates have sizes. So how to calculate the size of its objects?

In fact, in the storage space of class members, only member variables are stored, and member functions are placed in the public code section.

This is because the class instantiates different objects, and the member variables of these objects are different, so the member variables of each object must be stored separately, and the member functions of different objects are the same, but the values ​​​​of the passed parameters are different. , there is no need to store member functions separately. So put all member functions in the public code section
insert image description here

Assume that all members in class A are public. When A instantiates an object a, a.print()it actually searches for it in the public space or a.numinside the object.

Therefore, the size of an object is calculated by calculating the "sum" of the sizes of its member variables. Pay attention to the memory alignment principle here.

The memory alignment principle here is the same as the structure memory alignment. For the specific content of memory alignment, please refer to: Structure Memory Alignment

Pay attention to the size of the empty class. An empty class refers to a class that has no member variables and only member functions.
For an empty class, the compiler gives the empty class a byte to uniquely identify the object of this class. It is to occupy a place, indicating that the object exists.
insert image description here


this pointer

class Date
{
    
    
public:
	void Init(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}

   
	
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	d1.print(&d1)
	d2.Print();
	return 0;
}

Let’s think about a problem first. A class instantiates two objects. When accessing the same member function through the two objects, we know that the member function is stored in the public code segment. There is no distinction between different objects in the function body, so d1 calls
Init function, how does the function know that it should set the d1 object instead of the d2 object?

In C++, this problem is solved by introducing the this pointer
: the C++ compiler adds a hidden pointer parameter to each "non-static member function", so that the pointer points to the current object (the object that calls the function when the function is running), in All operations on "member variables" in the function body are accessed through this pointer. It's just that all operations are transparent to the user, that is, the user does not need to pass it, and the compiler completes it automatically.

Therefore, although different objects call the same function, the formal parameters in the function are different.

Taking Printa function as an example, the compiler will handle the this pointer hidden in the member function.
insert image description here

Calling member functions will also be automatically handled by the compiler:
insert image description here

When calling the Init function through d1, d1.Init(2022, 1, 11)4 parameters are actually passed:d1.Init(&d1,2022, 1, 11)

It should be noted that in C++, the this pointer is not allowed to be used explicitly in actual parameters or formal parameters, but it is allowed to be used in functions.
insert image description here

  • The type of this pointer is actually: class type * const this, here constis to modify the this pointer itself, so in the member function, you cannot assign a value to this, and you cannot modify this
  • The this pointer can only be used in member functions
  • The this pointer is a hidden formal parameter. When calling a member function, the object address is passed to this as an actual parameter.
  • As a formal parameter, this is stored in the stack area, not in the object. After the function ends, the this pointer is automatically destroyed.
  • The this pointer is the first implicit pointer parameter of the "member function". Generally, it is automatically passed by the compiler through the ecx register and does not need to be passed by the user.

Analyze whether the following code can run correctly:

class A
{
    
    
public:
	void PrintA()
	{
    
    
		cout << "Print()" << endl;
	}
private:
	int _a;
};
int main()
{
    
    
	A* p = nullptr;
	p->PrintA();
	return 0;
}

p->PrintA(), no dereference error will occur pwhen calling , because it is a member function, which is stored in the public code section and not in the object.PrintA()PrintA()

Here, the pointer p of type A is set as a null pointer, and it is accessed through p. PrintA()Here, it is actually ppassed as an actual parameter to the formal parameter PrintA()in it, so the pointer in the member function is a null pointer, but in this function there is no Dereference this pointer, so this function can run normallythisPrintA()this

Analyzing the following code:

class A
{
    
    
public:
	void PrintA()
	{
    
    
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
    
    
	A* p = nullptr;
	p->PrintA();
	return 0;
}

Similar to the above code, p is passed as an actual parameter to the formal parameter in the member function, but in this function, the pointer cout << _a << endlis actually dereferenced cout << this->_a << endlhere , so this code will go wrong.this


Guess you like

Origin blog.csdn.net/weixin_64116522/article/details/131784020