欢迎来到本期节目- -
内存区域
c/c++中程序内存区域划分:
你知道它在内存的哪个区域吗?
int global_var = 1;
static int static_global_var = 1;
void test1()
{
static int static_part_var = 1;
}
这里的全局变量,静态全局变量,静态局部变量都在数据区。 |
class Code
{
public:
void print() //普通成员函数
{
cout << _a << endl;
}
static void s_print() //静态成员函数
{
cout << _b << endl;
}
private:
int _a; //普通成员变量
static int _b; //静态成员变量
};
void test2()
{
Code obj;
int part_var;
}
这里的局部变量obj和part_var都在栈中; |
但是Code类的静态成员函数和该对象的普通成员函数是在代码区,且Code类的静态成员变量在静态区。 |
void test3()
{
char ch1[] = "哈哈";
const char* ch2 = "哈哈";
}
ch1字符数组的"哈哈"是在栈区上,"哈哈"是被拷贝到栈区的。 |
*ch2是在常量区。 |
void test4()
{
int* ptr = (int*)malloc(sizeof(int));
*ptr = 1;
free(ptr); //养成好习惯——申请的资源要释放
ptr = nullptr;
}
ptr是一个局部指针变量,所以是在栈区上。 |
*ptr是在堆区上。 |
内存管理方式
c管理方式
c管理内存函数有三:
-
malloc
void* malloc (size_t size);
该函数会在堆上申请大小为size字节的一段连续空间
void test1() { int* ptr1 = (int*)malloc(sizeof(int)); if(ptr1 == nullptr) { perror("malloc"); return 1; } //使用空间 //释放空间 free(ptr1); ptr1 = nullptr; }
在这其中,ptr1指向的空间是没有初始化的
-
calloc
void* calloc (size_t num, size_t size);
该函数会在堆上申请一段大小为size字节的连续空间,然后初始化
void test2() { int* ptr2 = (int*)calloc(sizeof(int)); if(ptr2 == nullptr) { perror("calloc"); return 1; } //使用空间 //释放空间 free(ptr2); ptr2 = nullptr; }
在这其中,ptr2指向的空间在开辟之后会初始化为0
-
realloc
void* realloc (void* ptr, size_t size);
该函数是为ptr所指向的空间扩容,如果ptr为空,该效果等价与malloc;
原地扩容;
异地扩容:扩容会在开好新空间之后,将原数据拷贝到新空间,然后释放旧空间,返回新空间的地址。void test2() { int* ptr3 = (int*)realloc(nullptr,sizeof(int)); if(ptr3 == nullptr) { perror("realloc"); return 1; } //扩容 int* tmp = (int*)realloc(ptr3,sizeof(int)*10); if(tmp == nullptr) { perror("realloc"); free(ptr3); return 1; } else { ptr3 = tmp; } //使用空间 //释放空间 free(ptr3); ptr3 = nullptr; }
c++管理方式
new/delete操作符
使用方式:
class my_type
{
public:
my_type(int n = 0)
:_n(n)
{
cout<<"my_type(int n = 0)"<<endl;
}
~my_type()
{
cout<<" ~my_type()"<<endl;
}
private:
int _n;
};
void test1()
{
// 单个对象
//操作符new + 类型int + 初始化()
int* ptr = new int(0); //内置类型,申请好空间,可以显示初始化
delete ptr; //直接释放该空间,此处和free等价
ptr = nullptr;
my_type* mptr = new my_type;//自定义类型在申请好空间之后,new会调用该类的默认构造函数
delete mptr; //自定义类型delete会先调用该类的析构函数,然后释放该空间
mptr = nullptr;
}
void test2()
{
// 多个对象
//操作符new + 类型int + 初始化{}
int* ptr = new int[10]{0}; //申请一段连续的内存空间,可以显示初始化
delete[] ptr; //直接释放ptr指向的空间,此处与delete和free效果等价
ptr = nullptr;
my_type* mptr = new my_type[10];//在vs中,在申请连续多个自定义对象时,会多开4个字节,用来存储对象的个数,然后调用构造函数初始化每个对象,然后返回申请的起始地址的偏移4个字节之后的地址。
delete mptr; //在VS中,自定义类型delete会先在mptr指针向前偏移4个字节的位置取出对象个数来调用该类的析构函数析构每个对象,然后释放该空间
mptr = nullptr;
}
使用new创建自定义对象空间时,如果没有默认构造,可以使用带参构造初始化。 |
注意
:new和delete匹配使用,new[]和delete[]匹配使用。
底层知识:
- new操作符在底层实际会调用operaor new全局函数申请空间,失败抛异常;(底层含有malloc)
- delete操作符在底层实际会调用operator delete全局函数释放空间;(底层含有free)
1.new和delete
- new
- 调用operator new全局函数申请空间
- 在申请的空间上执行构造函数,完成对象的构造(自定义类型)
- delete
- 在空间上执行析构函数,完成对象的资源清理(自定义类型)
- 调用operator delete全局函数释放对象空间
2.new[]和delete[]
- new T[N]
- 调用operator new[]全局函数,在该函数中实际调用operator new全局函数完成对N个对象的空间申请
- 在申请的空间上执行N次构造函数(自定义类型)
- delete []
- 在空间上执行N个对象的析构函数,完成N个对象的资源清理(自定义类型)
- 调用operator delete[]全局函数,实际调用operator delete全局函数释放空间
void test1()
{
//my_type* ptr = (my_type*)malloc(sizeof(my_type)); 等价
my_type* ptr = (my_type*)operator new(sizeof(my_type)); //申请空间
new (ptr)my_ptr(3); //定位new表达式,调用构造函数
ptr->~my_type(); //显示调用析构函数
// free(ptr); 等价
operator delete(ptr); //释放空间
ptr = nullptr;
}
void test2()
{
my_type* ptr = new my_type(3); //申请空间+调用构造
delete ptr; //调用析构+释放空间
ptr = nullptr;
}
二者关系
最后我们总结一下,两种管理方式的区别:
mallloc/free | new/delete |
---|---|
函数 | 操作符 |
malloc申请空间需要手动计算大小 | new 申请空间只需指定对象类型,可指定个数 |
malloc不会初始化 | new可以初始化 |
malloc返回的是void*,需要强转 | new返回的是空间对象类型的指针 |
malloc失败返空指针 | new失败抛异常 |
申请自定义类型对象,不会调用构造和析构 | 申请自定义类型对象,会自动调用构造和析构 |
希望该片文章对您有帮助,请点赞支持一下吧😘💕