std::shared_ptr循环引用导致内存泄漏因引用计数无法归零,解决方法是使用std::weak_ptr打破循环;混合使用裸指针可能引发重复释放或悬空指针,应避免用裸指针初始化多个智能指针,并通过get()谨慎传递非所有权访问;对于非内存资源,需通过自定义删除器(如Lambda、函数对象)确保智能指针正确释放资源,从而实现全面的RAII管理。

智能指针在C++中是防止内存泄漏的利器,但它们并非万无一失。我们之所以会遇到智能指针导致的“内存泄漏”,往往不是智能指针本身的设计缺陷,而是对其使用场景、所有权语义理解不透彻,或是未能妥善处理一些特殊情况,比如循环引用。说到底,智能指针是工具,工具用不好,自然达不到预期效果。
在C++中,智能指针是用来自动化管理动态分配内存的,它们的核心思想是RAII(Resource Acquisition Is Initialization)。但要彻底避免内存泄漏,我们得深入理解它们的行为模式和潜在陷阱。
一个常见的误区是,认为只要用了智能指针就高枕无忧了。实际上,最典型的“泄漏”场景是
std::shared_ptr
std::shared_ptr
另一个问题出在智能指针与裸指针的混合使用。如果你从
std::shared_ptr
get()
std::shared_ptr
std::shared_ptr
delete
立即学习“C++免费学习笔记(深入)”;
此外,智能指针默认管理的是
new
new
malloc
delete
避免这些问题的关键在于:明确所有权、警惕循环引用、避免裸指针的滥用,并为非标准资源提供正确的删除策略。
std::shared_ptr
std::shared_ptr
std::shared_ptr
std::shared_ptr
想象一下,我们有两个类
A
B
A
std::shared_ptr<B>
B
std::shared_ptr<A>
A
A
B
B
std::shared_ptr
class B; // 前向声明
class A {
public:
std::shared_ptr<B> b_ptr;
~A() { std::cout << "A destroyed" << std::endl; }
};
class B {
public:
std::shared_ptr<A> a_ptr;
~B() { std::cout << "B destroyed" << std::endl; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b; // b的引用计数变为2
b->a_ptr = a; // a的引用计数变为2
// 当a和b离开作用域时,它们的引用计数都只会降到1,永远不会到0
// 导致A和B的对象都无法被销毁,这就是内存泄漏。
return 0;
}在这个例子中,
A
B
main
shared_ptr
shared_ptr
A
B
解决方案:使用 std::weak_ptr
std::weak_ptr
std::shared_ptr
std::weak_ptr::lock()
std::shared_ptr
lock()
std::shared_ptr
修改上面的例子:
#include <iostream>
#include <memory>
class B; // 前向声明
class A {
public:
std::shared_ptr<B> b_ptr;
~A() { std::cout << "A destroyed" << std::endl; }
};
class B {
public:
std::weak_ptr<A> a_ptr; // 将强引用改为弱引用
~B() { std::cout << "B destroyed" << std::endl; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b; // b的引用计数变为2
b->a_ptr = a; // a的引用计数仍为1 (因为是weak_ptr)
// 当a离开作用域时,a的引用计数降到0,A对象被销毁。
// 此时b->a_ptr观察的对象已不存在。
// 随后b离开作用域时,b的引用计数降到0,B对象被销毁。
return 0;
}在这个修正后的版本中,
B
A
std::weak_ptr
main
A
A
A
b->a_ptr
A
B
B
B
std::weak_ptr
std::shared_ptr
std::weak_ptr
智能指针和裸指针混合使用是C++中一个常见的陷阱,它可能导致悬空指针、重复释放或未定义行为。核心问题在于所有权语义的模糊。
潜在风险:
重复释放 (Double Free): 如果你有一个
std::shared_ptr
get()
std::shared_ptr
std::shared_ptr
delete
int* raw_ptr = new int(10); std::shared_ptr<int> sp1(raw_ptr); // sp1管理raw_ptr指向的内存 // ... std::shared_ptr<int> sp2(raw_ptr); // 错误!sp2也试图管理同一块内存 // 当sp1和sp2析构时,会发生double free
正确做法是,如果已经有
std::shared_ptr
std::shared_ptr
悬空指针 (Dangling Pointer): 如果你将
std::unique_ptr
std::shared_ptr
std::unique_ptr<int> up(new int(5)); int* raw = up.get(); // 获取裸指针 // delete raw; // 假设某个函数内部错误地执行了这行 // ... // up离开作用域时,会再次尝试delete raw,导致double free
this
std::shared_ptr
return std::shared_ptr<MyClass>(this);
std::shared_ptr
this
std::shared_ptr
安全操作方法:
避免从裸指针创建多个智能指针: 一旦内存被智能指针管理,就应该通过智能指针本身来传递所有权或共享所有权。 使用
std::make_shared
std::make_unique
传递裸指针用于观察或临时访问: 当需要将智能指针管理的对象传递给接受裸指针的旧API或函数时,使用
get()
void legacy_api_process(int* data) {
// 假设这个API只会使用data,不会删除它
std::cout << *data << std::endl;
}
std::shared_ptr<int> sp = std::make_shared<int>(100);
legacy_api_process(sp.get()); // 安全,只要legacy_api_process不删除data从 this
std::shared_ptr
std::enable_shared_from_this
std::shared_ptr
std::enable_shared_from_this<MyClass>
shared_from_this()
std::shared_ptr
std::shared_ptr
std::shared_ptr
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
std::shared_ptr<MyClass> get_shared_this() {
return shared_from_this();
}
};
int main() {
std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();
std::shared_ptr<MyClass> another_obj = obj->get_shared_this(); // 安全
// obj和another_obj现在共享同一个MyClass对象
return 0;
}明确所有权语义: 在使用智能指针时,始终要思考谁拥有资源。
std::unique_ptr
std::shared_ptr
std::move
std::shared_ptr
智能指针的默认行为是使用
delete
new
fopen
fclose
pthread_mutex_lock
pthread_mutex_unlock
malloc
free
自定义删除器可以是普通函数、函数对象(functor)或 Lambda 表达式。
1. std::unique_ptr
std::unique_ptr
使用 Lambda 表达式作为删除器: 这是最灵活和现代的方式,可以直接在创建
unique_ptr
#include <iostream>
#include <memory>
#include <cstdio> // For FILE* and fclose
// 假设我们有一个需要特殊关闭函数的文件句柄
void close_file(FILE* file) {
if (file) {
std::cout << "Closing file..." << std::endl;
fclose(file);
}
}
int main() {
// 使用lambda表达式作为自定义删除器
std::unique_ptr<FILE, decltype(&close_file)> file_ptr(
fopen("example.txt", "w"), close_file);
if (file_ptr) {
fprintf(file_ptr.get(), "Hello from unique_ptr!\n");
} else {
std::cerr << "Failed to open file." << std::endl;
}
// file_ptr离开作用域时,close_file会被自动调用
return 0;
}注意
decltype(&close_file)
使用函数对象(Functor)作为删除器: 当删除逻辑比较复杂,或者需要在多个地方复用时,可以定义一个函数对象。
struct FileCloser {
void operator()(FILE* file) const {
if (file) {
std::cout << "Closing file via functor..." << std::endl;
fclose(file);
}
}
};
int main() {
std::unique_ptr<FILE, FileCloser> file_ptr(fopen("another.txt", "w"));
if (file_ptr) {
fprintf(file_ptr.get(), "Hello from functor!\n");
}
// file_ptr离开作用域时,FileCloser()会被自动调用
return 0;
}这里
unique_ptr
FileCloser
decltype(&close_file)
2. std::shared_ptr
std::shared_ptr
shared_ptr
使用 Lambda 表达式作为删除器:
#include <iostream>
#include <memory>
#include <mutex> // For std::mutex
int main() {
std::mutex mtx;
// 使用lambda表达式作为自定义删除器,管理互斥锁的解锁
std::shared_ptr<std::mutex> lock_ptr(&mtx, [](std::mutex* p) {
std::cout << "Unlocking mutex..." << std::endl;
p->unlock();
});
lock_ptr->lock(); // 锁定互斥锁
std::cout << "Mutex is locked." << std::endl;
// lock_ptr离开作用域时,lambda会被自动调用,解锁互斥锁
return 0;
}使用普通函数作为删除器:
void free_c_memory(void* ptr) {
if (ptr) {
std::cout << "Freeing C-style memory..." << std::endl;
free(ptr); // 使用free而不是delete
}
}
int main() {
// 使用malloc分配内存,并用shared_ptr管理
std::shared_ptr<int> c_array_ptr(
static_cast<int*>(malloc(10 * sizeof(int))), free_c_memory);
if (c_array_ptr) {
for (int i = 0; i < 10; ++i) {
c_array_ptr.get()[i] = i;
}
std::cout << "C-style array managed by shared_ptr: " << c_array_ptr.get()[5] << std::endl;
}
// c_array_ptr离开作用域时,free_c_memory会被自动调用
return 0;
}通过自定义删除器,智能指针的能力得到了极大的扩展,不再局限于管理
new/delete
以上就是C++如何避免智能指针导致的内存泄漏的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号