动态创建对象
1. 什么是动态创建对象?
在学习之前的知识点时,我们知道有静态存储期和自动存储期。 静态存储期的对象在程序的整个生命周期内都存在,全局变量和static修饰的局部变量都属于这一类。自动存储期的对象,这些对象在函数或代码块的执行期间存在,函数结束时自动销毁。局部变量是典型的自动存储期对象,它们在函数内部定义,函数结束时自动释放。
那么有没有人为命令的可以存储或者删除的对象呢?那就是动态创建对象,它允许你在程序运行时根据需要动态分配和释放内存。在处理不确定数量的对象、延迟初始化或者管理大型资源时非常有用。
2. 如何动态创建对象呢?
- 动态创建对象是通过new运算符来分配内存,而delete运算符用于释放内存。
#include <iostream>
using namespace std;int main() {// 动态创建一个整数int* x = new int; // 分配一个整数的内存// 给动态分配的整数赋值*x = 42;// 输出动态分配的整数cout << "Value: " << *x << endl;// 释放内存delete x; // 释放内存return 0;
}
在内存中的动态变化是这样的:
2. 动态创建对象有以下初始化的方法:
3. 动态创建数组
如果需要动态创建一个数组,可以使用new[]和delete[]。
#include <iostream>
using namespace std;int main() {int size = 5;int* arr = new int[size]; // 动态分配一个整数数组// 给数组赋值for (int i = 0; i < size; i++) {arr[i] = i * 10;}// 输出数组内容for (int i = 0; i < size; i++) {cout << arr[i] << " ";}cout << endl;// 释放内存delete[] arr; // 释放数组内存return 0;
}
3.动态对象的存储位置–堆
- 堆(Heap)
堆是一个动态分配的内存区域,主要用于存储程序运行时需要灵活管理的资源。堆的大小通常是动态的,可以根据需要分配和释放。 - 堆的特点
手动管理:堆上的内存分配和释放需要程序员手动管理(使用new和delete)。
灵活大小:堆的大小可以动态扩展,适合存储大对象或动态数量的对象。
生命周期可控:堆上的对象生命周期由程序员控制,可以跨越函数调用。
访问速度较慢:堆的内存分配和释放速度比栈慢,因为需要额外的内存管理操作。
与之作为对比的栈存储区域,可以做一个简单的了解。
- 栈(stack)
栈是一个后进先出(LIFO,Last In, First Out)的内存区域,主要用于存储函数调用的上下文信息和局部变量。栈的大小通常是固定的,由操作系统在程序启动时分配。
- 栈的特点
自动管理:栈上的内存分配和释放是自动的,由编译器管理。
快速访问:栈的内存分配和释放速度非常快,因为它是连续的内存区域,且操作简单。
有限大小:栈的大小通常是有限的(例如几MB),不能动态扩展。
局部性:栈上的变量通常是函数内部的局部变量,生命周期与函数的调用相关。
4. 动态创建对象失败
如果由于内存空间不足,创建对象失败,通常会像下面这样处理:
抛出异常:默认情况下,C++中的new操作符会在内存分配失败时抛出std::bad_alloc异常。这使得程序在遇到内存分配失败时能够及时发现并处理错误。C++通过try、throw和catch三个关键字实现异常处理。try块用于包裹可能会抛出异常的代码。当try块中的代码抛出异常时,程序会跳转到相应的catch块处理异常。throw语句用于抛出一个异常。异常可以是任何类型,通常使用标准异常类或自定义异常类。catch块用于捕获和处理异常。可以有多个catch块来处理不同类型的异常。
例如:
// 循环动态创建数组对象(异常处理)#include <new>
#include <iostream>using namespace std;int main() {cout << "循环创建元素个数为30000的double型数组。\n";while (true) {try {double* a = new double[30000]; // 创建数组} catch (bad_alloc) {cout << "数组创建失败,程序中断。\n";return 1;}}
}
返回空指针:在某些情况下,程序员可能不希望因为内存分配失败而中断程序的执行。这时可以使用std::nothrow来防止抛出异常,而是返回一个空指针。如果在创建对象时指定“(nothrow)”,则也可以在不引起异常的情况下返回空指针,示例如下:
// 循环动态创建数组对象(抑制异常发生)#include <cstdlib>
#include <iostream>using namespace std;int main() {cout << "循环创建元素个数为30000的double型数组。\n";while (true) {double* a = new(nothrow) double[30000]; // 创建(抑制异常发生)if (a == NULL) {cout << "数组创建失败,程序中断。\n";return 1;}}
}
5. 使用void指针动态创建对象
void指针是一种特殊的指针,它可以指向任何数据类型。void指针的类型是void*。由于void指针不能直接访问它所指向的数据,因此需要进行类型转换。
#include <iostream>
using namespace std;int main() {int num = 42;void* ptr = # // ptr指向num的地址// 通过类型转换访问数据cout << "Value: " << *static_cast<int*>(ptr) << endl;return 0;
}
在动态内存分配中,需要使用malloc和calloc等函数返回void*,再进行类型转换。malloc函数用于分配一块未初始化的内存。见下面例子。
#include <iostream>
#include <cstdlib> // 用于malloc和free
using namespace std;int main() {// 动态分配一个整数int* ptrInt = static_cast<int*>(malloc(sizeof(int)));if (ptrInt != nullptr) {*ptrInt = 42;cout << "Integer value: " << *ptrInt << endl;free(ptrInt); // 释放内存}// 动态分配一个字符数组char* ptrChar = static_cast<char*>(malloc(10 * sizeof(char)));if (ptrChar != nullptr) {strcpy(ptrChar, "Hello");cout << "String: " << ptrChar << endl;free(ptrChar); // 释放内存}return 0;
}
#include <iostream>
#include <cstdlib> // 用于calloc和free
using namespace std;int main() {// 动态分配一个整数数组int* ptrIntArray = static_cast<int*>(calloc(5, sizeof(int)));if (ptrIntArray != nullptr) {for (int i = 0; i < 5; i++) {ptrIntArray[i] = i * 10;}cout << "Integer array values: ";for (int i = 0; i < 5; i++) {cout << ptrIntArray[i] << " ";}cout << endl;free(ptrIntArray); // 释放内存}// 动态分配一个字符数组char* ptrCharArray = static_cast<char*>(calloc(10, sizeof(char)));if (ptrCharArray != nullptr) {strcpy(ptrCharArray, "Hello");cout << "String: " << ptrCharArray << endl;free(ptrCharArray); // 释放内存}return 0;
}