在C++的类设计中,成员变量的内存管理一直是需要重点关注的环节,传统的使用裸指针作为类成员的方式,需要开发者手动控制内存的分配和释放,很容易因为遗漏释放或者重复释放导致程序出现内存相关问题。智能指针的出现很好地解决了这类问题,它可以通过自身的生命周期管理所指向的对象,自动在合适的时机释放内存。不同的智能指针类型特性不同,在类成员场景下的适用场景也有明显区别,需要根据实际需求合理选择。

unique_ptr作为类成员的应用
unique_ptr是独占所有权的智能指针,同一时间只能有一个unique_ptr指向对应的对象,适合用来管理类独占的资源成员。当类对象被销毁时,unique_ptr会自动释放所指向的对象,不需要手动处理。
基本使用示例
下面是一个使用unique_ptr作为类成员的简单示例,类Resource是需要被管理的资源类,Owner类通过unique_ptr持有Resource的实例:
#include <memory>
#include <iostream>
// 资源类
class Resource {
public:
Resource() {
std::cout << "Resource 构造" << std::endl;
}
~Resource() {
std::cout << "Resource 析构" << std::endl;
}
void do_something() {
std::cout << "Resource 执行操作" << std::endl;
}
};
// 持有资源的类
class Owner {
private:
// 独占所有权的智能指针成员
std::unique_ptr<Resource> res_ptr;
public:
// 构造函数中初始化智能指针
Owner() : res_ptr(std::make_unique<Resource>()) {}
// 提供访问资源的方法
void use_resource() {
if (res_ptr) {
res_ptr->do_something();
}
}
// 禁止拷贝构造和拷贝赋值,因为unique_ptr不可拷贝
Owner(const Owner&) = delete;
Owner& operator=(const Owner&) = delete;
// 允许移动构造和移动赋值
Owner(Owner&&) = default;
Owner& operator=(Owner&&) = default;
};
int main() {
Owner owner;
owner.use_resource();
// owner离开作用域时,res_ptr自动释放Resource对象
return 0;
}
使用注意事项
unique_ptr不可拷贝,所以如果类包含unique_ptr成员,默认情况下类的拷贝构造和拷贝赋值会被删除,需要显式定义或者声明为删除,避免误用。- 优先使用
std::make_unique来创建unique_ptr,它比直接使用new更安全,能避免一些潜在的内存泄漏问题。 - 如果需要在类之间转移资源的所有权,可以使用移动语义,将
unique_ptr的所有权转移给其他对象。
shared_ptr作为类成员的应用
shared_ptr是共享所有权的智能指针,多个shared_ptr可以指向同一个对象,通过引用计数管理对象的生命周期,当最后一个指向对象的shared_ptr被销毁时,对象才会被释放。适合多个类需要共享同一个资源成员的场景。
基本使用示例
下面的示例中,多个Viewer类的实例可以共享同一个Resource对象:
#include <memory>
#include <iostream>
#include <vector>
class Resource {
public:
Resource() {
std::cout << "Resource 构造" << std::endl;
}
~Resource() {
std::cout << "Resource 析构" << std::endl;
}
void do_something() {
std::cout << "Resource 执行操作" << std::endl;
}
};
// 共享资源的查看者类
class Viewer {
private:
std::shared_ptr<Resource> res_ptr;
public:
Viewer(std::shared_ptr<Resource> ptr) : res_ptr(ptr) {}
void use_resource() {
if (res_ptr) {
res_ptr->do_something();
}
}
};
int main() {
// 创建共享的资源
auto shared_res = std::make_shared<Resource>();
std::cout << "资源引用计数: " << shared_res.use_count() << std::endl;
// 创建多个Viewer共享资源
Viewer v1(shared_res);
std::cout << "资源引用计数: " << shared_res.use_count() << std::endl;
Viewer v2(shared_res);
std::cout << "资源引用计数: " << shared_res.use_count() << std::endl;
// 离开作用域时,引用计数逐步减少,最后资源被释放
return 0;
}
循环引用问题
使用shared_ptr作为类成员时,最容易遇到的问题是循环引用,即两个类的成员互相用shared_ptr指向对方,导致引用计数永远无法降为0,引发内存泄漏。示例代码如下:
#include <memory>
#include <iostream>
class B; // 前向声明
class A {
public:
std::shared_ptr<B> b_ptr;
~A() {
std::cout << "A 析构" << std::endl;
}
};
class B {
public:
std::shared_ptr<A> a_ptr;
~B() {
std::cout << "B 析构" << std::endl;
}
};
int main() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b_ptr = b;
b->a_ptr = a;
// 此时a和b的引用计数都是2,离开作用域后引用计数降为1,不会触发析构,造成内存泄漏
return 0;
}
解决循环引用的方式是,将其中一个类的成员改为weak_ptr,weak_ptr不会增加引用计数,只是弱引用指向的对象,需要时可以通过lock()方法获取shared_ptr访问对象。修改后的B类如下:
class B {
public:
std::weak_ptr<A> a_ptr; // 使用weak_ptr打破循环引用
~B() {
std::cout << "B 析构" << std::endl;
}
};
智能指针作为类成员的选型建议
| 智能指针类型 | 适用场景 | 注意事项 |
|---|---|---|
unique_ptr | 类独占资源,不需要和其他对象共享 | 类默认拷贝功能会被删除,需要手动处理拷贝逻辑 |
shared_ptr | 多个对象需要共享同一个资源 | 注意避免循环引用,必要时搭配weak_ptr使用 |
weak_ptr | 作为辅助成员,避免循环引用,或者临时引用资源 | 访问资源前需要调用lock()检查资源是否还存在 |
常见问题总结
- 不要在类的析构函数中手动释放智能指针指向的对象,智能指针会自动管理生命周期,手动释放会导致重复释放的问题。
- 避免将
this指针直接交给智能指针管理,除非类继承自std::enable_shared_from_this,否则会导致多个独立的控制块,引发错误释放。 - 如果类成员是智能指针,初始化时尽量在构造函数的初始化列表中完成,避免在构造函数体内赋值,减少不必要的临时对象。
C++智能指针类成员shared_ptrunique_ptr修改时间:2026-06-16 15:00:29