[C++]: Classes and objects (1)

Friends, guys, we meet again. In this issue, I will explain to you the knowledge points about classes and objects in C++. If it has inspired you after reading it, please leave your comments. I wish you all the best. May all your wishes come true!

C language column: C language: from entry to proficiency

Data Structure Column: Data Structure

Personal homepage: stackY、

Table of contents

1. Preliminary understanding of process-oriented and object-oriented 

2. Introduction of classes

3. Class definition

3.1 Access qualifiers

3.2 Two ways of class definition:

3.3 Packaging

4. Class scope

5. Instantiation of classes

6. Class object model

6.1 How to calculate the size of a class object

6.2 Storage method of class objects

6.3 Structure memory alignment rules 

7.this pointer 

7.1 Introduction of this pointer

7.2Characteristics of this pointer

7.3. Comparison of Stack implementation in C language and C++


1. Preliminary understanding of process-oriented and object-oriented 

C language is process-oriented , focusing on the process , analyzing the steps to solve the problem, and gradually solving the problem through function calls.
C++ is based on object-oriented and focuses on objects . It splits one thing into different objects and relies on the interaction between objects.

2. Introduction of classes

Only variables can be defined in the C language structure. In C++, not only variables but also functions can be defined in the structure. For example: in the initial stage of data structure, when the stack was implemented in C language, only variables could be defined in the structure ; now when it is implemented in C++, you will find that functions can also be defined in the struct.
C language stage stack
typedef int DataType;
typedef struct Stack
{
	DataType* arry;
	size_t size;
	size_t capacity;
}ST;

void STInit(ST* pst)
{
	assert(pst);
	pst->arry = NULL;
	//top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
	//pst->top = -1     
	//top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
	pst->size = 0;
	pst->capacity = 0;
}

void STPush(ST* pst, DataType x)
{
	//...
}

void STPop(ST* pst)
{
	//...
}

int main()
{
	ST st;
	STInit(&st);
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPop(&st);
	return 0;
}

If written in C++, the stack interface can be defined directly inside the structure.

typedef int DataType;
struct Stack
{
	void STInit(struct Stack* pst)
	{
		assert(pst);
		pst->arry = NULL;
		//top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
		//pst->top = -1     
		//top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
		pst->size = 0;
		pst->capacity = 0;
	}

	void STPush(struct Stack* pst, DataType x)
	{
		//...
	}

	void STPop(struct Stack* pst)
	{
		//...
	}

	DataType* arry;
	size_t size;
	size_t capacity;
};


int main()
{
	struct Stack st;
	st.STInit(&st);
	st.STPush(&st, 1);
	st.STPush(&st, 2);
	st.STPush(&st, 3);
	st.STPop(&st);
	return 0;
}
For the definition of the structure above, it is preferred to use class instead of struct in C++ .

3. Class definition

class className
{
    // 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号
class is the keyword that defines the class , ClassName is the name of the class, and {} is the body of the class. Note that the semicolon cannot be omitted at the end of the class definition .
The contents in the class body are called members of the class: variables in the class are called attributes or member variables of the class ; functions in the class are called methods or member functions of the class .

 3.1 Access qualifiers

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.
[Access Qualifier Description]
1. Public modified members can be directly accessed outside the class
2. Protected and private modified members cannot be directly accessed outside the class ( protected and private are similar here )
3. The access permission scope starts from the position where the access qualifier appears until the next access qualifier appears.
4. If there is no access qualifier later, the scope will end at } , which is the class.
5. The default access rights of class are private and struct is public ( because struct must be compatible with C)
Note: Access qualifiers are only useful at compile time. When the data is mapped to memory, there is no difference in access qualifiers.

What is the difference between struct and class in C++?

C++ needs to be compatible with the C language, so struct in C++ can be used as a structure. In addition, struct in C++ can also be used
Define the class. It is the same as defining a class by class. The difference is that the default access permission of a class defined by struct is public , and the default access permission of a class defined by class is private . In inheritance and template parameter list positions, there are also differences between struct and class, and the order will be given later.
Home introduction.

3.2 Two ways of class definition:

1. All declarations and definitions are placed in the class body. Please note: if a member function is defined in a class , the compiler may treat it as an inline function .

class Person
{
public:
	//显示基本信息
	void ShowInfo()
	{
		cout << _name << "_" << _sex << "_" << _age << "_" << endl;
	}

//基本信息
public:
	char* _name;  //姓名
	int _age;     //年龄
	char* _sex;	  //性别
};
2. The class declaration is placed in the .h file, and the member function definition is placed in the .cpp file. Note: the class name needs to be added before the member function name:
//Person.h

class Person
{

//基本信息
public:
	char* _name;  //姓名
	int _age;     //年龄
	char* _sex;	  //性别
};


//Person.cpp

#include "Person.h"

//显示基本信息
void Person::ShowInfo()
{
	cout << _name << "_" << _sex << "_" << _age << "_" << endl;
}

We generally use the second method of writing declarations and definitions separately.

3.3 Packaging

In the class and object stage, we mainly study the encapsulation characteristics of classes. So what is encapsulation?

Encapsulation: Organically combine data and methods of operating 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 that makes it easier for users to use classes . For example: For a complex device like a computer, the only things provided to the user are the power on/off button, keyboard input, monitor, USB jack, etc., allowing the user to interact with the computer and complete daily tasks. But in fact, the real work of the computer is the CPU, graphics card, memory and other hardware components.
To implement encapsulation in the C++ language, data and methods for operating data can be organically combined through classes, and access rights can be used to hide the internal implementation details of objects and control which methods can be used directly outside the class .

4. Class scope

A class defines a new scope , and all members of the class are within the scope of the class . When defining members outside a class, you need to use the :: scope operator to indicate which class scope the member belongs to.
class Person
{
public:
	//显示基本信息
	void ShowInfo();
//基本信息
public:
	char* _name[20];  //姓名
	int _age;     //年龄
	char* _sex[10];	  //性别
};

//需要指定是哪个类域
void Person::ShowInfo()
{
	cout << _name << "_" << _sex << "_" << _age << "_" << endl;
}

5. Instantiation of classes

The process of creating an object from a class type is called instantiation of the class
1. A class describes an object . It is something like a model . It limits the members of the class. Defining a class does not allocate actual memory space to store it; for example: the student information form filled in when enrolling. The table can be regarded as a class to describe specific student information.
2. A class can instantiate multiple objects. The instantiated objects occupy actual physical space and store class member variables.
int main()
{
     Person._age = 100;   // 编译失败:error C2059: 语法错误:“.”
     return 0;
}
//Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄
3. Use an analogy. Instantiating objects from a class is like using architectural design drawings to build a house in reality. A class is like a design drawing . It only designs what is needed, but there is no physical building. Similarly, a class is just a design that is instantiated. Objects can actually store data and occupy physical space.
class Date
{
public:
	void Init(int year, int month, int day);

	void Print();

public:
	int _year;   // 声明
	int _month;
	int _day;
};

void Date::Init(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

int main()
{
	// 定义开空间
	Date d1;
	d1.Init(2023, 9, 30);
	d1.Print();

	//实例化多个对象
	Date d2;
	d2.Init(2023, 10, 1);
	d2.Print();

	return 0;
}

6. Class object model

6.1 How to calculate the size of class objects

class A
{
public:
    //成员函数
    void PrintA()
    {
       cout<<_a<<endl;
    }
private:
//    成员变量
    char _a;
};
Question: A class can have both member variables and member functions. So what does an object of a class contain? How to calculate the size of a class?

6.2 Storage method of class objects

Method 1: The object contains each member of the class

Defect: The member variables in each object are different, but the same function is called. If it is stored in this way, when one
When a class creates multiple objects, a copy of the code will be saved in each object. The same code is saved multiple times, which wastes space. So
How to solve it?
Method 2: Save only one copy of the code, and save the address of the code in the object
Method 3: Only save member variables, and store member functions in the public code section
So what method does the computer use to store it?
// 类中既有成员变量,又有成员函数
class A1 {
public:
	void f1() {}
private:
	int _a;
};
// 类中仅有成员函数
class A2 {
public:
	void f2() {}
};
// 类中什么都没有---空类
class A3
{};

int main()
{
	cout << sizeof(A1) << endl;
	cout << sizeof(A2) << endl;
//  分配1byte,不存储数据,只是占位,表示对象存在过
	cout << sizeof(A3) << endl;
	return 0;
}

in conclusion:

The size of a class is actually the sum of the "member variables" in the class. Of course, attention must be paid to memory alignment.
Pay attention to the size of the empty class. The empty class is special. The compiler gives the empty class one byte to uniquely identify the object of this class.

6.3 Structure memory alignment rules 

1. The first member is at the address offset 0 from the structure.
2. Other member variables should be aligned to an address that is an integer multiple of a certain number (alignment number).
Note: Alignment number = the smaller of the compiler's default alignment number and the size of the member.
The default number of alignments in VS is 8
3. The total size of the structure is: an integer multiple of the maximum alignment number (the largest of all variable types and the smallest default alignment parameter).
4. If a structure is nested and the nested structure is aligned to an integer multiple of its own maximum alignment number, the overall size of the structure is an integer of all maximum alignment numbers (including the alignment number of nested structures) times.

7.this pointer 

7.1 Introduction of this pointer

First define a date class:

class Date
{
public:
	//初始化
	void DateInit(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//打印
	void DatePrint()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1, d2;
	d1.DateInit(2023, 9, 30);
	d1.DatePrint();

	d2.DateInit(2023, 10, 1);
	d2.DatePrint();
	return 0;
}
For the above class, there is such a question:
There are two member functions, Init and Print, in the Date class. There is no distinction between different objects in the function body. So when d1 calls the Init function, how does the function know that it should set the d1 object instead of the d2 object?
C++ solves this problem by introducing the this pointer, that is: the C++ compiler adds a hidden pointer parameter to each "non-static member function", letting the pointer point to the current object (the object that calls the function when the function is running). 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, the compiler automatically completes it .

7.2Characteristics of this pointer

1. The type of this pointer: class type * const , that is, in member functions, this pointer cannot be assigned a value.

2. Can only be used inside a "member function".
class Date
{
public:
	// this在实参和形参位置不能显示写
	// 但是在类里面可以显示的用
	void DateInit(int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
3. The this pointer is essentially the formal parameter of the "member function" . When the object calls the member function, the object address is passed to the this parameter as an actual parameter. Therefore, the this pointer is not stored in the object .
4. The this pointer is the first implicit pointer parameter of the "member function". Generally, the compiler automatically passes it through the ecx register.
Passed, no need to be passed by the user.

Practice questions:

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

This code can run normally, because the Print function does not exist in the object, p->Print() does not dereference, it means to use p to find the corresponding function in the public code area, and here This pointer is p.

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:
    void PrintA() 
   {
        cout<<_a<<endl;
        //cout<<this->_a<<ednl; 
   }
private:
 int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}

 This code will crash when running, because p is a null when passing this pointer, and when printing, the this pointer needs to be dereferenced, so the null will be dereferenced, so the operation crashes.

 7.3 . Comparison of Stack implementation in C language and C++

C language implementation:

//对栈的初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	//top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
	//pst->top = -1     
	//top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
	pst->top = 0;
	pst->capacity = 0;
}

//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	//检测容量
	if (pst->top == pst->capacity)
	{
		int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		//当pst->a为NULL时执行的功能是和malloc一样
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = NewCapacity;
	}
	//入栈
	pst->a[pst->top] = x;
	pst->top++;
}

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	//判断栈是否为空
	assert(!StackEmpty(pst));
	//出栈
	pst->top--;
}

//获取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	//top指向的是栈顶的下一个位置的元素
	return pst->a[pst->top-1];
}

//获取栈中的有效元素的个数
int StackSize(Stack* pst)
{
	assert(pst);

	return pst->top;
}

//检测栈是否为空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/
	return pst->top == 0;
}

//销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);

	//释放
	free(pst->a);
	pst->a = NULL;
	//重置为0
	pst->top = pst->capacity = 0;
}
It can be seen that when implemented in C language, Stack related operation functions have the following common features:
        The first parameter of each function is Stack*
        The first parameter must be checked in the function because the parameter may be NULL
        In functions, the stack is manipulated through the Stack* parameter.
        The address of the Stack structure variable must be passed when calling
Only the structure for storing data can be defined in the structure. The method of operating data cannot be placed in the structure. That is, the data and the method of operating data are separated . Moreover, the implementation is quite complicated and involves a large number of pointer operations. If you are not careful, Something might go wrong.

C++ implementation (designed based on existing C++ knowledge)

//C++实现栈
typedef int DataType;
class Stack
{
public:
	//初始化
	void STInit()
	{
		_array = (DataType*)malloc(sizeof(DataType) * 4);
		if (_array == NULL)
		{
			perror("malloc fail!!!");
			exit(-1);
		}
		_size = 0;
		_capacity = 4;
	}

	//判空
	bool STEmpty()
	{
		return _size == 0;
	}

	//入栈
	void STPush(DataType x)
	{
		STCheck();
		//入栈
		_array[_size] = x;
		_size++;
	}

	//出栈
	void STPop()
	{
		assert(!STEmpty());
		_size--;
	}

	//数据个数
	int STSize()
	{
		return _size;
	}

	//栈顶元素
	int STTop()
	{
		assert(!STEmpty());
		return _array[_size - 1];
	}

	//销毁
	void STDestroy()
	{
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}

private:
	//检测
	void STCheck()
	{
		if (_size == _capacity)
		{
			int NewCapacity = 2 * _capacity;

			DataType* tmp = (DataType*)realloc(_array, sizeof(DataType) * NewCapacity);
			if (tmp == nullptr)
			{
				perror("realloc fail");
				exit(-1);
			}
			_array = tmp;
			_capacity = NewCapacity;
		}
	}
private:
	DataType* _array;
	int _size;
	int _capacity;
};

int main()
{
	Stack st;
	st.STInit();
	st.STPush(1);
	st.STPush(2);
	st.STPush(3);
	st.STPush(4);
	
	cout << st.STTop() << endl;
	cout << st.STSize() << endl;
	st.STPop();
	cout << st.STTop() << endl;
	return 0;
}
In C++, data and data manipulation methods can be perfectly combined through classes. Through access rights, you can control which methods can be called outside the class, that is, encapsulation . When used, it is just like using its own members, which is more in line with human understanding of a thing. knowledge of things. Moreover, each method does not need to pass the Stack* parameter . The parameter will be automatically restored after the compiler compiles. That is, the Stack* parameter in C++ is maintained by the compiler, but in C language it needs to be maintained by the user .

 

Friends and guys, good times are always short. Our sharing in this issue ends here. Don’t forget to leave your precious three pictures after reading it. Thank you all for your support!

Guess you like

Origin blog.csdn.net/Yikefore/article/details/133427075