✨️ 复制成功,转载请标注本文地址

内存管理

5582 字
28 分钟
内存管理

内存管理简介#

C++ 提供了多种内存管理方式,包括传统的 C 风格的 mallocfree,以及 C++ 专用的 newdelete

理解这些内存管理方法对于编写高效、安全的 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 用于释放之前由 malloccallocrealloc 分配的内存。其原型如下:

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++ 提供了更高层次的内存管理操作符:newdelete,它们不仅分配和释放内存,还调用构造函数和析构函数,提供类型安全。

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: 10
Object value: 10
Destructor 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] = 0
arr[1] = 10
arr[2] = 20
arr[3] = 30
arr[4] = 40

delete** 操作符**

用于释放由 new 分配的内存,并调用析构函数。

释放单个对象#

delete ptr;

释放数组#

delete[] ptr;

区别于 mallocfree#

  • 类型安全new 返回正确类型的指针,免去了强制类型转换。
  • 构造/析构newdelete 自动调用构造函数和析构函数。
  • 异常处理:在分配失败时,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/freenew/delete 的区别,有助于在编写 C++ 程序时正确选择内存管理方法。

特性malloc/freenew/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++ 对象时,需要手动调用构造函数和析构函数,这非常不便且易出错。因此,推荐使用 newdelete

**使用 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 called
Value: 30
Destructor called

兼容性#

  • C++ 类型特性newdelete 支持 C++ 的类型特性,包括构造函数、析构函数、多态等。
  • C 兼容性:在需要兼容 C 代码或通过 C 接口分配内存时,仍可能需要使用 mallocfree

高级内存管理#

使用 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 3
Reallocated array: 1 2 3 40 50

动态数组管理#

使用 mallocrealloc 来手动管理动态数组可以实现可变大小的数组,但需要处理内存分配、释放和数据复制。

封装动态数组

#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 3
Resized 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: 100
MyClass constructor: 200
obj1 value: 100
obj2 value: 200
MyClass destructor: 100
MyClass destructor: 200

说明

  • MemoryPool 管理固定大小的内存块,避免频繁调用 mallocfree
  • 使用“定位 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;
}
// 释放所有权,返回裸指针并设为 nullptr
T* 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: 1
Value: 1
Value: 1
ptr1 is now nullptr after move.
Value: 1
ptr2 is now nullptr after release.
Value: 1
Test Destructor: 1
Test Constructor: 2
Value: 2
Test Destructor: 2
ptr2 is now nullptr after reset.

解释

  • 创建 ptr1 并指向一个 Test 对象。
  • 使用 std::move 将所有权转移到 ptr2ptr1 变为 nullptr
  • 使用 release() 释放 ptr2 的所有权,获取裸指针后需要手动 delete
  • 使用 reset() 重新指向一个新的 Test 对象,自动释放之前的资源。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

内存管理
https://godze.online/posts/c-plus-plus/内存管理/
作者
Vafail
发布于
2026-05-21
许可协议
CC BY-NC-SA 4.0

评论区

公告
欢迎来到我的博客!
分类
标签
最新动态
站点统计
文章
61
分类
6
标签
8
总字数
136,467
运行时长
0
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
Firefly v6.15.3
文章许可
CC BY-NC-SA 4.0