C++(new和malloc)
目录
C++中内存区域划分:
malloc:
示例:
realloc:
new:
示例:
利用new开辟数组:
对malloc开辟的内存块进行一个初始化:
C++中内存区域划分:
栈内存:自动管理局部变量和函数调用帧,生命周期与其作用域绑定。如果递归过深可能会导致栈溢出。
堆内存:手动进行管理,通过new/malloc手动进行分配,容量受到系统限制,通过delete/free进行释放,否则会导致内存泄漏。
静态存储区:存放全局/静态变量,生命周期持续到程序结束。
常量区:存储字符串字面量和编译期常量,只读。
malloc:
用于在堆区上动态分配指定字节数的连续内存块,返回未初始化的void*指针,需要手动进行类型转换和释放。
需要注意:使用malloc分配内存时,需要判断是否分配成功,如果分配失败返回值未nullptr。需要手动初始化,如果未手动初始化可能含随机值。使用了malloc必须与free配对使用,进行内存释放,不然会导致内存泄漏。
示例:
#include <iostream>
#include <string>
#include <thread>class A {
public:A():_target(0){std::cout << "A::无参构造" << std::endl;}A(int target):_target(target){std::cout << "A::有参构造" << std::endl;}~A(){}void Get() {std::cout << "_target " << _target << std::endl;}
private:int _target;int* _data;
};int main() {A* ptr = (A*)malloc(sizeof(A));if (ptr == nullptr) {throw "error";}ptr->Get();free(ptr);return 0;
}
运行结果:
可以看出,malloc仅分配原始内存空间,不会调用类的构造函数,导致成员变量未被初始化,而直接调用成员函数Get()进行访问成员变量,会导致未定义行为。
#include <iostream>
#include <string>
#include <thread>class A {
public:A():_target(0),_data(new int(0)){std::cout << "A::无参构造" << std::endl;}A(int target,int data):_target(target),_data(new int(data)){std::cout << "A::有参构造" << std::endl;}~A(){delete _data;}void Get() {std::cout << "_target " << _target << std::endl;}
private:int _target;int* _data;
};int main() {int* ptr = (int*)malloc(10 * sizeof(int));if (ptr == nullptr) {throw "error";}for (int i = 0; i < 10; i++) {*(ptr + i) = i;}for (int i = 0; i < 10; i++) {std::cout << *(ptr + i) << " ";}free(ptr);return 0;
}
运行结果:
realloc:
调整已分配的内存块的大小,如果进行扩展的话,原内存块后方有足够空间,直接扩展并返回原指针,反之,如果内存块后方没有足够的空间,徐重新分配新内存块并复制数据,释放原内存块。
realloc失败也是返回nullptr,需要对其进行判断是否成功,如果当分配的内存大小为0则等效于free。尽量使用一个临时变量接收返回值,这样能够避免原指针被覆盖而导致的内存泄漏。
#include <iostream>
#include <string>
#include <thread>class A {
public:A():_target(0),_data(new int(0)){std::cout << "A::无参构造" << std::endl;}A(int target,int data):_target(target),_data(new int(data)){std::cout << "A::有参构造" << std::endl;}~A(){delete _data;}void Get() {std::cout << "_target " << _target << std::endl;}
private:int _target;int* _data;
};int main() {int* ptr = (int*)malloc(10 * sizeof(int));if (ptr == nullptr) {throw "error";}for (int i = 0; i < 10; i++) {*(ptr + i) = i;}for (int i = 0; i < 10; i++) {std::cout << *(ptr + i) << " ";}int* newptr = (int*)realloc(ptr,16*sizeof(int));if (newptr == nullptr) {free(ptr);return -1;}ptr = newptr;for (int i = 10; i < 16; i++) {*(ptr + i) = i;}for (int i = 0; i < 16; i++) {std::cout << *(ptr + i) << " ";}free(ptr);return 0;
}
运行结果:
使用realloc进行内存扩展,使用临时变量newptr进行接收其返回值,如果返回值为nullptr,则使用原指针ptr对原内存进行释放,避免了返回值将ptr进行覆盖而导致无法释放原内存块。如果扩展成功,那么将临时变量的值赋值给ptr,最后释放释放ptr就行或者newptr,因为这里是将newptr赋值给ptr,如果对ptr和newptr进行释放,则会导致双重释放。
new:
C++提供的更高层次的内存管理操作符,不只是会分配内存还可以进行初始化,提供类型安全。
示例:
#include <iostream>
#include <cstdlib>class A {
public:A():_target(0),_data(new int(0)){std::cout << "A::无参构造" << std::endl;}A(int target,int data):_target(target),_data(new int(data)){std::cout << "A::有参构造" << std::endl;}~A(){delete _data;std::cout << "A::析构函数" << std::endl;}void Get() {std::cout << "_target " << _target << std::endl;}
private:int _target;int* _data;
};int main() {A* ptr = new A();A* ptr1 = new A(10, 10);ptr->Get();ptr1->Get();delete ptr;delete ptr1;return 0;
}
运行结果:
从运行结果可以知道new进行初始化时,会调用构造函数,而当对其释放时,就会调用析构函数。如果通过显示调用了析构函数,还对其进行一个delete释放,那么则会导致双重释放,因为调用析构函数就会析构成员变量_data,而再对ptr进行释放时,尝试对整个对象进行释放,就会导致_data被释放分两次。
利用new开辟数组:
#include <iostream>
#include <cstdlib>class A {
public:A():_target(0),_data(new int(0)){std::cout << "A::无参构造" << std::endl;}A(int target,int data):_target(target),_data(new int(data)){std::cout << "A::有参构造" << std::endl;}~A(){delete _data;std::cout<<"A::析构函数"<<std::endl;}void Get() {std::cout << "_target " << _target << std::endl;}
private:int _target;int* _data;
};int main() {A* ptr = new A[3]{{1,1},{10,10},{6,6}};for (int i = 0; i < 3; i++) {(ptr + i)->Get();}delete[] ptr;return 0;
}
运行结果:
对malloc开辟的内存块进行一个初始化:
#include <iostream>
#include <cstdlib>class A {
public:A():_target(0),_data(new int(0)){std::cout << "A::无参构造" << std::endl;}A(int target,int data):_target(target),_data(new int(data)){std::cout << "A::有参构造" << std::endl;}~A(){delete _data;std::cout<<"A::析构函数"<<std::endl;}void Get() {std::cout << "_target " << _target << std::endl;}
private:int _target;int* _data;
};int main() {A* ptr = (A*)malloc(sizeof(A));if (ptr == nullptr) {throw "error";}new(ptr) A(10, 10);ptr->Get();ptr->~A();free(ptr);return 0;
}
运行结果:
对malloc开辟的内存块上使用placement new构造对象,调用了构造函数进行了初始化。显式调用析构函数释放_data的内存,然后再free对分配的内存进行释放。如果没有显式调用析构函数直接free会导致成员资源泄露。其中placement new是C++的特殊内存管理机制,允许在预先分配的内存地址上直接构造对象,不会分配内存,只是调用构造函数进行对象初始化,需要显式调用析构函数之后才进行释放。