C++智能指针简述

引言

在C++编程中,内存管理一直是个重要且复杂的话题。传统的裸指针虽然灵活,但也容易导致内存泄漏、悬空指针等问题。为了解决这些问题,C++11引入了智能指针的概念,它们能够自动管理内存生命周期,大大提高了代码的安全性和可维护性。

本文将深入探讨C++中四种主要的智能指针:unique_ptrshared_ptrweak_ptr以及已被废弃的auto_ptr,通过丰富的代码示例展示它们的用法和最佳实践。

一、为什么需要智能指针?

1.1 传统指针的问题

让我们先看一个使用裸指针可能导致问题的例子:

#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass(int val) : value(val) {
        cout << "构造函数被调用,value = " << value << endl;
    }
    
    ~MyClass() {
        cout << "析构函数被调用,value = " << value << endl;
    }
    
    void print() {
        cout << "Value: " << value << endl;
    }
    
private:
    int value;
};

void problematicFunction() {
    MyClass* ptr = new MyClass(10);
    ptr->print();
    
    // 如果这里发生异常或者提前返回
    // delete将不会被执行,导致内存泄漏
    // throw runtime_error("发生异常!");
    
    delete ptr;  // 可能会被跳过
}

int main() {
    problematicFunction();
    return 0;
}

1.2 智能指针的优势

智能指针通过RAII(Resource Acquisition Is Initialization)技术,将资源管理封装在对象生命周期中,确保资源在不再需要时被正确释放。

二、unique_ptr:独占所有权的智能指针

2.1 基本用法

unique_ptr实现独占式所有权,同一时间只能有一个unique_ptr指向特定对象。

#include <iostream>
#include <memory>
using namespace std;

void uniquePtrDemo() {
    cout << "=== unique_ptr 示例 ===" << endl;
    
    // 创建unique_ptr
    unique_ptr<MyClass> ptr1(new MyClass(100));
    
    // 使用make_unique(C++14推荐)
    auto ptr2 = make_unique<MyClass>(200);
    
    // 访问对象成员
    ptr1->print();
    ptr2->print();
    
    // 转移所有权
    unique_ptr<MyClass> ptr3 = move(ptr1);
    if (!ptr1) {
        cout << "ptr1 现在为空" << endl;
    }
    if (ptr3) {
        cout << "ptr3 拥有对象: ";
        ptr3->print();
    }
    
    // ptr2和ptr3在离开作用域时会自动释放内存
}

2.2 自定义删除器

#include <cstdio>

void customDeleterDemo() {
    cout << "\n=== 自定义删除器 ===" << endl;
    
    // 文件指针的自定义删除器
    auto fileDeleter = [](FILE* file) {
        if (file) {
            fclose(file);
            cout << "文件已关闭" << endl;
        }
    };
    
    unique_ptr<FILE, decltype(fileDeleter)> filePtr(fopen("test.txt", "w"), fileDeleter);
    if (filePtr) {
        fputs("Hello, World!", filePtr.get());
    }
    
    // 数组的自定义删除器
    auto arrayDeleter = [](int* arr) {
        delete[] arr;
        cout << "数组内存已释放" << endl;
    };
    
    unique_ptr<int[], decltype(arrayDeleter)> arrPtr(new int[10], arrayDeleter);
    for (int i = 0; i < 10; ++i) {
        arrPtr[i] = i * i;
    }
}

三、shared_ptr:共享所有权的智能指针

3.1 基本用法

shared_ptr通过引用计数实现共享所有权,当最后一个shared_ptr离开作用域时,对象才会被销毁。

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class Resource {
public:
    Resource(string name) : name(name) {
        cout << "Resource '" << name << "' 被创建" << endl;
    }
    
    ~Resource() {
        cout << "Resource '" << name << "' 被销毁" << endl;
    }
    
    void use() {
        cout << "使用 Resource '" << name << "'" << endl;
    }
    
private:
    string name;
};

void sharedPtrDemo() {
    cout << "\n=== shared_ptr 示例 ===" << endl;
    
    // 创建shared_ptr
    shared_ptr<Resource> ptr1 = make_shared<Resource>("资源A");
    
    {
        shared_ptr<Resource> ptr2 = ptr1;  // 共享所有权
        cout << "引用计数: " << ptr1.use_count() << endl;  // 输出2
        
        ptr1->use();
        ptr2->use();
    }  // ptr2离开作用域,引用计数减1
    
    cout << "引用计数: " << ptr1.use_count() << endl;  // 输出1
    ptr1->use();
}

3.2 循环引用问题

class Node {
public:
    string name;
    shared_ptr<Node> next;
    
    Node(string name) : name(name) {
        cout << "Node '" << name << "' 被创建" << endl;
    }
    
    ~Node() {
        cout << "Node '" << name << "' 被销毁" << endl;
    }
};

void cyclicReferenceDemo() {
    cout << "\n=== 循环引用问题 ===" << endl;
    
    auto node1 = make_shared<Node>("节点A");
    auto node2 = make_shared<Node>("节点B");
    
    node1->next = node2;
    node2->next = node1;  // 循环引用!
    
    cout << "node1 引用计数: " << node1.use_count() << endl;  // 输出2
    cout << "node2 引用计数: " << node2.use_count() << endl;  // 输出2
    
    // 离开作用域时,由于循环引用,内存不会被释放!
}

四、weak_ptr:解决循环引用

4.1 基本用法

weak_ptr是对shared_ptr所管理对象的非拥有式引用,不会增加引用计数。

class SafeNode {
public:
    string name;
    weak_ptr<SafeNode> next;  // 使用weak_ptr避免循环引用
    
    SafeNode(string name) : name(name) {
        cout << "SafeNode '" << name << "' 被创建" << endl;
    }
    
    ~SafeNode() {
        cout << "SafeNode '" << name << "' 被销毁" << endl;
    }
    
    void setNext(shared_ptr<SafeNode> nextNode) {
        next = nextNode;
    }
    
    void printNext() {
        if (auto nextPtr = next.lock()) {  // 转换为shared_ptr
            cout << "下一个节点是: " << nextPtr->name << endl;
        } else {
            cout << "下一个节点已被销毁" << endl;
        }
    }
};

void weakPtrDemo() {
    cout << "\n=== weak_ptr 解决方案 ===" << endl;
    
    auto node1 = make_shared<SafeNode>("安全节点A");
    auto node2 = make_shared<SafeNode>("安全节点B");
    
    node1->setNext(node2);
    node2->setNext(node1);  // 使用weak_ptr,不会造成循环引用
    
    cout << "node1 引用计数: " << node1.use_count() << endl;  // 输出1
    cout << "node2 引用计数: " << node2.use_count() << endl;  // 输出1
    
    node1->printNext();
    node2->printNext();
    
    // 离开作用域时,内存会被正确释放
}

4.2 weak_ptr的典型应用场景

class Observer;

class Subject {
public:
    void addObserver(shared_ptr<Observer> observer) {
        observers.push_back(observer);
    }
    
    void notifyObservers();
    
private:
    vector<weak_ptr<Observer>> observers;  // 使用weak_ptr避免影响Observer生命周期
};

class Observer {
public:
    virtual void update() = 0;
    virtual ~Observer() = default;
};

五、智能指针实战应用

5.1 工厂模式与智能指针

#include <iostream>
#include <memory>
#include <map>
using namespace std;

// 产品接口
class Product {
public:
    virtual void operation() = 0;
    virtual ~Product() = default;
};

// 具体产品
class ConcreteProductA : public Product {
public:
    void operation() override {
        cout << "ConcreteProductA 操作" << endl;
    }
};

class ConcreteProductB : public Product {
public:
    void operation() override {
        cout << "ConcreteProductB 操作" << endl;
    }
};

// 工厂类
class Factory {
public:
    static unique_ptr<Product> createProduct(const string& type) {
        if (type == "A") {
            return make_unique<ConcreteProductA>();
        } else if (type == "B") {
            return make_unique<ConcreteProductB>();
        }
        return nullptr;
    }
};

void factoryDemo() {
    cout << "\n=== 工厂模式示例 ===" << endl;
    
    auto productA = Factory::createProduct("A");
    auto productB = Factory::createProduct("B");
    
    if (productA) productA->operation();
    if (productB) productB->operation();
}

5.2 实现简单的对象池

#include <queue>
#include <functional>

template<typename T>
class ObjectPool {
public:
    ObjectPool(size_t initialSize = 10) {
        for (size_t i = 0; i < initialSize; ++i) {
            pool.push(make_shared<T>());
        }
    }
    
    shared_ptr<T> acquire() {
        if (pool.empty()) {
            return make_shared<T>();
        }
        
        auto obj = pool.front();
        pool.pop();
        return obj;
    }
    
    void release(shared_ptr<T> obj) {
        // 重置对象状态(如果需要)
        pool.push(obj);
    }
    
    size_t available() const {
        return pool.size();
    }
    
private:
    queue<shared_ptr<T>> pool;
};

void objectPoolDemo() {
    cout << "\n=== 对象池示例 ===" << endl;
    
    ObjectPool<string> stringPool(3);
    
    cout << "可用对象数: " << stringPool.available() << endl;
    
    auto str1 = stringPool.acquire();
    auto str2 = stringPool.acquire();
    
    *str1 = "第一个字符串";
    *str2 = "第二个字符串";
    
    cout << "使用后可用对象数: " << stringPool.available() << endl;
    
    stringPool.release(str1);
    stringPool.release(str2);
    
    cout << "归还后可用对象数: " << stringPool.available() << endl;
}

六、智能指针的最佳实践

6.1 选择正确的智能指针

void bestPractices() {
    cout << "\n=== 最佳实践 ===" << endl;
    
    // 1. 优先使用make_unique和make_shared
    auto ptr1 = make_unique<int>(42);
    auto ptr2 = make_shared<string>("Hello");
    
    // 2. 明确所有权语义
    unique_ptr<int> createUnique() {
        return make_unique<int>(100);
    }
    
    shared_ptr<int> createShared() {
        return make_shared<int>(200);
    }
    
    // 3. 在接口中明确指针的语义
    void processObject(unique_ptr<int> ptr) {  // 转移所有权
        cout << "处理对象: " << *ptr << endl;
    }
    
    void observeObject(const shared_ptr<int>& ptr) {  // 不转移所有权,只是观察
        if (ptr) {
            cout << "观察对象: " << *ptr << endl;
        }
    }
    
    // 4. 避免混用裸指针和智能指针
    int* rawPtr = new int(50);
    shared_ptr<int> smartPtr(rawPtr);
    // 错误:不要再用rawPtr操作内存
}

6.2 性能考虑

#include <chrono>

void performanceTest() {
    cout << "\n=== 性能测试 ===" << endl;
    
    const int iterations = 1000000;
    
    // 测试裸指针
    auto start1 = chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        int* ptr = new int(i);
        delete ptr;
    }
    auto end1 = chrono::high_resolution_clock::now();
    
    // 测试unique_ptr
    auto start2 = chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        auto ptr = make_unique<int>(i);
    }
    auto end2 = chrono::high_resolution_clock::now();
    
    auto duration1 = chrono::duration_cast<chrono::microseconds>(end1 - start1);
    auto duration2 = chrono::duration_cast<chrono::microseconds>(end2 - start2);
    
    cout << "裸指针耗时: " << duration1.count() << " 微秒" << endl;
    cout << "unique_ptr耗时: " << duration2.count() << " 微秒" << endl;
}

七、总结

C++智能指针是现代C++编程中不可或缺的工具,它们通过自动内存管理大大提高了代码的安全性和可维护性。让我们回顾一下关键点:

  1. unique_ptr:用于独占所有权场景,性能接近裸指针

  2. shared_ptr:用于共享所有权场景,通过引用计数管理生命周期

  3. weak_ptr:配合shared_ptr使用,解决循环引用问题

  4. 最佳实践:优先使用make函数、明确所有权语义、避免混用指针类型

智能指针虽然不能解决所有内存管理问题,但在大多数场景下,它们能够显著减少内存相关的bug。掌握智能指针的正确用法,是成为优秀C++程序员的重要一步。

int main() {
    uniquePtrDemo();
    customDeleterDemo();
    sharedPtrDemo();
    cyclicReferenceDemo();
    weakPtrDemo();
    factoryDemo();
    objectPoolDemo();
    bestPractices();
    performanceTest();
    
    return 0;
}
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质?你是否想成为一名资深开发人员,想开发别人做不了的高性能程序?你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹? 那么C++就是你个人能力提升,职业之路进阶的不二之选。【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署;2.吊打一切关于C++的笔试面试题;3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块基础篇本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。进阶篇本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。提升篇:本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值