[C++] is memory management, but C++! ! && Template Primer

Table of contents

1. Looking back at C language memory

Two, C++ memory management method 

1. Built-in types

2. Custom types

3. The difference between new & malloc return content

4. operator new   & operator  delete 

5. Summary of the difference between malloc/free and new/delete

6. Position new expression (placement-new) (understand)

Third, the initial template

1. Generic Programming - Concept

2. Function templates

(1. Template instantiation

   concept 

(2. Explicit instantiation

(3. Implicit instantiation

3. Class template - [Exercise] Implementing the Push of the stack


1. Looking back at C language memory

Try giving the following answers:

result:

Parse:

Two, C++ memory management method 

The C language memory management method can continue to be used in C++, but it is powerless in some places, and it is more troublesome to use, so C++ has proposed its own memory method : dynamic memory management through new and delete operators .

1. Built-in types

 There is no essential difference between new / delete and malloc & free for built-in types

void Test()
{   // 申请一个int空间
	int* z1 = new int;
    // 申请5个int的数组
	int* z2 = new int[5];
	// 申请一个int空间,初始化为5
	int* z3 = new int(5);

    // 销毁
	delete z1;
	delete[] z2; // 销毁类型要匹配
	delete z3;
	// 对于内置类型 new / delete 与 malloc & free 没有本质的区别
	// 仅仅new只是用法上简化了
}

Note: There is no innovation in C++ that supports realloc in C language, and the C++ interface does not support capacity expansion

2. Custom types

The principle of new
  1. Call the operator new function to apply for space
  2. Execute the constructor on the requested space to complete the construction of the object
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
The principle of new T[N]
  1. Call the operator new[] function, and actually call the operator new function in operator new[] to complete N pairs
application for image space
  2. Execute the constructor N times on the requested space
The principle of delete[]
  1. Execute N times of 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
put space

struct  B
{
public:
	B(int b)
	{
		k = b;
		cout << "B" << endl;
	}

	~B()
	{
		cout << "~B" << endl;
	}
private:
	int k;
};

struct  A
{
public:
	A(int sum, int b)
		: _sum(sum)
		, _b(b)
	{
		cout << "A" << endl;
	}

	~A()
	{
		cout << "~A" << endl;
	}
private:
	int _sum;
	B _b;
};

int func()
{
	A* p1 = new A(10, 20); // 1.内置类型初始化  2. 自定义类型调用其构造函数
	A* p2 = new A[10];     
	A* p3 = new A[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 多实例,并且初始化
	delete p1;             // 1. 销毁内置类型   2. 调用析构函数
	delete[] p2;           // 销毁类型一定要匹配使用, 否则可能出现报错
	delete[] p3;
	return 0;  // 返回码
}

3. The difference between new & malloc return content

(1) malloc fails to return  a null pointer

(2) If new fails, an exception will be returned (we will focus on the exception later)

void test()
{
	try   // 其中如果new失败会进入异常,也就是catch
	{
		// 当内存申请超过一定限度,会申请失败
	// malloc 失败会返回null
		char* p1 = (char*)malloc(sizeof(char) * 1024u * 1024u * 1024u * 1024u * 1024 * 1024);
		printf("%p\n", p1);
		// new  失败会抛异常
		char* p2 = new char[1024 * 1024 * 1024 * 1024 * 1024 * 1024];
		printf("%p\n", p2);

		free(p1);
		delete[] p2;
	}
	catch (const std::exception& e)   // 打印最近的异常信息
	{
		cout << e.what() << endl;
	}
}

Note: Returning an exception in the face of new failure will use a try to enclose the code that needs new.

4. operator new   & operator  delete 

       new and delete are operators for users to apply and release dynamic memory . operator new and operator delete are global . new calls the operator new global function at the bottom layer to apply for space, and delete uses the operator delete global function at the bottom layer to release space .
(Generally speaking, operator new & delete is not a function overload, but a global function that helps new and delete implement its mechanism)

 

operator new : This function actually applies for space through malloc, and returns directly when malloc successfully applies for space; if space application fails, try to implement countermeasures for insufficient space. If the countermeasures are set by the user, continue to apply, otherwise throw an exception . ( new -> operator new -> malloc -> pointer or exception )

operator delete : This function finally releases space  through free .

5. Summary of the difference between malloc/free and new/delete

The common points of malloc/free and new/delete are:
They all apply for space from the heap and need to be released manually by the user .
The differences are:
  • 1. malloc and free are functions, new and delete are operators
  • 2. The space requested by malloc will not be initialized, but new can be initialized
  • 3. When malloc applies for space, you need to manually calculate the size of the space and pass it on. New just needs to follow it with the type of space. If there are multiple objects, specify the number of objects in []
  • 4. The return value of malloc is void*, which must be forced when used, and new does not need it, because new is followed by the type of space
  • 5. When malloc fails to apply for space, it returns NULL, so it must be judged as empty when using it, new does not need it, but new needs to catch exceptions
  • 6. When applying for a custom type of object, malloc/free will only open up space, and will not call the constructor and destructor, while new will call the constructor to complete the initialization of the object after applying for space, and delete will call the parser before releasing the space. The constructor completes the cleanup of resources in the space.

6.  Position new expression (placement-new) (understand)

The positioning new expression is to call the constructor to initialize an object in the allocated original memory space

Such as: new (p1) A (10) // Among them, p1 has opened up space through malloc but has not been initialized, A is the type, and 10 is the value initialized by calling the custom constructor.

scenes to be used:
Positioning the new expression is generally used in conjunction with the memory . Because the memory allocated by the memory pool is not initialized, if it is an object of a custom type, it needs to use the definition expression of new to explicitly call the constructor for initialization.

struct  B
{
public:
	B(int b)
	{
		k = b;
		cout << "B" << endl;
	}

	~B()
	{
		cout << "~B" << endl;
	}
private:
	int k;
};

void test1()
{
	B* p3 = (B*)malloc(sizeof(B));
	// malloc 的空间并未初始化
	new (p3) B(100); // 给构造函数参数 100
	p3->~B();
	delete p3;
}


Third, the initial template

1. Generic Programming - Concept

A 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.

We must have written programs like this

void Swap(int& z1, int& z2)
{
	int tmp = z2;
	z2 = z1;
	z1 = tmp;
}

Exchange functions generally can only handle one type of data; and a program needs to exchange multiple types of data, so we need to create multiple types of exchange functions, this process is repetitive and inefficient .

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.

2. Function templates

Principle: A function template is a blueprint. It is not a function itself, but a mold for the compiler to generate a specific type of function by using it. So in fact, the template is to hand over the repetitive things that we should have done to the compiler.

Compiler uses template steps:

1. Deduction type

2. Template instantiation

Let's briefly show it:

template <class T>   // 或者 <template T> T只是取名字
void Swap(T& z1, T& z2)
{
	T tmp = z2;
	z2 = z1;
	z1 = tmp;
}

int main()
{
	int i = 1, b = 2;
	Swap(i, b);
	return 0;
}

In the compiler compilation stage , for the use of template functions, the compiler needs to deduce and generate corresponding types of functions for calling according to the type of actual parameters passed in . For example: when using a function template with a double type, the compiler determines T as a double type through the deduction of the actual parameter type, and then generates a code that deals specifically with the double type . Similarly, this is the template of our exchange function, in the middle The compiler will deduce the type, the compilation is more complicated, and the time will be slightly longer.

Note: The swap uses the same set of templates, but the function called is not the same function . (The compiler automatically creates different types of swap functions for us)

Here comes the key point: there is no need to create exchange functions yourself in the future . There is a set of exchange functions in the C++ library, which are in the std namespace.

The operation is as follows:

#include <iostream>
using namespace std;
int main()
{
	int i = 1, b = 2;
	double z = 1.1, k = 2.2;
	swap(i, b);   // 小写就行
	swap(z, k);   // 模板推导,需要类型相同,否则报推导类型不确定
	return 0;
}

(1. Template instantiation

concept 

When a function template is used with parameters of different types , it is called instantiation . Template parameter instantiation is divided into : implicit instantiation    and    explicit instantiation .

(2. Explicit instantiation

Specify the actual type of the template parameter in <> after the function name   

template <class T>   // 或者 <template T>
T* func1(int n)
{
	T* k1 = new T(n);
}


int main(void)
{
 int a = 10;
 double b = 20.0;
 
 // 显式实例化
 Add<int>(a, b);  1. 普通
 func1<A>(10);    2. 特殊,必须要显式否则怎么推演?计算机:二进制给你,你来推演!
 return 0;
}

(3. Implicit instantiation

Let the compiler deduce    the actual type of the template  parameter based on the actual parameter

#include <iostream>
using namespace std;

int ADD(size_t & z1, size_t & z2)  // size_t是标准C库中定义的,它是一个基本的与机器相关的无符号整数的C/C + +类型
  // 就简单理解为 unsiged int 主要防止 负数
{
	return z1 + z2;
}

template <class T>   // 或者 <template T>
T ADD(const T& z1, const T& z2)
{
	return z1 + z2;
}

template <class B1, class B2> // 可以提供多个模板
B1 ADD(B1& b1, B2& b2)
{
	return b1;
}

int main()
{
	// 隐式实例 ——通过编译器自动推导, 不人为干预。
	int i = 1, b = 2;
	double z = 1.1, k = 2.2;
	cout << ADD(i, (int)z) << endl; // 模板 不支持1.1 -> 1 隐式转化,但可以提前,进入的是
	cout << ADD(1.2, (double)1) << endl;

	// 思考;调用哪一个函数
	cout << ADD(i, b) << endl;  // 计算机会调用现成的函数,
	// 调用 int ADD(int& z1, int& z2) ; 模板实例化需要通过编译器,所以优先级比较低。
	cout << ADD(z, i) << endl;  // 两种类型,则会选择多类型模型 B1 ADD(B1& b1, B2& b2)
	return 0;
}

3. Class template - [Exercise] Implementing the Push of the stack

#include <iostream>
using namespace std;
#include <assert.h>

template <class T>
class stack
{
public:
	stack(int capacity = 4)
		: _size(0)
		, _capacity(capacity)
		, plist(nullptr)
	{
		plist = new T[_capacity];
	}

	void Push(T x)
	{
		assert(plist);
		if (_size == _capacity)
		{
			// 1. 开空间
			// 2. 拷贝旧数据
			T* tmp = new T[_capacity * 2];
			if (plist)   // plist 首先是空指针,如果是是第一次不需要拷贝旧数据
			{
				memcpy(tmp, plist, sizeof(T) * _capacity);
				// 如果申请成功
				_capacity *= 2;
				delete[] plist;
				plist = tmp;
			}
		}
		plist[_size++] = x;
		cout << plist[_size - 1] << endl;
	}

	void Pop()
	{
		assert(_size > 0)
		_size--;
	}

	const T& TopStack()  // 返回栈顶元素,用的是引用,可以修改 私密成员,所以需要const修饰
	{
		assert(_size > 0);  // 防止栈为空
		return plist[_size - 1];
	}

	~stack()
	{
		delete[] plist;
		_size = _capacity = 0;
	}
private:
	T* plist;
	int _size;
	int _capacity;
};

void func()
{
	try
	{
		stack<int> p1;  // 其中如果new失败会进入异常,也就是catch
		stack<char> p2;
		p1.Push(1);
		p1.Push(2);
		p1.Push(3);
		p1.Push(4);
		p1.Push(5);

		cout << p1.TopStack() << endl;
	}
	catch (const std::exception& e)
	{
		cout << e.what() << endl; // 打印最近的异常信息
	}
}

Summarize:

  • Class template definition and declaration cannot be separated (separation is allowed in the same file), otherwise an error will be reported.
  • When a general class is instantiated, there is no class type, so the general class template must be instantiated explicitly.
  • In C++, class templates are defined in header files, so they can be written as " .hpp " header files, which means not only declarations but also implementations , usually class template implementations.

epilogue

This section is over here, thank you friends for browsing, if you have any suggestions, welcome to comment in the comment area; if you bring some gains to your friends, please leave your likes, your likes and concerns will become bloggers The driving force of the master's creation.

Guess you like

Origin blog.csdn.net/qq_72112924/article/details/130782506