C++ classes and objects (1)


foreword

This chapter will officially start C++a series of explanations, and this article will give a preliminary understanding of classes and objects

1. From process-oriented to object-oriented

  • C语言It is process-oriented, focusing on the process , analyzing the steps to solve the problem , and solving the problem step by step through function calls
  • C++It is based on object-oriented, focusing on objects , splitting one thing into different objects , and relying on the interaction between objects to complete.

For example: To do the laundry

C语言:
insert image description here
C++:

  1. Four objects: people, clothes, washing powder, washing machine
  2. The process of washing clothes: the person puts the clothes into the washing machine, pours the washing powder, starts the washing machine, the washing machine will wash the clothes and spin dry
  3. The whole process is completed by the interaction between the four objects of people, clothes, washing powder, and washing machine . People don't need to care about how the washing machine washes clothes and how it dries them.
    insert image description here

2. The introduction of the class

C语言In , only variables can be defined in the structure . In , not only variables can be definedC++ in the structure , but also functions can be defined .

When we used to implement the data structure - stack in the past C语言, we defined it like this:

//C语言
typedef int dataOfStackType;

typedef struct stack
{
    
    
	dataOfStackType* a;
	int top;
	int capacity;
}stack;

void StackInit(stack* ps);

void StackPush(stack* ps, dataOfStackType data);

void StackPop(stack* ps);

//...

Whereas in C++, we can define it like this:

//C++
typedef int dataOfStackType;

typedef struct stack
{
    
    
	void StackInit(stack* ps);

	void StackPush(stack* ps, dataOfStackType data);

	void StackPop(stack* ps);
//...

	dataOfStackType* a;
	int top;
	int capacity;
}stack;

Like the definition above, C++中prefer to use a new name - classinstead struct:

class stack
{
    
    
	void StackInit(stack* ps);

	void StackPush(stack* ps, dataOfStackType data);

	void StackPop(stack* ps);
//...

	dataOfStackType* a;
	int top;
	int capacity;
};

3. Class definition

1. What is a class

class className
{
    
    
	//...类的主体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

As shown above, the definition of a class is very similar to the definition of a struct:

  1. class: A keyword that defines a class
  2. class Name: the name of the class
  3. The main body of the class: {}the middle is the main body of the class, which consists of member variables and member functions
  4. Member variable: also known as the attribute of the class, refers to the variable defined in the class
  5. Member function: also known as class method, refers to the function defined in the class

2. Class definition

There are generally two ways to define a class:

  1. The declaration and definition are all placed in the class body. Note: If the member function is defined in the class, the compiler may treat it as an inline function .
//日期类
class Date
{
    
    
	void Init(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	int _year;
	int _month;
	int _day;
};
  1. 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 ::.
//Date.h文件中声明类
//日期类
class Date
{
    
    
public:

	void Init(int year, int month, int day);
	void Print();
	
	int _year;
	int _month;
	int _day;
};
//Date.c文件中定义成员函数
#include"Date.h"

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

In general, the second approach is more desirable.

3. Suggestions for member variable naming rules

Let's look at the following class:

class Date
{
    
    
public:
	void Init(int year)
 	{
    
    
 		//这里的year到底是成员变量,还是函数形参?
 		year = year;
 	}
private:
 	int year;
};

Are the variables above awkward? Are they member variables or function parameters? In order to avoid confusion, we recommend naming member variables as follows Init函数:year

class Date
{
    
    
public:
 	void Init(int year)
 	{
    
    
 		_year = year;
 	}
private:
 	int _year;
};

// 或者这样
class Date
{
    
    
public:
 	void Init(int year)
 	{
    
    
 		mYear = year;
 	}
private:
 	int mYear;
};

Other methods are also possible, depending on the company's requirements. Generally, it is enough to add a prefix or suffix to identify the difference.

4. Class access qualifier and encapsulation

1. Access qualifiers

We define various member functions and member variables in the class. Sometimes, we don't want others to access some members of the class casually, such as member variables, but other members are open to the outside world, such as member functions. Then we need to use access qualifiers to modify these members .

There are three access qualifiers: public, protected,private

insert image description here
Access qualifier description :

  1. classThe default access permissions are private, structfor public(because of structcompatibility C)
  2. publicModified members can be directly accessed outside the class
  3. protectedand modified members cannot be directly accessedprivate outside the class (here and are similar)protectedprivate
  4. Access scopes start at the occurrence of this access qualifier until the next occurrence of the access qualifier
  5. If there is no access qualifier behind, the scope ends at the }end of the class

Note: Access qualifiers are only useful at compile time , when the data is mapped to memory, there is no difference in access qualifiers.

So the question is: what is the difference between c++neutralization structand neutralization?class

Answer: C++It needs to be compatible C语言, so not C++only structcan it be used as a structure , C++but structit can also be used to define a class , which classis the same as defining a class. The difference is that the default access permissionstruct of the defined class is , and the default access permission of the defined class is .publicclassprivate

2. Encapsulation

Object-oriented has three characteristics: encapsulation, inheritance, and polymorphism.

At the current stage, we only learn the features of the package. So what is encapsulation?

Encapsulation: Organically combine data and methods of manipulating data , hide object properties and implementation details, and only expose interfaces to interact with objects

Encapsulation is essentially a kind of management, making it easier for users to use classes :

  • For example: for such a complex device as a computer, the only thing provided to the user is the power button, keyboard input, monitor, USBjack, etc., allowing the user to interact with the computer and complete daily affairs. But in fact, the real work of the computer is some hardware components such as CPU, graphics card, and memory.
  • For computer users, they don’t need to care about internal core components , such as how the circuits on the motherboard are laid out, how the CPU is designed inside, etc. Users only need to know how to turn on the computer and how to interact with the computer through the keyboard and mouse . Therefore, when the computer manufacturer leaves the factory, it puts a shell on the outside to hide the internal implementation details, and only provides external switches, mouse and keyboard jacks, etc., so that users can interact with the computer .
  • Encapsulation in C++the language can organically combine data and methods of manipulating data through classes, hide the internal implementation details of objects through access rights, and control which methods can be used directly outside the class .

Five. The scope of the class

A class defines a new scope , and all members of the class are in the scope of the class. When defining members outside the class, you need to use ::the scope operator to indicate which class domain the member belongs to :

class Person
{
    
    
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
    
    
	cout << _name << " "<< _gender << " " << _age << endl;
}

Six. Class instantiation

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

//类的声明
class Date
{
    
    
public:
	//...
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	//类的实例化对象
	Date d1;
	Date d2;
	Date d3;
	return 0;
}

Class instantiation of objects is like building a house using design drawings in reality . Classes are like design drawings . Only what is needed is designed, but there is no physical building. The instantiated object is the real house that can actually store data and occupy physical space.
Like a design drawing, it is not that a design drawing can only build one house, and a class can also instantiate multiple objects.

Seven. Class object model

A class can have both member variables and member functions, so how to calculate the size of a class object?

As mentioned above, C++classes and structures are essentially the same. In order to be compatible C语言, the calculation rules of the size of the structureC++ will be followed , and the class is the same as the structure, so the class will continue to use the same calculation rules as the structure .

Note: Member functions are in the public code segment , so member functions do not occupy space . Therefore, the size of a class is actually the size of the member variables in the class, following the rules of memory alignment.

Let's verify it:

// 类中既有成员变量,又有成员函数
class A1 {
    
    
public:
	void f1() {
    
    }
private:
	int _a;
	char _b;
};

// 类中仅有成员函数
class A2 {
    
    
public:
	void f2() {
    
    }
};

// 类中什么都没有---空类
class A3
{
    
    };

int main()
{
    
    
	cout << sizeof(A1) << endl;
	cout << sizeof(A2) << endl;
	cout << sizeof(A3) << endl;

	return 0;
}

operation result:

insert image description here

As shown in the figure above, the result is consistent with the conclusion: the size of the class only calculates the size of the member variables in the class, and follows the memory alignment rules .

  • So why is the size of the empty class 1?
    This is because the empty class is special, and the compiler gives the empty class a byte to uniquely identify objects of this class .

Supplement: Structure memory alignment rules :

  1. The first member is at the address of the structure at offset .0
  2. Other member variables should be aligned to an address that is an integer multiple of a certain number (alignment number) . Note: Alignment = the smaller value between the compiler's default alignment and the member size . The default alignment number 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, the nested structure is aligned to an integer multiple of its own maximum alignment , and the overall size of the structure is an integer multiple of all maximum alignments (including the alignment of the nested structure) .

Eight. this pointer

1. Know the this pointer

Let's define a date class first Date:

//日期类
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();
	d2.Print();
	return 0;
}

For the above operation, there is such a question:

  • DateThere are two member functions Initand in the class , and there is no distinction between different objects in the function body , so when the function is called , how does the function know that the object should be set instead of the object?Printd1Initd1d2

C++this指针Solve this problem by introducing :

C++The compiler adds a hidden pointer parameter to each non-static member function , making the pointer point to the current object (the object that calls the function when the function is running), and all member variable operations 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 .

2. Characteristics of this pointer

insert image description here
thisPointers have the following properties :

  1. thisThe type of the pointer: the type * const, that is, in the member function, cannot assign thisa value to the pointer
void Init(int year, int month, int day)
{
    
    
	//错误示例
	this=nullptr;
}
  1. Can only be used inside member functions
  2. thisA pointer is essentially a formal parameter of a member function . When an object calls a member function, the address of the object is passed to the formal parameter as an actual thisparameter . So no pointer is stored in the objectthis .
  3. thisThe pointer is the first implicit pointer parameter of the member function . Generally, it is automatically passed by the compiler through ecxthe register and does not need to be passed by the user.

With thispointers, the date class we implemented above can also be implemented like this:

//日期类
class Date
{
    
    
public:
	void Init(int year, int month, int day)
	{
    
    
			this->_year = year;
			this->_month = month;
			this->_day = day;
	}
	void Print()
	{
    
    
		cout <<this-> _year << "-" <<this-> _month << "-" <<this-> _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

But there is no need to write this in the process of actually writing code, because the compiler has already done it for us, so we don't want to do thankless things.

【Interview questions】

  • thisWhere do pointers exist ?
    Answer: The this pointer is a formal parameter of a member function, so there is a stack area .
  • thisCan a pointer be null ?
    Look at the following two pieces of code:
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
    
    
public:
	void Print()
	{
    
    
		cout << "Print()" << endl;
	}
private:
	int _a;
};

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

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

Choose C for the first course and B for the second course, because:

  • PCall Print, will not be dereferenced, because the address Printof is not in the object , Ppassed as an argument to thisthe pointer
  • Program 1: thisThe pointer is null, but the function does not dereference thisthe pointer
  • The second program: thisthe pointer is empty, but access within the function _a, the essence is this->_a, that is, dereferencing the null pointer, and an operation error will occur.

Nine. Comparison of C language and C++ implementation stack

1. C language implementation

typedef int DataType;
typedef struct Stack
{
    
    
	DataType* array;
	int capacity;
	int size;
}Stack;

void StackInit(Stack* ps)
{
    
    
	assert(ps);
	ps->array = (DataType*)malloc(sizeof(DataType) * 3);
	if (NULL == ps->array)
	{
    
    
		assert(0);
		return;
	}
	ps->capacity = 3;
	ps->size = 0;
}

void StackDestroy(Stack* ps)
{
    
    
	assert(ps);
	if (ps->array)
	{
    
    
		free(ps->array);
		ps->array = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

void CheckCapacity(Stack* ps)
{
    
    
	if (ps->size == ps->capacity)
	{
    
    
		int newcapacity = ps->capacity * 2;
		DataType* temp = (DataType*)realloc(ps->array,
			newcapacity * sizeof(DataType));
		if (temp == NULL)
		{
    
    
			perror("realloc申请空间失败!!!");
			return;
		}
		ps->array = temp;
		ps->capacity = newcapacity;
	}
}

void StackPush(Stack* ps, DataType data)
{
    
    
	assert(ps);
	CheckCapacity(ps);
	ps->array[ps->size] = data;
	ps->size++;
}

int StackEmpty(Stack* ps)
{
    
    
	assert(ps);
	return 0 == ps->size;
}

void StackPop(Stack* ps)
{
    
    
	if (StackEmpty(ps))
		return;
	ps->size--;
}

DataType StackTop(Stack* ps)
{
    
    
	assert(!StackEmpty(ps));
	return ps->array[ps->size - 1];
}

int StackSize(Stack* ps)
{
    
    
	assert(ps);
	return ps->size;
}

int main()
{
    
    
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);
	StackPush(&s, 4);
	printf("%d\n", StackTop(&s));
	printf("%d\n", StackSize(&s));
	StackPop(&s);
	StackPop(&s);
	printf("%d\n", StackTop(&s));
	printf("%d\n", StackSize(&s));
	StackDestroy(&s);
	return 0;
}

It can be seen that when implemented in C language, Stack-related operation functions have the following commonality:

  1. The first parameter of each function isStack*
  2. The first parameter must be detected in the function , because the parameter may beNULL
  3. In the function, Stack*the stack is manipulated through parameters.
  4. The address ofStack the structure variable must be passed when calling

Only the structure for storing data can be defined in the structure , and the method of operating data cannot be placed in the structure, that is, the data and the way of operating data are separated , and the implementation is a bit more complicated, involving a large number of pointer operations, and errors may occur if you are not careful.

2. C++ implementation

typedef int DataType;
class Stack
{
    
    
public:
	void Init()
	{
    
    
		_array = (DataType*)malloc(sizeof(DataType) * 3);
		if (NULL == _array)
		{
    
    
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = 3;
		_size = 0;
	}
	void Push(DataType data)
	{
    
    
		CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	void Pop()
	{
    
    
		if (Empty())
			return;
		_size--;
	}
	DataType Top() {
    
     return _array[_size - 1]; }
	int Empty() {
    
     return 0 == _size; }
	int Size() {
    
     return _size; }
	void Destroy()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	void CheckCapacity()
	{
    
    
		if (_size == _capacity)
		{
    
    
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity *
				sizeof(DataType));
			if (temp == NULL)
			{
    
    
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
int main()
{
    
    
	Stack s;
	s.Init();
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);
	printf("%d\n", s.Top());
	printf("%d\n", s.Size());
	s.Pop();
	s.Pop();
	printf("%d\n", s.Top());
	printf("%d\n", s.Size());
	s.Destroy();
	return 0;
}

C++Classes can be used to perfectly combine data and methods of manipulating data , and access rights can be used to control those methods that can be called outside the class , that is, encapsulation , which is more in line with human cognition of a thing, just like using its own members . Moreover, each method does not need to pass parameters , and the parameters will be automatically restored after the compiler compiles, that is, the parameters are maintained by the compiler, and the parameters need to be maintained by the user.Stack*C++Stack *C语言


This is the end of this article, the code text is not easy, please support me a lot!

Guess you like

Origin blog.csdn.net/weixin_67401157/article/details/130302091