C++ 智能指针详解:修订间差异
外观
Page creation by admin bot |
Page update by admin bot |
||
第1行: | 第1行: | ||
= C++智能指针详解 = | = C++智能指针详解 = | ||
智能指针是C++ | 智能指针是C++中用于自动化内存管理的工具,它们通过封装原始指针并提供自动内存释放机制,帮助开发者避免内存泄漏、悬垂指针等问题。C++标准库提供了多种智能指针类型,每种适用于不同的场景。 | ||
== | == 智能指针概述 == | ||
智能指针是类模板,它们的行为类似于指针,但在生命周期结束时自动释放所管理的内存。C++11引入了以下主要智能指针类型: | |||
* | * <code>std::unique_ptr</code> - 独占所有权指针 | ||
* | * <code>std::shared_ptr</code> - 共享所有权指针 | ||
* | * <code>std::weak_ptr</code> - 弱引用指针(配合shared_ptr使用) | ||
传统C++中使用<code>new</code>和<code>delete</code>手动管理内存容易出错: | |||
<syntaxhighlight lang="cpp"> | |||
void problematicFunction() { | |||
int* rawPtr = new int(42); // 分配内存 | |||
// ...使用指针... | |||
if (someCondition) { | |||
return; // 内存泄漏! | |||
} | |||
delete rawPtr; // 必须记得释放 | |||
} | |||
</syntaxhighlight> | |||
智能指针解决了这个问题: | |||
<syntaxhighlight lang="cpp"> | |||
#include <memory> | |||
void safeFunction() { | |||
std::unique_ptr<int> smartPtr(new int(42)); | |||
// ...使用指针... | |||
if (someCondition) { | |||
return; // 内存自动释放 | |||
} | |||
// 不需要手动delete | |||
} | |||
</syntaxhighlight> | |||
== | == std::unique_ptr == | ||
独占所有权指针,保证同一时间只有一个unique_ptr指向特定对象。 | |||
=== | === 基本用法 === | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include <memory> | #include <memory> | ||
第23行: | 第44行: | ||
int main() { | int main() { | ||
// | // 创建unique_ptr | ||
std::unique_ptr<int> | std::unique_ptr<int> ptr1(new int(10)); | ||
// | // 使用get()获取原始指针 | ||
std::cout << | std::cout << *ptr1.get() << std::endl; // 输出: 10 | ||
// 重置指针 | |||
ptr1.reset(new int(20)); // 自动释放之前的int(10) | |||
// 移动语义(所有权转移) | |||
std::unique_ptr<int> ptr2 = std::move(ptr1); | |||
if (!ptr1) { | |||
std::cout << "ptr1 is now empty" << std::endl; | |||
} | |||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === 所有权转移示意图 === | ||
< | <mermaid> | ||
graph LR | |||
</ | A[unique_ptr1 拥有对象] -->|move| B[unique_ptr2 拥有对象] | ||
A --> C[unique_ptr1 变为空] | |||
</mermaid> | |||
== | == std::shared_ptr == | ||
共享所有权指针,多个shared_ptr可以指向同一对象,使用引用计数管理生命周期。 | |||
=== | === 基本用法 === | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include <memory> | #include <memory> | ||
#include <iostream> | #include <iostream> | ||
class MyClass { | |||
public: | |||
MyClass() { std::cout << "MyClass constructed\n"; } | |||
~MyClass() { std::cout << "MyClass destroyed\n"; } | |||
}; | |||
int main() { | int main() { | ||
// | // 创建shared_ptr | ||
std::shared_ptr< | std::shared_ptr<MyClass> ptr1(new MyClass()); | ||
std::shared_ptr< | |||
{ | |||
// 复制构造,引用计数增加 | |||
std::shared_ptr<MyClass> ptr2 = ptr1; | |||
std::cout << "Reference count: " << ptr2.use_count() << std::endl; // 输出: 2 | |||
// | } // ptr2析构,引用计数减1 | ||
std::cout << " | |||
std::cout << "Reference count: " << ptr1.use_count() << std::endl; // 输出: 1 | |||
return 0; | |||
return 0; // ptr1析构,引用计数为0,对象被销毁 | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === 引用计数机制 === | ||
< | <mermaid> | ||
graph TD | |||
A[对象] --> B[shared_ptr1] | |||
A --> C[shared_ptr2] | |||
A --> D[shared_ptr3] | |||
= | B --> E[引用计数=3] | ||
</mermaid> | |||
== | == std::weak_ptr == | ||
弱引用指针,不增加引用计数,用于解决shared_ptr的循环引用问题。 | |||
=== | === 循环引用问题示例 === | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include <memory> | #include <memory> | ||
#include <iostream> | #include <iostream> | ||
class Node { | |||
public: | |||
std::shared_ptr<Node> next; | |||
~Node() { std::cout << "Node destroyed\n"; } | |||
}; | |||
int main() { | int main() { | ||
auto node1 = std::make_shared<Node>(); | |||
auto node2 = std::make_shared<Node>(); | |||
std:: | |||
node1->next = node2; | |||
node2->next = node1; // 循环引用,内存泄漏 | |||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === 使用weak_ptr解决 === | ||
< | <syntaxhighlight lang="cpp"> | ||
class SafeNode { | |||
</ | public: | ||
std::weak_ptr<SafeNode> next; // 使用weak_ptr打破循环 | |||
~SafeNode() { std::cout << "SafeNode destroyed\n"; } | |||
}; | |||
==== | int main() { | ||
auto node1 = std::make_shared<SafeNode>(); | |||
auto node2 = std::make_shared<SafeNode>(); | |||
node1->next = node2; | |||
node2->next = node1; // 不会造成循环引用 | |||
return 0; // 对象正常销毁 | |||
} | |||
</syntaxhighlight> | |||
== | == 智能指针创建方法 == | ||
推荐使用<code>std::make_shared</code>和<code>std::make_unique</code>(C++14): | |||
=== | <syntaxhighlight lang="cpp"> | ||
auto ptr1 = std::make_shared<int>(42); // 推荐 | |||
auto ptr2 = std::make_unique<double>(3.14); // C++14 | |||
// 而非: | |||
std::shared_ptr<int> ptr3(new int(42)); // 不推荐,可能造成内存泄漏 | |||
</syntaxhighlight> | |||
== | == 性能考虑 == | ||
* <code>unique_ptr</code>几乎无额外开销 | |||
* <code>shared_ptr</code>有引用计数开销 | |||
* <code>make_shared</code>通常比直接构造更高效 | |||
== | == 实际应用案例 == | ||
=== 资源管理 === | |||
<syntaxhighlight lang="cpp"> | |||
class FileHandler { | |||
private: | |||
std::unique_ptr<FILE, decltype(&fclose)> file_; | |||
public: | |||
FileHandler(const char* filename, const char* mode) | |||
: file_(fopen(filename, mode), &fclose) { | |||
if (!file_) throw std::runtime_error("File open failed"); | |||
} | |||
// 自动调用fclose | |||
}; | |||
</syntaxhighlight> | |||
=== 工厂模式 === | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
class Shape { | |||
public: | |||
virtual ~Shape() = default; | |||
virtual void draw() = 0; | |||
}; | |||
class Circle : public Shape { | |||
std:: | public: | ||
void draw() override { std::cout << "Drawing circle\n"; } | |||
}; | }; | ||
std::unique_ptr<Shape> createShape() { | |||
return std::make_unique<Circle>(); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== | == 常见问题 == | ||
=== 智能指针能否用于数组? === | |||
可以,但需要指定删除器或使用C++17的数组版本: | |||
<syntaxhighlight lang="cpp"> | |||
// C++11/14方式 | |||
std::unique_ptr<int[]> arr(new int[10]); | |||
std::shared_ptr<int> arr2(new int[10], std::default_delete<int[]>()); | |||
// C++17方式 | |||
std::shared_ptr<int[]> arr3(new int[10]); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== | === 何时使用原始指针? === | ||
智能指针不适用于: | |||
1. 需要与C API交互时 | |||
2. 性能极端敏感的场景 | |||
3. 需要指针算术运算时 | |||
== 总结 == | == 总结 == | ||
智能指针类型选择指南: | |||
* | * 优先使用<code>unique_ptr</code> - 简单、高效 | ||
* | * 需要共享所有权时使用<code>shared_ptr</code> | ||
* | * 需要观察但不拥有对象时使用<code>weak_ptr</code> | ||
* 避免混合使用智能指针和原始指针 | |||
智能指针大大简化了C++内存管理,是现代C++编程中不可或缺的工具。 | |||
[[Category:编程语言]] | [[Category:编程语言]] | ||
[[Category:C++]] | [[Category:C++]] | ||
[[Category:C++ | [[Category:C++ 内存管理]] |
2025年4月28日 (一) 21:30的最新版本
C++智能指针详解[编辑 | 编辑源代码]
智能指针是C++中用于自动化内存管理的工具,它们通过封装原始指针并提供自动内存释放机制,帮助开发者避免内存泄漏、悬垂指针等问题。C++标准库提供了多种智能指针类型,每种适用于不同的场景。
智能指针概述[编辑 | 编辑源代码]
智能指针是类模板,它们的行为类似于指针,但在生命周期结束时自动释放所管理的内存。C++11引入了以下主要智能指针类型:
std::unique_ptr
- 独占所有权指针std::shared_ptr
- 共享所有权指针std::weak_ptr
- 弱引用指针(配合shared_ptr使用)
传统C++中使用new
和delete
手动管理内存容易出错:
void problematicFunction() {
int* rawPtr = new int(42); // 分配内存
// ...使用指针...
if (someCondition) {
return; // 内存泄漏!
}
delete rawPtr; // 必须记得释放
}
智能指针解决了这个问题:
#include <memory>
void safeFunction() {
std::unique_ptr<int> smartPtr(new int(42));
// ...使用指针...
if (someCondition) {
return; // 内存自动释放
}
// 不需要手动delete
}
std::unique_ptr[编辑 | 编辑源代码]
独占所有权指针,保证同一时间只有一个unique_ptr指向特定对象。
基本用法[编辑 | 编辑源代码]
#include <memory>
#include <iostream>
int main() {
// 创建unique_ptr
std::unique_ptr<int> ptr1(new int(10));
// 使用get()获取原始指针
std::cout << *ptr1.get() << std::endl; // 输出: 10
// 重置指针
ptr1.reset(new int(20)); // 自动释放之前的int(10)
// 移动语义(所有权转移)
std::unique_ptr<int> ptr2 = std::move(ptr1);
if (!ptr1) {
std::cout << "ptr1 is now empty" << std::endl;
}
return 0;
}
所有权转移示意图[编辑 | 编辑源代码]
[编辑 | 编辑源代码]
共享所有权指针,多个shared_ptr可以指向同一对象,使用引用计数管理生命周期。
基本用法[编辑 | 编辑源代码]
#include <memory>
#include <iostream>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
};
int main() {
// 创建shared_ptr
std::shared_ptr<MyClass> ptr1(new MyClass());
{
// 复制构造,引用计数增加
std::shared_ptr<MyClass> ptr2 = ptr1;
std::cout << "Reference count: " << ptr2.use_count() << std::endl; // 输出: 2
} // ptr2析构,引用计数减1
std::cout << "Reference count: " << ptr1.use_count() << std::endl; // 输出: 1
return 0; // ptr1析构,引用计数为0,对象被销毁
}
引用计数机制[编辑 | 编辑源代码]
std::weak_ptr[编辑 | 编辑源代码]
弱引用指针,不增加引用计数,用于解决shared_ptr的循环引用问题。
循环引用问题示例[编辑 | 编辑源代码]
#include <memory>
#include <iostream>
class Node {
public:
std::shared_ptr<Node> next;
~Node() { std::cout << "Node destroyed\n"; }
};
int main() {
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1; // 循环引用,内存泄漏
return 0;
}
使用weak_ptr解决[编辑 | 编辑源代码]
class SafeNode {
public:
std::weak_ptr<SafeNode> next; // 使用weak_ptr打破循环
~SafeNode() { std::cout << "SafeNode destroyed\n"; }
};
int main() {
auto node1 = std::make_shared<SafeNode>();
auto node2 = std::make_shared<SafeNode>();
node1->next = node2;
node2->next = node1; // 不会造成循环引用
return 0; // 对象正常销毁
}
智能指针创建方法[编辑 | 编辑源代码]
推荐使用std::make_shared
和std::make_unique
(C++14):
auto ptr1 = std::make_shared<int>(42); // 推荐
auto ptr2 = std::make_unique<double>(3.14); // C++14
// 而非:
std::shared_ptr<int> ptr3(new int(42)); // 不推荐,可能造成内存泄漏
性能考虑[编辑 | 编辑源代码]
unique_ptr
几乎无额外开销shared_ptr
有引用计数开销make_shared
通常比直接构造更高效
实际应用案例[编辑 | 编辑源代码]
资源管理[编辑 | 编辑源代码]
class FileHandler {
private:
std::unique_ptr<FILE, decltype(&fclose)> file_;
public:
FileHandler(const char* filename, const char* mode)
: file_(fopen(filename, mode), &fclose) {
if (!file_) throw std::runtime_error("File open failed");
}
// 自动调用fclose
};
工厂模式[编辑 | 编辑源代码]
class Shape {
public:
virtual ~Shape() = default;
virtual void draw() = 0;
};
class Circle : public Shape {
public:
void draw() override { std::cout << "Drawing circle\n"; }
};
std::unique_ptr<Shape> createShape() {
return std::make_unique<Circle>();
}
常见问题[编辑 | 编辑源代码]
智能指针能否用于数组?[编辑 | 编辑源代码]
可以,但需要指定删除器或使用C++17的数组版本:
// C++11/14方式
std::unique_ptr<int[]> arr(new int[10]);
std::shared_ptr<int> arr2(new int[10], std::default_delete<int[]>());
// C++17方式
std::shared_ptr<int[]> arr3(new int[10]);
何时使用原始指针?[编辑 | 编辑源代码]
智能指针不适用于: 1. 需要与C API交互时 2. 性能极端敏感的场景 3. 需要指针算术运算时
总结[编辑 | 编辑源代码]
智能指针类型选择指南:
- 优先使用
unique_ptr
- 简单、高效 - 需要共享所有权时使用
shared_ptr
- 需要观察但不拥有对象时使用
weak_ptr
- 避免混合使用智能指针和原始指针
智能指针大大简化了C++内存管理,是现代C++编程中不可或缺的工具。