为什么需要智能指针-WHY
C++通常使用new和delete进行动态内存管理(C中使用malloc和free)。但是,现代的C++鼓励使用智能指针和容器来管理动态内存,以避免内存泄漏和其他问题。
智能指针是资源获取即初始化(RAII)
思想的典型应用。C++中的智能指针是管理动态分配内存的工具,它们可以自动释放所管理的内存,防止内存泄漏
,定义在<memory>头文件
中。
内存泄漏是指程序在动态分配内存后,由于程序逻辑错误或设计缺陷,失去了对该内存区域的有效引用,从而无法释放已分配的内存空间,导致系统内存资源被无效占用且无法回收的现象。
特性 | unique_ptr | shared_ptr | weak_ptr |
---|---|---|---|
所有权 | 独占 | 共享 | 无所有权 |
拷贝语义 | 不可拷贝,只能移动 | 可拷贝 | 可拷贝 |
性能 | 零开销 | 有引用计数开销 | 有开销 |
使用场景 | 单一所有者 | 多个所有者 | 解决循环引用 |
线程安全 | 否 | 引用计数操作原子性 | 否 |
什么是智能指针-WHAT
1. std::unique_ptr
- 独占所有权指针
std::make_unique<T>
返回一个 std::unique_ptr<T>
对象
特点:
- 独占所有权 :同一时间只能有一个
unique_ptr
拥有对象 - 不可拷贝,只能移动(move)
- 零开销:与裸指针几乎相同的性能
- 自动释放:离开作用域时自动删除对象
使用场景
- 资源管理(数据库连接,文件操作等)
- 工程模式下对象创建
cpp
#include <memory>
// unique_ptr的创建方法
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
auto ptr2 = std::make_unique<std::string>("Hello");
// 移动语义
std::unique_ptr<int> ptr3 = std::move(ptr1); // ptr1 变为 nullptr
// 自定义删除器
std::unique_ptr<FILE, decltype(&fclose)> filePtr(fopen("test.txt", "r"), &fclose);
cpp
// 定义支付处理类
class PaymentProcessor {
public:
virtual ~PaymentProcessor() = default; //析构函数
virtual bool process(double amount) = 0; // 虚函数
};
// 定义信用卡支付处理类
class CreditCardProcessor : public PaymentProcessor {
public:
bool process(double amount) override {
std::cout << "处理信用卡支付: $" << amount << std::endl;
return true;
}
};
// 定义PayPal支付处理类
class PayPalProcessor : public PaymentProcessor {
public:
bool process(double amount) override {
std::cout << "处理PayPal支付: $" << amount << std::endl;
return true;
}
};
// 支付类创建(工厂模式)
class PaymentFactory {
public:
static std::unique_ptr<PaymentProcessor> createProcessor(const std::string& type) {
if (type == "creditcard") {
return std::make_unique<CreditCardProcessor>();
} else if (type == "paypal") {
return std::make_unique<PayPalProcessor>();
}
return nullptr;
}
};
// 使用工厂
void processPayment(const std::string& method, double amount) {
auto processor = PaymentFactory::createProcessor(method);
if (processor) {
processor->process(amount);
}
// 函数执行完后,processor 自动释放(自动调用析构函数),无需手动删除
}
2. std::shared_ptr
- 共享所有权指针
每个 shared_ptr 管理的对象都有一个关联的控制块(Control Block)
控制块中包含:
-
引用计数:记录当前有多少个 shared_ptr 共享该对象
-
弱引用计数:记录 weak_ptr 的数量
-
被管理对象的指针
-
删除器(可选)
shared_ptr A ──────┐
│
shared_ptr B ──────┤ Control Block
│ ┌─────────────┐
shared_ptr C ──────┼───►│ ref count: 3│
│ │ weak count: 1│
weak_ptr D ──────┤ │ object ptr ─┼───► Actual Object
│ │ deleter │
│ └─────────────┘
特点:
- 共享所有权 :多个
shared_ptr
可以共享同一个对象 - 引用计数:内部维护引用计数,计数为0时自动删除对象(创建和拷贝时计数增加)
- 可以拷贝
- 有一定开销:需要维护引用计数
使用场景:
cpp
#include <memory>
std::shared_ptr<int> shared1 = std::make_shared<int>(100); // 创建 shared_ptr
std::shared_ptr<int> shared2 = shared1; // 引用计数+1
std::cout << "引用计数: " << shared1.use_count() << std::endl; // 输出 2
// 自定义删除器
std::shared_ptr<int> shared3(new int[10], [](int* p) { delete[] p; });
cpp
class Configuration {
// ...
};
class Component {
private:
std::shared_ptr<Configuration> config_;
public:
Component(std::shared_ptr<Configuration> config) : config_(config) {}
// ...
};
// 创建配置对象
auto config = std::make_shared<Configuration>();
// 创建多个组件共享配置
Component comp1(config);
Component comp2(config);
// 当comp1, comp2都被销毁,且config引用计数为0时,Configuration对象自动释放
3. std::weak_ptr
- 弱引用指针
特点:
- 不增加引用计数 :观察
shared_ptr
但不拥有所有权 - 解决循环引用问题
cpp
// QT 页面调用
class PageA {
std::shared_ptr<PageB> b_ptr;
};
class PageB {
std::shared_ptr<PageA> a_ptr; // 循环引用!
//std::weak_ptr<PageA> a_weak_ptr; // 使用weak_ptr打破循环引用
};
- 需要通过
lock()
方法获取可用的shared_ptr
使用场景:
cpp
std::shared_ptr<int> shared = std::make_shared<int>(50);
std::weak_ptr<int> weak = shared;
// 使用 weak_ptr
if (auto temp = weak.lock()) {
std::cout << "值: " << *temp << std::endl;
} else {
std::cout << "对象已被销毁" << std::endl;
}
实际应用示例
cpp
class DatabaseConnection {
public:
static std::unique_ptr<DatabaseConnection> create(const std::string& connStr) {
return std::make_unique<DatabaseConnection>(connStr);
}
void execute(const std::string& query) {
// 执行SQL
}
~DatabaseConnection() {
// 自动关闭连接
std::cout << "数据库连接已关闭\n";
}
};
// RAII方式(自动管理)
void processUserData() {
auto db = DatabaseConnection::create("connStr"); // 自动管理
db->execute("UPDATE users...");
// 不用担心,连接会自动关闭
}
// 传统方式
void processUserData() {
DatabaseConnection* db = new DatabaseConnection("connStr");
db->execute("UPDATE users...");
// 如果这里return或抛出异常,连接永远不会关闭!
delete db; // 需要手动释放
}
cpp
class ImageCache {
private:
std::unordered_map<std::string, std::shared_ptr<Image>> cache_;
std::mutex mutex_;
public:
std::shared_ptr<Image> getImage(const std::string& path) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(path);
if (it != cache_.end()) {
return it->second; // 返回共享的图像
}
// 加载新图像
auto image = std::make_shared<Image>(path);
cache_[path] = image;
return image;
}
};
// 多个UI组件共享同一图像资源
class UIComponent {
std::shared_ptr<Image> background_;
public:
UIComponent(const std::shared_ptr<Image>& bg) : background_(bg) {}
void render() {
// 使用共享的背景图像
// 当所有UI组件都销毁时,图像自动从缓存中释放
}
};
4. std::auto_ptr
(已废弃)
注意: C++17 中已移除,不推荐使用。