C/C++ memory management & template elementary

1 Memory management

1.1 Memory distribution of C/C++

Insert picture description here

  • The stack is also called the stack, non-static local local variables, function parameters and return values, the stack grows downward.
  • The memory mapped segment is an efficient I/O mapping method for loading a shared dynamic memory library. Users can use the system interface to create shared
    shared memory for inter-process communication.
  • The heap is used for program operation to allocate dynamic memory, and the heap grows upward.
  • Data segment : store global data and static data
  • Code segment : executable code and read-only constants

1.2 Dynamic memory management in C

  • malloc
  • free
  • calloc
  • realloc

1.3 Dynamic memory management in C++

C's memory management method can continue to be used in C++, but in some places it cannot meet the needs, so C++ proposes its own memory management method: dynamic memory management through the new and delete operators

1.3.1 New/delete operation built-in type

    // 申请
	//动态申请一个int类型的空间
	int* ptr0 = new int;
	//动态申请一个int类型的空间,并初始化为10
	int* ptr1 = new int(10);
	//动态申请12个int类型的空间
	int* ptr2 = new int[12];

Insert picture description here

   // 释放
    delete ptr0;
	delete ptr1;
	delete[] ptr2;

Insert picture description here

Note: To apply for and release the space of a single element, use the new/delete operator, apply and release continuous space such as object arrays, use new[N] and delete[]

1.3.2 new/delete operation custom type

class A{
    
    
public:
	A(int data = 10) 
		:_data(data)
	{
    
     
		cout << "A(int data = 10)" << endl; 
	}
	~A(){
    
    
		cout << "~A()" << endl;
	}
private:
	int _data;
};
void Test1(){
    
    
	//申请1个A类型的空间
	A* p1 = (A*)malloc(sizeof(A));
	free(p1);
	//申请1个A类型的空间
	A* p2 = (A*)malloc(sizeof(A)* 10);
	free(p2);
}
void Test2(){
    
    
	//申请1个A类型的空间
	A* p3 = new A;
	//A* p4 = new A();
	delete p3;
	//申请10个A类型的空间
	A* p4 = new A[10];
	delete[] p4;
}
int main()
{
    
    
	Test2();
	return 0;
}

Insert picture description here

When applying for a custom type of space, new will call the constructor, delete will call the destructor, but malloc and free will not.

1.3.3 operator new and operator delete functions

new and delete are operators for users to apply and release dynamic memory. operator new and operator delete are global functions provided by the system. new calls the operator new global function at the bottom to apply for space, and delete calls operator delete at the bottom to release space.

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) {
    
    
 // try to allocate size bytes
 void *p;
 while ((p = malloc(size)) == 0)
 if (_callnewh(size) == 0)
 {
    
    
 // report no memory
 // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
 static const std::bad_alloc nomem;
 _RAISE(nomem);
 }
 return (p);
 }
 /*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData) {
    
    
 _CrtMemBlockHeader * pHead;
 RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
 if (pUserData == NULL)
 return;
 _mlock(_HEAP_LOCK); /* block other threads */
 __TRY
 /* get a pointer to memory block header */
 pHead = pHdr(pUserData);
 /* verify block type */
 _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
 _free_dbg( pUserData, pHead->nBlockUse );
 __FINALLY
 _munlock(_HEAP_LOCK); /* release other threads */
 __END_TRY_FINALLY
 return;
 }
 /*
free的实现
*/
#define free(p) _free_dbg(p,_NORMAL_BLOCK)

Through the implementation of the above two global functions, it is known that operator new actually applies for space through malloc. If malloc applies for space
successfully, it will return directly. Otherwise, the user-provided space shortage countermeasure will be implemented. If the user provides this measure, continue to apply, otherwise Throw anomaly
. Operator delete is finally releasing space through free .

1.3.4 The realization principle of new and delete

For built-in types

  • If you apply for a built-in type of space, new and malloc, delete and free are basically similar, the difference is: new/delete applies for and
    releases the space of a single element, and new[] and delete[] apply for continuous space. And new will throw an exception when it fails to apply for space, and
    malloc will return NULL.
    For custom types
  • Principle of new
  1. Call operator new function to apply for space
  2. Execute the constructor on the requested space to complete the construction of the object.
    We can look at the assembly code to explore the underlying principles
#include <iostream>
using namespace std;
//二叉树结点的定义
struct TreeNode
{
    
    
	TreeNode* _left;
	TreeNode* _right;
	int _val;
	TreeNode(int val = 0) :_val(val), _left(nullptr), _right(nullptr){
    
    }
};

int main()
{
    
    
	TreeNode* p = new TreeNode;

	return 0;
}

Insert picture description here

  • The principle of delete
  1. Execute the destructor on the space to complete the cleanup of resources in the object
  2. Call the operator delete function to release the space of the object
  • Principle of new T[N]
  1. Call operator new [] function, the operator new [] operator new function in the actual call is completed N objects space application
    please
  2. Execute the constructor N times on the requested space
  • The principle of delete[]
  1. Execute N destructors on the released object space to complete the cleanup of resources in N objects
  2. Call operator delete[] to release space, actually call operator delete in operator delete[] to release space

To sum up!
new is equivalent to operator new + constructor and operator new is equivalent to malloc + malloc. Failure throwing exception
delete is equivalent to destructor + operator delete and operator delete is equivalent to free

1.4 The difference between malloc/free and new/delete

Same point

  • All apply for space from the heap and need to be manually released by the user

difference

  • 1. Malloc and free are functions, new and delete are operators
  • 2. The space requested by malloc will not be initialized, but new will be initialized
  • 3. When malloc applies for space, you need to manually calculate the space size and pass it, new only needs to be followed by the type of space
  • 4. The return value of malloc is void*, which must be forced when used. New is not needed, because new is followed by the type of space
  • 5. When malloc fails to apply for space, it returns NULL, so it must be null when used. New is not needed, but new needs to catch exceptions
  • 6. When applying for a custom object, malloc/free will only open up the space and will not call the constructor and destructor. New will use the constructor to complete the initialization of the object after applying for the space, and delete will call the destructor before releasing the space. The function completes the cleanup of resources in the space

2 Preliminary template

2.1 Generic programming

How to implement a common exchange function?

void Swap(int& left, int& right) {
    
    
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right) {
    
    
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right) {
    
    
	char temp = left;
	left = right;
	right = temp;
}

Although it is possible to use function overloading, there are several disadvantages:

  • Overloaded functions are only different in types, and the code reuse rate is relatively low. As long as new types appear, the corresponding functions need to be added
  • The maintainability of the code is relatively low, and an error may cause all overloads to go wrong

Can you tell the compiler a model, and let the compiler use the model to generate code according to different types?
Insert picture description here
If in C++, there can also be such a mold, by filling this mold with different materials (types) to obtain castings of different materials
(generating specific types of code), it will save a lot of hair. Coincidentally, the predecessors have already planted the tree, we just need to enjoy the shade here.
Generic programming: Writing generic code that has nothing to do with types is a means of code reuse. Templates are the foundation of generic programming.
Insert picture description here

2.1 Function template

2.1.1 Concept

The function template represents a family of functions. The function template has nothing to do with the type. It is parameterized when used, and a specific
type version of the function is generated according to the type of the actual parameter .

2.1.2 Format

template<typename T1, typename T2,…,typename Tn>
return value type function name (parameter list) {}

template<typename T>
void Swap( T& left, T& right) {
    
    
 T temp = left;
 left = right;
 right = temp; }

Note: typename is used to define template parameter keywords, and class can also be used (remember: you cannot use struct instead of class)

2.1.3 Principle

So how to solve the above problem? Everyone knows that when Watt improved the steam engine, mankind started the industrial revolution and liberated the productive forces. Machine
production eliminated many handmade products. What is the essence, the repetitive work is handed over to the machine to complete. Someone gave the argument: lazy people create the world. The function template is a blueprint, it is not a function itself, but a mold for the compiler to use the method to generate a specific type of function. So in fact, the template is to give you the compiler the repetitive things that we should have done.
Insert picture description here
In the compilation stage of the compiler, for the use of template functions, the compiler needs to deduce and generate the corresponding type of function for
calling based on the type of the actual parameter passed in . For example, when using a function template with the double type, the compiler determines T as the double type by deducing the type of the actual parameter, and then
generates a code that deals with the double type, and the same is true for the character type.

2.1.4 Instantiation

When a function template is used with different types of parameters, it is called the instantiation of the function template. Template parameter instantiation is divided into: implicit instantiation and explicit instantiation
.
1. Implicit instantiation: Let the compiler deduce the actual type of the template parameter based on the actual parameter

template<class T> 
T Add(const T& left, const T& right) {
    
    
	return left + right;
}
int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);
	Add(d1, d2);

	/*
	该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
	编译器无法确定此处到底该将T确定为int 或者 double类型而报错
	注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
	Add(a1, d1);
	*/

	// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
	Add(a1, (int)d1);
	return 0;
}

2. Explicit instantiation: specify the actual type of the template parameter in the <> after the function name

template<class T> 
T Add(const T& left, const T& right) {
    
    
	return left + right;
}
int main(void) {
    
    
	int a = 10;
	double b = 20.0;

	// 显式实例化
	Add<int>(a, b);
	return 0;
}

If the types do not match, the compiler will try to perform implicit type conversion. If the conversion fails, the compiler will report an error.

2.1.5 Matching rules

1. The function can be a non-template and a template function with the same name exist, but also the function template may be instantiated as the non-template function
Number

// 专门处理int的加法函数
int Add(int left, int right) {
    
    
	return left + right;
}
// 通用加法函数
template<class T> T Add(T left, T right) {
    
    
	return left + right;
}
void Test()
{
    
    
	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
	Add<int>(1, 2); // 调用编译器特化的Add版本
}

2. For a non-template function and a function template with the same name, if other conditions are the same, the non-template function will be called first during transfer instead of
generating an instance from the template . If the template can produce a function with a better match, then the template will be selected

// 专门处理int的加法函数
int Add(int left, int right) {
    
    
	return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right) {
    
    
	return left + right;
}
void Test()
{
    
    
	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函 数
}

3. Template functions do not allow automatic type conversion, but ordinary functions can perform automatic type conversion

2.2 Class template

2.2.1 Define format

template<class T1, class T2, ..., class Tn>
class 类模板名
{
    
    
	// 类内成员定义
};
#include <iostream>
using namespace std;
//MyVector类模板
// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class MyVector{
    
    
public:
	MyVector(size_t capacity)
		:_pData(new T[capacity])
		, _size(0)
		, _capacity(capacity)
	{
    
    }
	// 使用析构函数演示:在类中声明,在类外定义
	~MyVector(); 
	size_t MySize(){
    
    
		return _size;
	}
	T& operator[](size_t pos)
	{
    
    
		assert(pos < _size);
		return _pData[pos];
	}
private:
	int* _pData;
	size_t _size;
	size_t _capacity;
};
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
MyVector<T>::~MyVector(){
    
    
	if (_pData != nullptr){
    
    
		delete[] _pData;
		_pData = nullptr;
		_size = _capacity = 0;
	}
}
int main()
{
    
    
	MyVector<int>;
	MyVector<double>;
	return 0;
}

2.2.2 Instantiation

The instantiation of a class template is different from the instantiation of a function template. The instantiation of a class template needs to follow the name of the class template with <>, and then put the instantiated type in <>
. The name of the class template is not the real class, but the instantiated The result is the real class.

// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

Guess you like

Origin blog.csdn.net/CZHLNN/article/details/114686941