电子商务网站开发设计,黑龙江建设网网上办事大厅,扶贫网站建设优势,博兴网站建设目录 1. C/C中各种资源的内存分布1.1 C/C程序内存区域划分1.2 各资源的内存分布情况#xff08;练习#xff09; 2. C中的动态内存管理方式2.1 new/delete开辟内置类型空间2.2 new/delete开辟销毁自定义类型空间 3. operator new 与 operator delete函数4. new与delete的实现… 目录 1. C/C中各种资源的内存分布1.1 C/C程序内存区域划分1.2 各资源的内存分布情况练习 2. C中的动态内存管理方式2.1 new/delete开辟内置类型空间2.2 new/delete开辟销毁自定义类型空间 3. operator new 与 operator delete函数4. new与delete的实现原理5. 定位new表达式与池化计数6. malloc/free与new/delete的异同 1. C/C中各种资源的内存分布
1.1 C/C程序内存区域划分 正在执行的程序是在计算机的内存空间上运行的C/C为了程序的高效运行将内存划分了多个区域来进行对不同特性种类资源的区别管理。 1.2 各资源的内存分布情况练习
//全局变量数据段
int globalVar 1;//静态全局变量数据段
static int staticGlobalVar 1;
void Test()
{//静态变量数据段static int staticVar 1;//局部变量栈int localVar 1;//局部变量数组栈//sizeof(num1)代表整个数组40字节int num1[10] { 1, 2, 3, 4 };//局部变量字符数组栈//sizeof(char2)代表整个数组5字节//strlen(char2)字符串长度4char char2[] abcd;//sizeof(pChar3)指针4/8字节//strlen(pChar3)字符串长度4//只读字符串代码段const char* pChar3 abcd;//动态开辟空间堆//ptr1指针4/8字节int* ptr1 (int*)malloc(sizeof(int) * 4);//calloc会用0进行申请空间的初始化int* ptr2 (int*)calloc(4, sizeof(int));int* ptr3 (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);
}2. C中的动态内存管理方式 在编写程序时我们时常需要一段可以动态增长我们可以控制其开辟与销毁的空间C语言中我们学习过在内存中动态开辟空间的方式我们通过malloc与free来进行空间开辟与销毁。而C中有新的动态开辟空间的方法new与delete它们在使用上更加方便且可以应用于更广泛的场景接下来就让我们来进行对其的学习与使用。 2.1 new/delete开辟内置类型空间 new //开辟一个指定类型大小的空间
int* ptr1 new int;//为开辟的空间赋于指值
int* ptr2 new int(10);//开辟连续n个指定类型大小的空间
int* ptr3 new int[10];//连续空间的初始化赋值
int* ptr4 new int[3]{1,2,3};delete //释放大小为1个指定类型大小的空间
delete ptr1;//释放大小为多个指定类型空间大小的空间
delete[] ptr3注 new/delete 与 new[]/delete[] 必须配合使用不能混用 2.2 new/delete开辟销毁自定义类型空间 new/delete开辟自定义类型的动态空间会自动调用自定义类型的构造与析构函数 class A
{
private:int _a;
public:A(int a 0):_a(a){cout A() endl;}A(const A tmp){_a tmp._a;cout A(const A) endl;} ~A(){cout ~A() endl;}
};int main()
{A* pa1 new A;delete pa1;A* pa2 new A[3];delete[] pa2;return 0;
}开辟自定义类型空间的初始化方式 A aa1;
A aa2;
A aa3;//一段空间
//方法1(用存在的对象)
A* pa1 new A(aa1);
//方法2创建匿名对象
A* pa2 new A(A());
//方法3(隐式类型转换构造 拷贝构造优化为构造)
A* pa3 new A(3);//多段空间
//方法1
A* pa4 new A[3]{aa1, aa2, aa3};
//方法2
A* pa5 new A[3]{A(), A(), A()};
//方法3前三个元素初始化为123后面会赋值的部分会全部默认初始化为0类似数组
A* pa6 new A[10]{1, 2, 3};3. operator new 与 operator delete函数 new和delete是我们进行动态内存申请和释放的操作符而其实现空间的开辟与释放的方式为去调用用名为operator new 和operator delete的两个函数。这两个函数是系统提供的全局函数new在底层调用operator new全局函数来申请空间delete在底层通过operator delete全局函数来释放空间接下来就让我们来学习这两个函数的相关知识。虽然名为operator但与运算符重载无关两者为全局函数operator new与operator delete的实现底层为直接调用malloc与free来实现空间的动态开辟其用法也与malloc/free相同。operator new与operator delete两者可以直接调用 int* pa (int*)operator new(sizeof(int));*pa 10;
cout *pa endl;operator delete(pa);既然operator new/delete实现依旧是调用malloc/free实现动态开辟空间那为什么不去直接使用malloc/freemalloc申请空间失败会返回0NULL此返回值不符合面向对象的编程特性所以C对其进行了一层封装使得申请空间失败后抛出异常为了与malloc的封装匹配于是将free也进行了封装封装为operator delete。调用抛出异常演示 //连续申请空间申请空间不足开辟失败
void func_test()
{char* c1 new char[1024 * 1024 * 1024];//cout c1 endl;//char类型的变量流插入操作自动识别时会默认识别为字符串而不是指针cout (void*)c1 endl;//捕获异常的操作会改变执行流不再执行下面操作而会抛出异常//类似gotochar* c2 new char[1024 * 1024 * 1024];cout (void*)c2 endl;
}int main()
{try{func_test();}catch (const exception e){cout e.what() endl;}return 0;
}4. new与delete的实现原理 申请开辟自定义类型空间时new操作符会先开辟出指定大小的空间而后调用构造函数初始化开辟出的空间。申请空间 调用构造 delelte销毁自定义类型的申请空间时会先调用自定义类型的析构函数而后再进行空间的销毁释放。调用析构销毁空间 new/delete操作符在编译时会直接按照上述调用步骤生成汇编指令。汇编调试calljump new调用new申请时会先开辟空间再调用构造构造时再调用new。销毁时会先调用析构析构先销毁里层new申请的空间而后再销毁外层new申请的空间。 class Stack
{
private:int* _a;int _capacity;int _top;
public:Stack(int n 4){cout Stack() endl;_a new int[n];_top 0;_capacity 4;}~Stack(){cout ~Stack() endl;delete[] _a;_top 0;_capacity 0;}
};int main()
{Stack* p1 new Stack;delete[] p1;return 0;
}new/delete与new[]/delete[]不匹配使用可能会产生的风险与delete[]的工作原理 汇编调试 int main()
{Stack* p1 new Stack[10];delete[] p1;return 0;
}10个Stack类型的变量组成的空间大小应为120字节可是经过汇编调试后大小却为124字节。这是因为new[]申请连续的自定义类型空间时会额外在头部申请一个四字节大小的空间用来存放申请的自定义类型空间个数delete[]在析构时会向前调整四个字节读取需要调用析构的次数。delete获取需要调用析构的次数 当使用delete去释放new[] 出的空间时不会向前调整四个字节而是调用一次析构函数后从第一个元素的首地址释放空间会导致内存泄漏。 编译器的优化因为类A的成员变量只有一个int变量且构造时没有开辟额外空间当我们将类A的析构函数屏蔽后没有使用析构的必要当再次new[]一段连续空间时将不再于头部开辟额外空间存储元素个数。delete所要做的就只是释放开辟的空间这一点使用free也同样可以做到。 class A
{
private:int _a;
public:A(){cout A() endl;}/*~A(){cout ~A() endl;}*/
};int main()
{A* pa new A[10];//用delete去销毁new[]申请的空间delete pa;//free(pa);return 0;
}5. 定位new表达式与池化计数 在前面的学习中我们了解到类的析构函数可以显示调用而构造函数却不可以。那么当我们不使用new的方式开辟出自定义类型的空间后有没有办法对这段空间使用构造函数进行初始化呢接下来我们引入定位new表达式。 //调用默认构造
new(指针指定空间地址)类型
//赋予初始值
new(指针)类型(初始值)示例
class A
{
private:int _a;
public:A(int a 0):_a(a){cout A() endl;}~A(){cout ~A() endl;}
};int main()
{A* pa1 (A*)operator new(sizeof(A));//调用构造new(pa1)A;//析构pa1-~A();//释放空间operator delete(pa1);A* pa2 (A*)operator new(sizeof(A));//调用构造并指定初始值new(pa1)A(10);pa2-~A();operator delete(pa2);return 0;
}定位new应用场景 1 因为new申请动态开辟的空间是在堆上很多时候我们需要频繁的调用申请空间这样的效率非常低。 2 所以C中创建了内存池来应对这一类问题先提前申请出一大块空间备用这块空间被称为内存池当我们需要申请空间时不用再去堆上申请而是可以直接找内存池申请划分。 3 这些申请来的空间自定义类型是已经开辟好的这些空间没有调用构造函数进行初始化无法使用而定位new就可以解决这样的问题。 6. malloc/free与new/delete的异同 相同点 都是于堆区上开辟空间且都需要手动释放 不同点 malloc申请的空间不会进行初始化而new申请出的空间可以进行初始化malloc申请空间需要计算空间大小而new只需要指定对象个数malloc的返回必须要强转为对应类型的指针而new在申请空间时就声明了类型malloc申请空间失败返回空new申请失败抛出异常malloc开辟销毁空间时不会调用构造析构函数而new/delete开辟销毁空间时会调用构造与析构