Table of contents
1. Looking back at C language memory
Two, C++ memory management method
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)
1. Generic Programming - Concept
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
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
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
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
- 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.
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
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
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
(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.