C++ STL <memory>
C++标准库中的<memory>
头文件提供了一组与动态内存管理相关的工具,包括智能指针、内存分配器、对象构造与销毁的辅助工具等。这些工具极大地简化了动态内存管理,并提高了代码的安全性和可维护性。
1. <memory>
概述
主要内容
<memory>
头文件主要包括以下内容:
- 智能指针:
std::shared_ptr
、std::unique_ptr
、std::weak_ptr
。 - 内存管理工具:
std::allocator
、std::uninitialized_*
函数。 - 对象构造和销毁工具:
std::construct_at
、std::destroy_at
等。 - 其他工具:如
std::addressof
、std::pointer_traits
。
头文件
#include <memory>
2. 智能指针
智能指针是<memory>
中最常用的工具,主要解决动态内存管理中的资源泄漏问题。
2.1 std::shared_ptr
std::shared_ptr
是一个共享所有权的智能指针,多个shared_ptr
可以共享同一个对象。当最后一个shared_ptr
被销毁时,所管理的对象会被自动释放。
主要功能
- 自动管理动态内存,避免手动释放。
- 使用引用计数管理对象的生命周期。
常用函数
use_count()
:返回当前引用计数。reset()
:释放当前对象的控制权。get()
:返回原始指针。make_shared
:创建shared_ptr
的推荐方式。
示例
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sp1 = std::make_shared<int>(10);
std::shared_ptr<int> sp2 = sp1; // 引用计数+1
std::cout << "Value: " << *sp1 << ", Use count: " << sp1.use_count() << "\n";
sp2.reset(); // sp2释放控制权
std::cout << "Use count after reset: " << sp1.use_count() << "\n";
return 0;
}
运行结果
Value: 10, Use count: 2
Use count after reset: 1
2.2 std::unique_ptr
std::unique_ptr
是一个独占所有权的智能指针,保证一个unique_ptr
对象只能管理一个动态分配的对象。
主要功能
- 独占所有权,不能被复制(支持移动)。
- 更轻量级,适合不需要共享的场景。
常用函数
release()
:释放控制权并返回原始指针。reset()
:释放当前对象并管理新对象。get()
:返回原始指针。make_unique
:创建unique_ptr
的推荐方式。
示例
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> up = std::make_unique<int>(20);
std::cout << "Value: " << *up << "\n";
std::unique_ptr<int> up2 = std::move(up); // 转移所有权
if (!up) {
std::cout << "up is null after move.\n";
}
return 0;
}
运行结果
Value: 20
up is null after move.
2.3 std::weak_ptr
std::weak_ptr
是一个非拥有型指针,用于解决std::shared_ptr
的循环引用问题。
主要功能
- 不影响共享对象的引用计数。
- 可用于检查
shared_ptr
所管理的对象是否仍然存在。
常用函数
lock()
:返回一个指向共享对象的shared_ptr
。expired()
:检查所管理的对象是否已销毁。use_count()
:返回共享对象的引用计数。
示例
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sp = std::make_shared<int>(30);
std::weak_ptr<int> wp = sp;
std::cout << "Use count: " << wp.use_count() << "\n";
if (auto sp2 = wp.lock()) { // 检查对象是否仍然存在
std::cout << "Value: " << *sp2 << "\n";
}
sp.reset(); // 销毁共享对象
if (wp.expired()) {
std::cout << "Object expired.\n";
}
return 0;
}
Use count: 1 #不影响共享对象的引用计数
Value: 30
wp.expired():检查wp所关联的对象是否已经被销毁。由于sp2仍然持有对象的引用,所以对象尚未被销毁,wp.expired()返回false,不会输出Object expired.。
3. 内存管理工具
3.1 std::allocator
std::allocator
是C++标准库的默认内存分配器,用于容器的动态内存分配。
主要功能
- 提供内存的分配和释放。
- 提供对象的构造与销毁。
常用函数
allocate()
:分配内存。deallocate()
:释放内存。construct()
:构造对象(C++17后已弃用)。destroy()
:销毁对象(C++17后已弃用)。
示例
#include <iostream>
#include <memory>
int main() {
std::allocator<int> alloc;
int* p = alloc.allocate(3); // 分配3个int的内存
alloc.construct(p, 42); // 在p处构造一个值为42的int
std::cout << "Value: " << *p << "\n";
alloc.destroy(p); // 销毁对象
alloc.deallocate(p, 3); // 释放内存
return 0;
}
3.2 std::uninitialized_*
系列函数
这些函数用于在未初始化的内存中构造对象,常用于性能优化。
主要函数
std::uninitialized_copy
:在未初始化的内存中复制元素。std::uninitialized_fill
:在未初始化的内存中填充元素。std::uninitialized_move
:在未初始化的内存中移动元素。
示例
#include <iostream>
#include <memory>
#include <vector>
int main() {
std::vector<int> src = {1, 2, 3};
int* dest = static_cast<int*>(operator new[](src.size() * sizeof(int)));
std::uninitialized_copy(src.begin(), src.end(), dest);
for (size_t i = 0; i < src.size(); ++i) {
std::cout << dest[i] << " ";
}
std::destroy(dest, dest + src.size());
operator delete[](dest);
return 0;
}
4. 对象构造与销毁工具
4.1 std::construct_at
和 std::destroy_at
std::construct_at
在指定内存地址上构造对象。
std::destroy_at
销毁指定地址上的对象。
示例
#include <iostream>
#include <memory>
int main() {
alignas(int) char buffer[sizeof(int)];
int* p = reinterpret_cast<int*>(buffer);
std::construct_at(p, 42); // 在buffer处构造int对象
std::cout << "Value: " << *p << "\n";
std::destroy_at(p); // 销毁对象
return 0;
}
5. 其他工具
5.1 std::addressof
std::addressof
用于获取对象的真实地址,避免被重载的operator&
干扰。
示例
#include <iostream>
#include <memory>
struct Example {
int operator&() { return 42; }
};
int main() {
Example e;
std::cout << "Address: " << std::addressof(e) << "\n"; // 获取真实地址
return 0;
}
5.2 std::pointer_traits
std::pointer_traits
是一个模板类,用于获取指针类型的相关信息。
6. 注意事项
-
智能指针的循环引用
- 避免
std::shared_ptr
之间的循环引用,使用std::weak_ptr
解决。
- 避免
-
std::unique_ptr
的所有权- 不能复制
std::unique_ptr
,只能通过std::move
转移所有权。
- 不能复制
-
内存泄漏
- 确保动态分配的内存被正确释放,智能指针可以显著减少此类问题。
-
性能优化
- 使用
std::make_shared
和std::make_unique
可以减少内存分配的开销。
- 使用
全力以赴度过今天,自然就能看清楚明天。 —稻盛和夫