[C++] Classes and Objects (Part 1)

Table of contents

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

2. Introduction of classes

3. Class definition

4. Class access qualifiers and encapsulation

4.1 Access qualifiers

4.2 Packaging

5. Scope of class

6. Instantiation of classes

7. Class object model

7.1 How to calculate the size of a class object

7.2 Guessing the storage method of class objects

7.3 Structure memory alignment rules

8. this pointer

8.1 Introduction of this pointer

8.2 Characteristics of this pointer

8.3 Comparison of Stack implementation in C language and C++


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

C language isprocess-oriented, focused It is a process that analyzes the steps to solve the problem and solves the problem step by step through function calls.

C++ isbased on object-oriented and focused on It isobject, which splits one thing into different objects and is completed by 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, the stack was implemented in C language. Only variables can be defined in the structure. Now it is implemented in C++ implementation, will find that functions can also be defined in struct.

typedef int DataType;
struct Stack
{
	void Init(size_t capacity)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}

		_capacity = capacity;
		_size = 0;
	}

	void Push(const DataType& data)
	{
		// 扩容
		_array[_size] = data;
		++_size;
	}

	DataType Top()
	{
		return _array[_size - 1];
	}

	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}

	DataType* _array;
	size_t _capacity;
	size_t _size;
};

int main()
{
	Stack s;
	s.Init(10);
	s.Push(1);
	s.Push(2);
	s.Push(3);
	cout << s.Top() << endl;
	s.Destroy();
	return 0;
}

For the definition of the above structure, prefers to use class instead of in C++.

3. Class definition

class className
{
	// 类体:由成员函数和成员变量组成

}; // 一定要注意后面的分号

class is the keyword that defines the class, ClassName is the name of the class, {} is the main body of the class, please note The semicolon after the end of the class definition cannot be omitted.

The contents of the class body are calledmembers of the class: variables in the class is called attribute of class or member variable; functionsin a class are called methods of the classor Member functions.

Two ways to define classes:

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

        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::

Under normal circumstances, the second method is more expected. Note: In order to facilitate the demonstration of using method 1 to define classes in class, everyone should try to use the second method in subsequent work.

Suggestions for member variable naming rules:

// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
	void Init(int year)
	{
		// 这里的year到底是成员变量,还是函数形参?
		year = year;
	}
private:
	int 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;
};

// 其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行。

4. Class access qualifiers and encapsulation

4.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 the outside through access rights. User uses.

[Access Qualifier Description]

  1. Members modified by public 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. Access rightsThe scope starts from the position where this access qualifier occurs until the next access qualifier occurs
  4. If there is no access qualifier afterwards, the scope reaches } The class ends
  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.

[Interview questions]

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

Answer: C++ needs to be compatible with C language, so struct in C++ can be used as a structure. In addition, struct in C++ can also be used to define classes. It is the same as class defining a 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. Note: There are also differences between struct and class in inheritance and template parameter list positions, which will be introduced later.

4.2 Packaging

[Interview questions]

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

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.

For computer users, there is no need to worry about the internal core components, such as how the circuits on the motherboard are laid out, how the CPU is designed internally, etc. Users only need to know how to turn on the computer and how to communicate with the computer through the keyboard and mouse. Just interact. ThereforeWhen computer manufacturers leave the factory, they put a shell on the outside to hide the internal implementation details, and only provide power switches, mouse and keyboard jacks to the outside so that users can interact with the computer. Just.

To implement encapsulation in C++ language, you can organically combine data and methods of operating data through classes, hide the internal implementation details of objects through access permissions, and control which methods can be used in Used directly outside the class.

5. Scope of class

The class defines a new scope, and all members of the class are in 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 PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};

// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << _age << endl;
}

6. 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 and is a model< a i=4>The same thing, it limits the members of a class. Defining a classdoes not allocate actual memory space to store it; for example: The student information form filled in when enrolling can be regarded as a class to describe specific student information.

        The class is like a riddle, describing the answer, and the answer is an instance of the riddle.

        Riddle: & quot; I am not old, with a beard, the owner is here, so I call my mother & quot; Mystery: goat.

        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;
}

        The Person class has no space. Only objects instantiated by the Person class have specific ages.

        3. Make an example. instantiating objects from a class is like using architectural design drawings to build a house in reality. Classes are like design drawings, only designing what is needed, but There is no physical building, and the class is just a design. Only the instantiated objects can actually store data and occupy physical space.

7. Class object model

7.1 How to calculate the size of a class object

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?

7.2 Guessing the storage method of class objects

  • The object contains various members of the class

Defect: The member variables in each object are different, but the same function is called. If stored in this way, when a
class creates multiple objects, < /span> how to solve it? Each object will save a copy of the code. The same code is saved multiple times, which wastes space. So

  • Only one copy of the code is saved, and the address where the code is stored is saved in the object.

  • Only member variables are saved, and member functions are stored in the public code section.

Question: Regarding the above three storage methods, which method does the computer store?

Let’s analyze it by getting the sizes of the different objects below.

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

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

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

sizeof(A1) : ______        sizeof(A2) : ______        sizeof(A3) : ______

Conclusion: The size of a class is actually the sum of the "member variables" in the class. Of course, you must pay attention 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.

7.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: The 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.

8. this pointer

8.1 Introduction of this pointer

Let’s first define a date class 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 class, there is such a problem:

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", so that the pointer Points to the current object (the object that calls the function when the function is running). 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, the compiler automatically completes it.

8.2 Characteristics of this pointer

  1. 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 "member functions".
  3. This pointer is essentially a formal parameter of a "member function". When an object calls a member function, the object address is passed to the this parameter as an actual parameter. Sothis pointer is not stored in the object.
  4. 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..

8.3 Comparison of Stack implementation in C language and C++

1. Clingual reality

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 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, and the method of operating data cannot be placed in the structure, that is,The data and the method of operating data are separated, and the implementation is quite complicated, involving a large number of pointer operations, and errors may occur if you are not careful.

2. C++ reality

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;
}

In C++, data and methods for operating data can be perfectly combined through classes. Access permissions can be used to control which methods can be called outside the class, that is, encapsulation. When using Just like using its own members, it is more in line with human understanding of a thing. 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, and the user needs to use it in C language. Maintain yourself.


End of article

Guess you like

Origin blog.csdn.net/m0_73156359/article/details/134484450