内存管理
内存管理简介
C++ 提供了多种内存管理方式,包括传统的 C 风格的 malloc 和 free,以及 C++ 专用的 new 和 delete。
理解这些内存管理方法对于编写高效、安全的 C++ 程序至关重要。
本文将详细介绍这些内存管理方法,包含基本用法、复杂操作(如 realloc),并配以实际案例和代码示例。
内存管理基础
在 C++ 程序中,内存主要分为以下几个区域:
- 栈(Stack):自动管理内存,存储局部变量和函数调用信息。内存分配和释放速度快,但空间有限。
- 堆(Heap):手动管理内存,用于动态分配内存。内存分配和释放由程序员控制,灵活但易出错(如内存泄漏、悬挂指针)。
- 全局/静态区(
Data/BSS Segment):存储全局变量和静态变量。
了解栈和堆的区别,以及如何有效地在堆上分配和管理内存,是编写高效且安全的 C++ 程序的基础。
C 风格内存管理
malloc 函数
malloc(memory allocation)用于在堆上分配指定字节数的内存。其原型如下:
#include <cstdlib>
void* malloc(size_t size);- 参数:
size- 要分配的内存字节数。 - 返回值:指向分配内存的指针,如果分配失败则返回
nullptr。
free 函数
free 用于释放之前由 malloc、calloc 或 realloc 分配的内存。其原型如下:
void free(void* ptr);- 参数:
ptr- 要释放的内存指针。
示例代码
#include <iostream>#include <cstdlib> // 包含 malloc 和 free
int main() { // 分配一个整数的内存 int* p = (int*)malloc(sizeof(int)); if (p == nullptr) { std::cerr << "Memory allocation failed" << std::endl; return 1; }
*p = 42; std::cout << "Value: " << *p << std::endl;
// 释放内存 free(p); return 0;}注意事项
- 类型转换:
malloc返回void*,需要显式转换为所需类型的指针。 - 初始化:
malloc分配的内存未初始化,内容不确定。 - 释放对应性:由
malloc分配的内存必须使用free释放,避免使用delete。
C++ 内存管理
C++ 提供了更高层次的内存管理操作符:new 和 delete,它们不仅分配和释放内存,还调用构造函数和析构函数,提供类型安全。
new** 操作符**
用于在堆上分配对象,并调用其构造函数。
单个对象
Type* ptr = new Type(parameters);- 例子:
#include <iostream>
class MyClass {public: MyClass(int val) : value(val) { std::cout << "Constructor called with value: " << value << std::endl; } ~MyClass() { std::cout << "Destructor called for value: " << value << std::endl; } int value;};
int main() { MyClass* obj = new MyClass(10); std::cout << "Object value: " << obj->value << std::endl; delete obj; // 调用析构函数并释放内存 return 0;}输出:
Constructor called with value: 10Object value: 10Destructor called for value: 10数组
Type* array = new Type[size];- 例子:
#include <iostream>
int main() { int* arr = new int[5]; // 分配5个整数 for (int i = 0; i < 5; ++i) { arr[i] = i * 10; } for (int i = 0; i < 5; ++i) { std::cout << "arr[" << i << "] = " << arr[i] << std::endl; } delete[] arr; // 释放数组内存 return 0;}输出:
arr[0] = 0arr[1] = 10arr[2] = 20arr[3] = 30arr[4] = 40delete** 操作符**
用于释放由 new 分配的内存,并调用析构函数。
释放单个对象
delete ptr;释放数组
delete[] ptr;区别于 malloc 和 free
- 类型安全:
new返回正确类型的指针,免去了强制类型转换。 - 构造/析构:
new和delete自动调用构造函数和析构函数。 - 异常处理:在分配失败时,
new默认抛出std::bad_alloc异常,而malloc返回nullptr。
异常安全的 new
可以通过 nothrow 参数防止 new 抛出异常,改为返回 nullptr。
#include <iostream>#include <new> // 包含 std::nothrow
int main() { int* p = new(std::nothrow) int; if (p == nullptr) { std::cerr << "Memory allocation failed" << std::endl; return 1; } *p = 100; std::cout << "Value: " << *p << std::endl; delete p; return 0;}总结和对比
了解 malloc/free 与 new/delete 的区别,有助于在编写 C++ 程序时正确选择内存管理方法。
| 特性 | malloc/free | new/delete |
|---|---|---|
| 类型安全 | 需要显式类型转换 | 自动类型转换,无需显式转换 |
| 构造/析构函数 | 不调用对象的构造/析构函数 | 调用对象的构造/析构函数 |
| 返回值 | void*,需要转换为目标类型 | 返回目标类型指针,类型安全 |
| 错误处理 | 分配失败返回 nullptr | 分配失败抛出 std::bad_alloc 异常 |
| 多态行为 | 无 | 支持多态,通过虚函数正确调用析构函数 |
| 内存分配与释放对应性 | 必须使用 free 释放由 malloc 分配的内存 | 必须使用 delete 释放由 new 分配的内存 |
示例对比
**使用 malloc 和 **free
#include <iostream>#include <cstdlib>
class MyClass {public: MyClass(int val) : value(val) { std::cout << "Constructor called" << std::endl; } ~MyClass() { std::cout << "Destructor called" << std::endl; } int value;};
int main() { // 使用 malloc 分配内存 MyClass* obj = (MyClass*)malloc(sizeof(MyClass)); if (obj == nullptr) { std::cerr << "malloc failed" << std::endl; return 1; }
// 手动调用构造函数(不推荐) new(obj) MyClass(20); // 通过“定位 new”调用构造函数
std::cout << "Value: " << obj->value << std::endl;
// 手动调用析构函数 obj->~MyClass();
// 释放内存 free(obj); return 0;}注意:使用 malloc 分配 C++ 对象时,需要手动调用构造函数和析构函数,这非常不便且易出错。因此,推荐使用 new 和 delete。
**使用 new 和 **delete
#include <iostream>
class MyClass {public: MyClass(int val) : value(val) { std::cout << "Constructor called" << std::endl; } ~MyClass() { std::cout << "Destructor called" << std::endl; } int value;};
int main() { // 使用 new 分配内存并调用构造函数 MyClass* obj = new MyClass(30); std::cout << "Value: " << obj->value << std::endl;
// 使用 delete 释放内存并调用析构函数 delete obj; return 0;}输出:
Constructor calledValue: 30Destructor called兼容性
- C++ 类型特性:
new和delete支持 C++ 的类型特性,包括构造函数、析构函数、多态等。 - C 兼容性:在需要兼容 C 代码或通过 C 接口分配内存时,仍可能需要使用
malloc和free。
高级内存管理
使用 realloc 进行内存重分配
realloc 用于调整之前分配的内存块大小。这在动态数组等数据结构中非常有用。
原型
#include <cstdlib>
void* realloc(void* ptr, size_t new_size);- 参数:
ptr:指向之前分配的内存块。new_size:新的内存大小(以字节为单位)。
- 返回值:指向重新分配后的内存块的新指针。如果重新分配失败,返回
nullptr,原内存块保持不变。
示例代码
#include <iostream>#include <cstdlib>#include <cstring> // 包含 memcpy
int main() { // 初始分配 3 个整数 int* arr = (int*)malloc(3 * sizeof(int)); if (arr == nullptr) { std::cerr << "Initial malloc failed" << std::endl; return 1; }
// 初始化数组 for (int i = 0; i < 3; ++i) { arr[i] = i + 1; }
std::cout << "Initial array: "; for (int i = 0; i < 3; ++i) { std::cout << arr[i] << " "; } std::cout << std::endl;
// 重新分配为 5 个整数 int* temp = (int*)realloc(arr, 5 * sizeof(int)); if (temp == nullptr) { std::cerr << "Realloc failed" << std::endl; free(arr); // 释放原内存 return 1; } arr = temp;
// 初始化新元素 for (int i = 3; i < 5; ++i) { arr[i] = (i + 1) * 10; }
std::cout << "Reallocated array: "; for (int i = 0; i < 5; ++i) { std::cout << arr[i] << " "; } std::cout << std::endl;
// 释放内存 free(arr); return 0;}输出:
Initial array: 1 2 3Reallocated array: 1 2 3 40 50动态数组管理
使用 malloc 和 realloc 来手动管理动态数组可以实现可变大小的数组,但需要处理内存分配、释放和数据复制。
封装动态数组
#include <iostream>#include <cstdlib>#include <cstring>
class DynamicArray {public: DynamicArray(size_t initial_size = 1) : size(initial_size), data((int*)malloc(size * sizeof(int))) { if (data == nullptr) { throw std::bad_alloc(); } }
~DynamicArray() { free(data); }
void resize(size_t new_size) { int* temp = (int*)realloc(data, new_size * sizeof(int)); if (temp == nullptr) { throw std::bad_alloc(); } data = temp; size = new_size; }
int& operator[](size_t index) { return data[index]; }
size_t getSize() const { return size; }
private: size_t size; int* data;};
int main() { try { DynamicArray arr(3); // 初始化 for (size_t i = 0; i < arr.getSize(); ++i) { arr[i] = i + 1; }
std::cout << "Initial array: "; for (size_t i = 0; i < arr.getSize(); ++i) { std::cout << arr[i] << " "; } std::cout << std::endl;
// 调整大小 arr.resize(5); arr[3] = 40; arr[4] = 50;
std::cout << "Resized array: "; for (size_t i = 0; i < arr.getSize(); ++i) { std::cout << arr[i] << " "; } std::cout << std::endl;
} catch (const std::bad_alloc& e) { std::cerr << "Memory allocation error: " << e.what() << std::endl; return 1; }
return 0;}输出:
Initial array: 1 2 3Resized array: 1 2 3 40 50注意:这种方式需要手动管理内存和数组大小,且缺乏类型安全性和自动化。推荐使用 C++ 标准容器如 std::vector 来代替。
实际案例
案例一:动态数组实现
实现一个简单的动态数组类,支持添加元素、访问元素和自动扩展。
代码示例
#include <iostream>#include <cstdlib>#include <stdexcept>
class DynamicArray {public: DynamicArray() : capacity(2), size(0), data((int*)malloc(capacity * sizeof(int))) { if (data == nullptr) { throw std::bad_alloc(); } }
~DynamicArray() { free(data); }
void add(int value) { if (size == capacity) { resize(capacity * 2); } data[size++] = value; }
int get(size_t index) const { if (index >= size) { throw std::out_of_range("Index out of range"); } return data[index]; }
size_t getSize() const { return size; }
private: void resize(size_t new_capacity) { int* temp = (int*)realloc(data, new_capacity * sizeof(int)); if (temp == nullptr) { throw std::bad_alloc(); } data = temp; capacity = new_capacity; }
size_t capacity; size_t size; int* data;};
int main() { try { DynamicArray arr; arr.add(10); arr.add(20); arr.add(30); // 触发扩展
std::cout << "Dynamic Array Contents:" << std::endl; for (size_t i = 0; i < arr.getSize(); ++i) { std::cout << arr.get(i) << " "; } std::cout << std::endl; } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation error: " << e.what() << std::endl; return 1; } catch (const std::out_of_range& e) { std::cerr << "Array access error: " << e.what() << std::endl; return 1; }
return 0;}输出:
Dynamic Array Contents:10 20 30案例二:自定义内存管理器
实现一个简单的内存池,用于高效分配和释放固定大小的对象。
代码示例
#include <iostream>#include <cstdlib>#include <stack>
class MemoryPool {public: MemoryPool(size_t objectSize, size_t poolSize) : objSize(objectSize), totalSize(poolSize), pool((char*)malloc(objectSize * poolSize)) { if (pool == nullptr) { throw std::bad_alloc(); } // 初始化 free list for (size_t i = 0; i < poolSize; ++i) { freeList.push(pool + i * objectSize); } }
~MemoryPool() { free(pool); }
void* allocate() { if (freeList.empty()) { throw std::bad_alloc(); } void* ptr = freeList.top(); freeList.pop(); return ptr; }
void deallocate(void* ptr) { freeList.push((char*)ptr); }
private: size_t objSize; size_t totalSize; char* pool; std::stack<void*> freeList;};
class MyClass {public: MyClass(int val) : value(val) { std::cout << "MyClass constructor: " << value << std::endl; } ~MyClass() { std::cout << "MyClass destructor: " << value << std::endl; } int value;};
int main() { try { // 创建一个能容纳 3 个 MyClass 对象的内存池 MemoryPool pool(sizeof(MyClass), 3);
// 分配对象内存 void* mem1 = pool.allocate(); void* mem2 = pool.allocate();
// 使用“定位 new”构造对象 MyClass* obj1 = new(mem1) MyClass(100); MyClass* obj2 = new(mem2) MyClass(200);
// 使用对象 std::cout << "obj1 value: " << obj1->value << std::endl; std::cout << "obj2 value: " << obj2->value << std::endl;
// 显式调用析构函数 obj1->~MyClass(); obj2->~MyClass();
// 释放内存 pool.deallocate(mem1); pool.deallocate(mem2);
} catch (const std::bad_alloc& e) { std::cerr << "Memory pool allocation error: " << e.what() << std::endl; return 1; }
return 0;}输出:
MyClass constructor: 100MyClass constructor: 200obj1 value: 100obj2 value: 200MyClass destructor: 100MyClass destructor: 200说明:
- MemoryPool 管理固定大小的内存块,避免频繁调用
malloc和free。 - 使用“定位 new”在预分配的内存上构造对象。
- 需要手动调用析构函数和将内存返回给内存池。
注意:这种方法适用于大量小对象的高效管理,但需要确保正确使用构造和析构函数。
避免内存泄漏
内存泄漏是指程序分配的内存未被释放,导致内存被浪费,甚至耗尽。避免内存泄漏的策略包括:
- **确保每个
new有对应的 **delete。 - 使用
RAII和智能指针:自动管理资源,避免手动管理内存。 - 工具辅助:使用工具如
Valgrind检测内存泄漏。
示例:内存泄漏
#include <iostream>
int main() { int* p = new int(10); // 忘记 delete p; 导致内存泄漏 return 0;}解决方法:
#include <iostream>
int main() { int* p = new int(10); // 正确释放内存 delete p; return 0;}RAII(资源获取即初始化)
RAII 是 C++ 中的一种编程惯用法,通过对象的生命周期管理资源,确保资源在对象构造时获取,析构时释放,避免泄漏。
示例:RAII** 实现类似于**shared_ptr智能指针
std::shared_ptr 是 C++ 标准库中功能强大的智能指针之一,提供了共享所有权的能力,使得多个指针可以共同管理同一个动态分配的对象。通过引用计数机制,shared_ptr 确保了对象在最后一个指针被销毁时自动释放,极大地简化了内存管理,防止了内存泄漏和悬挂指针问题。
SimpleSharedPtr** 的基本概念**
SimpleSharedPtr 是一个简化版的 shared_ptr 实现,旨在帮助理解其核心机制。其基本功能包括:
- 共享所有权:多个
SimpleSharedPtr实例可以指向同一个对象,共享对该对象的所有权。 - 自动管理生命周期:当最后一个
SimpleSharedPtr被销毁或指向其他对象时,管理的对象被自动释放。 - 引用计数:内部维护一个引用计数,记录有多少个
SimpleSharedPtr实例指向同一个对象。
引用计数控制块的设计
为了实现引用计数机制,SimpleSharedPtr 需要一个控制块(Control Block),它包含:
- 引用计数(
ref_count):记录有多少个SimpleSharedPtr指向同一个对象。 - 指向对象的指针(
ptr):指向实际管理的对象。
控制块通常与被管理对象一起被分配,但为了简化实现,本示例将它们独立管理。
struct ControlBlock { int ref_count; // 引用计数 // 可以扩展为包含自定义删除器等
ControlBlock() : ref_count(1) {}};SimpleSharedPtr 的实现
类结构
SimpleSharedPtr 是一个模板类,模板参数 T 表示它所管理的对象类型。
template <typename T>class SimpleSharedPtr {private: T* ptr; // 指向管理的对象 ControlBlock* control; // 指向控制块
public: // 构造函数、析构函数、拷贝与移动操作、操作符重载等};构造函数与析构函数
- 默认构造函数:初始化指针和控制块为空。
- 参数化构造函数:接受一个裸指针,初始化控制块,并引用计数为1。
- 析构函数:减少引用计数,若引用计数为0,则释放对象和控制块。
// 默认构造函数SimpleSharedPtr() : ptr(nullptr), control(nullptr) {}
// 参数化构造函数explicit SimpleSharedPtr(T* p) : ptr(p) { if (p) { control = new ControlBlock(); } else { control = nullptr; }}
// 析构函数~SimpleSharedPtr() { release();}**辅助函数 **release:
private:void release() { if (control) { control->ref_count--; if (control->ref_count == 0) { delete ptr; delete control; } } ptr = nullptr; control = nullptr;}拷贝构造与拷贝赋值
拷贝构造函数和拷贝赋值操作符允许多个 SimpleSharedPtr 实例共享同一个对象,共享相同的控制块。
// 拷贝构造函数SimpleSharedPtr(const SimpleSharedPtr& other) : ptr(other.ptr), control(other.control) { if (control) { control->ref_count++; }}
// 拷贝赋值操作符SimpleSharedPtr& operator=(const SimpleSharedPtr& other) { if (this != &other) { // 释放当前资源 release();
// 复制新的资源和控制块 ptr = other.ptr; control = other.control; if (control) { control->ref_count++; } } return *this;}移动构造与移动赋值
移动语义允许资源所有权从一个 SimpleSharedPtr 转移到另一个,而不增加引用计数。
// 移动构造函数SimpleSharedPtr(SimpleSharedPtr&& other) noexcept : ptr(other.ptr), control(other.control) { other.ptr = nullptr; other.control = nullptr;}
// 移动赋值操作符SimpleSharedPtr& operator=(SimpleSharedPtr&& other) noexcept { if (this != &other) { // 释放当前资源 release();
// 接管 `other` 的资源 ptr = other.ptr; control = other.control;
// 置 `other` 为空 other.ptr = nullptr; other.control = nullptr; } return *this;}操作符重载
重载 * 和 -> 操作符,以便像使用原生指针一样使用 SimpleSharedPtr。
// 解引用操作符T& operator*() const { return *ptr;}
// 箭头操作符T* operator->() const { return ptr;}其他成员函数
use_count:返回当前引用计数。get:返回裸指针。reset:重置指针,指向新对象或nullptr。
// 获取引用计数int use_count() const { return control ? control->ref_count : 0;}
// 获取裸指针T* get() const { return ptr;}
// 重置指针void reset(T* p = nullptr) { // 释放当前资源 release();
// 指向新资源 ptr = p; if (p) { control = new ControlBlock(); } else { control = nullptr; }}完整代码示例
以下是 SimpleSharedPtr 的完整实现及其使用示例。
#include <iostream>
// 控制块结构struct ControlBlock { int ref_count;
ControlBlock() : ref_count(1) {}};
// 简化版的 shared_ptr 实现template <typename T>class SimpleSharedPtr {private: T* ptr; // 指向管理的对象 ControlBlock* control; // 指向控制块
// 释放当前资源 void release() { if (control) { control->ref_count--; std::cout << "Decremented ref_count to " << control->ref_count << std::endl; if (control->ref_count == 0) { delete ptr; delete control; std::cout << "Resource and ControlBlock destroyed." << std::endl; } } ptr = nullptr; control = nullptr; }
public: // 默认构造函数 SimpleSharedPtr() : ptr(nullptr), control(nullptr) { std::cout << "Default constructed SimpleSharedPtr (nullptr)." << std::endl; }
// 参数化构造函数 explicit SimpleSharedPtr(T* p) : ptr(p) { if (p) { control = new ControlBlock(); std::cout << "Constructed SimpleSharedPtr, ref_count = " << control->ref_count << std::endl; } else { control = nullptr; } }
// 拷贝构造函数 SimpleSharedPtr(const SimpleSharedPtr& other) : ptr(other.ptr), control(other.control) { if (control) { control->ref_count++; std::cout << "Copied SimpleSharedPtr, ref_count = " << control->ref_count << std::endl; } }
// 拷贝赋值操作符 SimpleSharedPtr& operator=(const SimpleSharedPtr& other) { if (this != &other) { release(); ptr = other.ptr; control = other.control; if (control) { control->ref_count++; std::cout << "Assigned SimpleSharedPtr, ref_count = " << control->ref_count << std::endl; } } return *this; }
// 移动构造函数 SimpleSharedPtr(SimpleSharedPtr&& other) noexcept : ptr(other.ptr), control(other.control) { other.ptr = nullptr; other.control = nullptr; std::cout << "Moved SimpleSharedPtr." << std::endl; }
// 移动赋值操作符 SimpleSharedPtr& operator=(SimpleSharedPtr&& other) noexcept { if (this != &other) { release(); ptr = other.ptr; control = other.control; other.ptr = nullptr; other.control = nullptr; std::cout << "Move-assigned SimpleSharedPtr." << std::endl; } return *this; }
// 析构函数 ~SimpleSharedPtr() { release(); }
// 解引用操作符 T& operator*() const { return *ptr; }
// 箭头操作符 T* operator->() const { return ptr; }
// 获取引用计数 int use_count() const { return control ? control->ref_count : 0; }
// 获取裸指针 T* get() const { return ptr; }
// 重置指针 void reset(T* p = nullptr) { release(); ptr = p; if (p) { control = new ControlBlock(); std::cout << "Reset SimpleSharedPtr, ref_count = " << control->ref_count << std::endl; } else { control = nullptr; } }};
// 测试类class Test {public: Test(int val) : value(val) { std::cout << "Test Constructor: " << value << std::endl; } ~Test() { std::cout << "Test Destructor: " << value << std::endl; } void show() const { std::cout << "Value: " << value << std::endl; }
private: int value;};
int main() { std::cout << "Creating default constructed shared_ptr..." << std::endl; SimpleSharedPtr<Test> ptr1; // 默认构造 std::cout << "ptr1 use_count: " << ptr1.use_count() << std::endl;
std::cout << "\nCreating shared_ptr with resource..." << std::endl; SimpleSharedPtr<Test> ptr2(new Test(100)); // 非默认构造 std::cout << "ptr2 use_count: " << ptr2.use_count() << std::endl; ptr2->show();
std::cout << "\nCopying ptr2 to ptr3..." << std::endl; SimpleSharedPtr<Test> ptr3 = ptr2; // 拷贝构造 std::cout << "ptr2 use_count: " << ptr2.use_count() << std::endl; std::cout << "ptr3 use_count: " << ptr3.use_count() << std::endl; ptr3->show();
std::cout << "\nAssigning ptr3 to ptr1..." << std::endl; ptr1 = ptr3; // 拷贝赋值 std::cout << "ptr1 use_count: " << ptr1.use_count() << std::endl; std::cout << "ptr2 use_count: " << ptr2.use_count() << std::endl; std::cout << "ptr3 use_count: " << ptr3.use_count() << std::endl;
std::cout << "\nResetting ptr2..." << std::endl; ptr2.reset(new Test(200)); // 重新指向新的对象 std::cout << "ptr2 use_count: " << ptr2.use_count() << std::endl; ptr2->show(); std::cout << "ptr1 use_count: " << ptr1.use_count() << std::endl; std::cout << "ptr3 use_count: " << ptr3.use_count() << std::endl;
std::cout << "\nExiting scope..." << std::endl; } // ptr2, ptr1, ptr3 离开作用域
std::cout << "End of main." << std::endl; return 0;}SimpleUniquePtr 的实现
std::unique_ptr 是一种独占所有权的智能指针,确保在任意时刻,只有一个 unique_ptr 实例指向特定资源。它不支持拷贝操作,只支持移动操作。
基本结构
首先,定义一个模板类 SimpleUniquePtr,它持有一个指向资源的裸指针:
template <typename T>class SimpleUniquePtr {private: T* ptr; // 指向管理对象的裸指针
public: // 构造函数、析构函数、删除拷贝构造与拷贝赋值 // 实现移动构造与移动赋值 // 重载操作符};构造函数与析构函数
- 默认构造函数:初始化指针为空。
- 参数化构造函数:接受一个指向资源的裸指针。
- 析构函数:当
SimpleUniquePtr被销毁时,释放所管理的资源。
// 默认构造函数SimpleUniquePtr() : ptr(nullptr) {}
// 参数化构造函数explicit SimpleUniquePtr(T* p) : ptr(p) {}
// 析构函数~SimpleUniquePtr() { delete ptr;}删除拷贝构造与拷贝赋值
为了确保唯一性,禁止拷贝构造和拷贝赋值:
// 删除拷贝构造SimpleUniquePtr(const SimpleUniquePtr&) = delete;
// 删除拷贝赋值SimpleUniquePtr& operator=(const SimpleUniquePtr&) = delete;移动语义
支持移动构造和移动赋值,以转移所有权:
// 移动构造SimpleUniquePtr(SimpleUniquePtr&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr;}
// 移动赋值SimpleUniquePtr& operator=(SimpleUniquePtr&& other) noexcept { if (this != &other) { delete ptr; // 释放当前资源 ptr = other.ptr; // 转移所有权 other.ptr = nullptr; } return *this;}操作符重载
重载 * 和 -> 操作符,以模拟指针的行为:
// 解引用操作符T& operator*() const { return *ptr;}
// 箭头操作符T* operator->() const { return ptr;}
// 获取裸指针T* get() const { return ptr;}
// 释放所有权,返回裸指针并设为 nullptrT* release() { T* temp = ptr; ptr = nullptr; return temp;}
// 重新设定指针void reset(T* p = nullptr) { delete ptr; ptr = p;}示例代码
以下示例展示了如何使用 SimpleUniquePtr:
#include <iostream>
// SimpleUniquePtr 实现template <typename T>class SimpleUniquePtr {private: T* ptr;
public: // 默认构造函数 SimpleUniquePtr() : ptr(nullptr) {}
// 参数化构造函数 explicit SimpleUniquePtr(T* p) : ptr(p) {}
// 析构函数 ~SimpleUniquePtr() { delete ptr; }
// 删除拷贝构造和拷贝赋值 SimpleUniquePtr(const SimpleUniquePtr&) = delete; SimpleUniquePtr& operator=(const SimpleUniquePtr&) = delete;
// 移动构造 SimpleUniquePtr(SimpleUniquePtr&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; }
// 移动赋值 SimpleUniquePtr& operator=(SimpleUniquePtr&& other) noexcept { if (this != &other) { delete ptr; ptr = other.ptr; other.ptr = nullptr; } return *this; }
// 解引用操作符 T& operator*() const { return *ptr; }
// 箭头操作符 T* operator->() const { return ptr; }
// 获取裸指针 T* get() const { return ptr; }
// 释放所有权 T* release() { T* temp = ptr; ptr = nullptr; return temp; }
// 重新设定指针 void reset(T* p = nullptr) { delete ptr; ptr = p; }};
// 测试类class Test {public: Test(int val) : value(val) { std::cout << "Test Constructor: " << value << std::endl; } ~Test() { std::cout << "Test Destructor: " << value << std::endl; } void show() const { std::cout << "Value: " << value << std::endl; }
private: int value;};
int main() { // 创建一个 SimpleUniquePtr SimpleUniquePtr<Test> ptr1(new Test(1)); ptr1->show(); (*ptr1).show();
// 移动所有权到 ptr2 SimpleUniquePtr<Test> ptr2 = std::move(ptr1); if (ptr1.get() == nullptr) { std::cout << "ptr1 is now nullptr after move." << std::endl; } ptr2->show();
// 释放所有权 Test* rawPtr = ptr2.release(); if (ptr2.get() == nullptr) { std::cout << "ptr2 is now nullptr after release." << std::endl; } rawPtr->show(); delete rawPtr; // 手动删除
// 使用 reset ptr2.reset(new Test(2)); ptr2->show();
ptr2.reset(); // 自动删除 if (ptr2.get() == nullptr) { std::cout << "ptr2 is now nullptr after reset." << std::endl; }
return 0;}输出:
Test Constructor: 1Value: 1Value: 1ptr1 is now nullptr after move.Value: 1ptr2 is now nullptr after release.Value: 1Test Destructor: 1Test Constructor: 2Value: 2Test Destructor: 2ptr2 is now nullptr after reset.解释:
- 创建
ptr1并指向一个Test对象。 - 使用
std::move将所有权转移到ptr2,ptr1变为nullptr。 - 使用
release()释放ptr2的所有权,获取裸指针后需要手动delete。 - 使用
reset()重新指向一个新的Test对象,自动释放之前的资源。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!







