C++——8(智能指针&STL)

一、智能指针

头文件:

智能指针:智能指针(smart pointer)是个特殊的类模板,重载了->*运算符,实现了C++的自动内存回收机制。

分类:

  • auto_ptr:C++11中已经弃用,有设计缺陷不足之处

  • shared_ptr:共享智能指针,允许多个智能指针共享同一资源,通过引用计数自动管理内存。(当引用该指针的数目为0时,自动释放)

    • 核心原理
      • 每个 shared_ptr 维护一个 “引用计数”,记录有多少个 shared_ptr 指向同一资源。

      • 当新的 shared_ptr 指向资源时,计数 + 1;当 shared_ptr 销毁或重新赋值时,计数 - 1。

      • 当计数为 0 时,自动释放资源。

/*===============================================
*   文件名称:2_SharedPoint.cpp
*   创 建 者:青木莲华
*   创建日期:2025年09月04日
*   描    述:智能指针_Shared 示例
================================================*/
#include <iostream>
using namespace std;
#include <memory>
#include <unistd.h>

class Demo
{
    public:
    Demo(){
        cout << __func__ << endl;
    }
    ~Demo(){
        cout << __func__ << endl;
    }

    void func()
    {
        cout << "Demo::func()" << endl;
    }

};

void test()
{
    //定义共享智能指针   不安全写法
    //shared_ptr<Demo> p(new Demo());
    //安全写法  安全创建堆区空间
    shared_ptr<Demo> p = make_shared<Demo>(); 	//计数值 = 1
    shared_ptr<Demo> p2 = p;   				//计数值 = 2

    p->func(); 

    p.reset();      //结束生命周期(智能指针),引用计数值 = 1

    sleep(3);       //sleep延时,查看是否释放资源
    p2->func();     //执行完后,函数结束,引用计数值 == 0 ,自动回收资源
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}

运行截图

alt

  • unique_ptr:独占资源智能指针,资源只能被一个 unique_ptr 持有(独占所有权),转移所有权后原指针失效。

    • 核心特点
      • 不可拷贝,只能移动:通过 std::move 转移所有权,原指针变为 nullptr

      • 高效轻量:无引用计数开销,性能接近原始指针。

      • 支持自定义删除器:可管理非内存资源(如文件句柄、网络连接)。

      • 兼容 STL 容器:因支持移动语义,可安全存入 vector 等容器。

#include <iostream>
using namespace std;

#include <memory>

class Demo
{
   public:
   Demo(){
       cout << __func__ << endl;
   }
   ~Demo(){
       cout << __func__ << endl;
   }

   void func()
   {
       cout << "Demo::func()" << endl;
   }

};


unique_ptr<Demo> test()
{
   //资源独占智能指针
   unique_ptr<Demo> p = make_unique<Demo>();

   p->func();
   //unique_ptr<Demo> p2(p);   不能直接初始化,因为独占

   //移交所有权  p ---> p2  ,  p = nullptr;
   unique_ptr<Demo> p2 = move(p);

   p2->func();

   //return p2;  (隐式)函数返回unique指针时,会自动移交所有权
   return move(p2);    //显示返回移交
}   


int main(int argc, char *argv[])
{
   unique_ptr<Demo> p = test();    //接收返回指针,并接收所有权
   cout << "-----------" << endl;
   p->func();
   return 0;
}

运行结果

alt

  • weak_ptr:配合 shared_ptr 使用,不增加引用计数,用于解决 shared_ptr循环引用问题

    • 核心特点
      • 不管理资源生命周期,仅 “观察”shared_ptr 管理的资源。

      • 通过 lock() 方法获取 shared_ptr(若资源未释放),或用 expired() 检查资源是否有效。

/*===============================================
*   文件名称:4_WeakPoint.cpp
*   创 建 者:青木莲华
*   创建日期:2025年09月04日
*   描    述:智能指针_Weak 示例
================================================*/
#include <iostream>
using namespace std;

#include <memory>
#include <unistd.h>

class Demo
{
    public:
    Demo(){
        cout << __func__ << endl;
    }
    ~Demo(){
        cout << __func__ << endl;
    }

    void func()
    {
        cout << "Demo::func()" << endl;
    }

};

void test()
{

    shared_ptr<Demo> p = make_shared<Demo>();
    weak_ptr<Demo> wp = p;      //协助管理shared_ptr

    p->func();

    if(wp.expired())    //判断堆区中对象是否释放
    {
        cout << "Obj is not exist" << endl;
    }
    else
    {
        cout << "Obj exist" << endl;
    }

    p.reset();          //结束shared_ptr生命周期
    if(wp.expired())    //判断堆区中对象是否释放
    {
        cout << "Obj is not exist" << endl;
    }
    else
    {
        cout << "Obj exist" << endl;
    }
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}

运行结果

alt

智能指针 所有权 线程安全 适用场景 核心缺陷
auto_ptr 独占(旧版) 简单场景(已弃用) 所有权转移易出错、功能弱
shared_ptr 共享 引用计数线程安全 多指针共享资源 循环引用导致内存泄漏
unique_ptr 独占 单指针管理资源、STL 容器 不可共享
weak_ptr 弱引用(无) 解决 shared_ptr 循环引用 不能直接访问资源

二、标准模板库 (STL)

  • STL:主要是一些容器的集合,list、vector、set、map等。

  • 目的:标准化组件,可以使用现成的组件。

  • 分类:

    • 容器:实现了数组、链表、队列、等等,实质是模板类
    • 迭代器:一种复杂的指针,可以通过其读写容器中的对象,实质是运算符重载
    • 空间配置器:容器的空间配置管理的模板类
    • 配接器:用来修饰容器、仿函数、迭代器接口
    • 算法:算法通过迭代器对容器实现相关操作
    • 仿函数:类似函数,通过重载()运算符来模拟函数行为的类
  • 组件间关系:

    container(容器)通过allocator(配置器)取得数据储存空间,algorithm(算法)通过iterator(迭代器)存取container(容器)内容,functor(仿函数)可以协助algorithm(算法)完成不同的策略变化,adapter(配接器)可以修饰或套接functor(仿函数)

三、容器(Container)

  • 序列容器

    • Vector:动态数组容器

    • Deque:双端队列容器

    • list:双向链表容器

  • 关联容器

    • Set / multiset:根据元素值自动排序,由红黑树实现,便于查找(set内相同元素只能出现一次,multiset可以存在多次)
    • map / multimap:成对的键值 / 实值,由红黑树实现(map 内相同元素只能出现一次,multimap可以出现多次)
    • Hash相关:hash_map、hash_set、hash_multimap、hash_multiset等

alt

1.List 集合

  • 概述:

    <list> 是一个非常重要的容器类,用于存储元素集合,支持双向迭代器。<list> 是 C++ 标准模板库(STL)中的一个序列容器,它允许在容器的任意位置快速插入和删除元素。不需要在创建时指定大小,并且可以在任何位置添加或删除元素,而不需要重新分配内存。

  • 以下是 <list> 容器的一些基本操作:

    • 包含头文件:#include <list>
    • 声明列表:std::list<T> mylist;,其中 T 是存储在列表中的元素类型。
    • 插入元素:mylist.push_back(value);
    • 删除元素:mylist.pop_back();mylist.erase(iterator);
    • 访问元素:mylist.front();mylist.back();
    • 遍历列表:使用迭代器 for (auto it = mylist.begin(); it != mylist.end(); ++it)
  • 特点

    • 双向迭代<list> 提供了双向迭代器,可以向前和向后遍历元素。
    • 动态大小:与数组不同,<list> 的大小可以动态变化,不需要预先分配固定大小的内存。
    • 快速插入和删除:可以在列表的任何位置快速插入或删除元素,而不需要像向量那样移动大量元素。

示例代码:

/*===============================================
*   文件名称:List.cpp
*   创 建 者:青木莲华
*   创建日期:2025年09月04日
*   描    述:集合示例
================================================*/
#include <iostream>
using namespace std;
#include <list>
class Good
{
    public:
        Good(){}
        Good(string name , string note , double price):
        g_name(name) , g_note(note) , g_price(price)
        {}
        friend ostream& operator<<(ostream& out,const Good &g)
        {
            out << "商品名:" << g.g_name << endl;
            out << "商品简介:" << g.g_note << endl;
            out << "商品价格:" << g.g_price << endl;
        
            return out;
        }


    private:
        string g_name;
        string g_note;
        double g_price;
};


int main(int argc, char *argv[])
{

    Good good_a("小牛","牛牛的小牛车",20000);
    Good good_b("大牛","牛牛的大牛兰博基尼",9999999);
    Good good_c("二牛","牛牛的二手车",1000);

    list<Good> goods;  
    
    goods.push_back(good_a);
    goods.push_back(good_b);
    goods.push_back(good_c);
    
    for(Good good : goods)
    {
        cout << good << endl;
        cout << "=======================" << endl;
    }

    return 0;
}

运行截图

alt

2.Vector 动态数组

  • 概述: C++ 中的 vector 是一种序列容器,它允许你在运行时动态地插入和删除元素。vector 是基于数组的数据结构,但它可以自动管理内存,这意味着你不需要手动分配和释放内存。
  • 基本特性:
    • 动态大小vector 的大小可以根据需要自动增长和缩小。
    • 连续存储vector 中的元素在内存中是连续存储的,这使得访问元素非常快速。
    • 可迭代vector 可以被迭代,你可以使用循环(如 for 循环)来访问它的元素。
    • 元素类型vector 可以存储任何类型的元素,包括内置类型、对象、指针等。

vector示例:

/*===============================================
*   文件名称:Vector.cpp
*   创 建 者:青木莲华
*   创建日期:2025年09月04日
*   描    述:Vector容器
================================================*/
#include <iostream>
using namespace std;
#include <vector>

class Student
{
    public:
        //无参
        Student(){}
        //有参
        Student(string sno,string sname,int age,bool gender)
            :sno(sno),sname(sname),sage(age),gender(gender){};
        //重构输出
        friend ostream& operator<<(ostream &out,const Student *stu)
        {
            out << "学号:" << stu->sno << endl;
            out << "姓名:" << stu->sname << endl;
            out << "年龄:" << stu->sage << endl;
            string gen = (stu->gender ? "男" : "女");
            out << "性别:" << gen << endl;
            return out;
        }
    private:
        string sno;
        string sname;
        int sage;
        bool gender;
};



int main(int argc, char *argv[])
{

    //1.创建Student Vector 默认为空
    vector<Student *> students;
    //2.添加元素  尾部插入
    students.push_back(new Student("25060101",
                    "张三",18,true));
    students.push_back(new Student("25060102",
                    "李四",20,true));
    students.push_back(new Student("25060103",
                "王五",19,false));
    //打印vector元素个数
    cout << "vector_size = " << students.size() << endl;
    cout << "=====================" << endl; 
    //迭代打印 + 回收资源
    for(Student *stu : students)
    {
        cout << stu << endl;
        delete stu;
    }

    students.clear();
    cout << "=====================" << endl; 
    cout << "vector_size = " << students.size() << endl;

    return 0;
}

运行截图

alt

3.Deque 双端队列

  • 概述:

    在 C++中,<deque> 是标准模板库(STL)的一部分,它提供了双端队列(double-ended queue)的实现。

    双端队列是一种允许在两端进行插入和删除操作的线性数据结构。

  • 常用操作:

函数名称 功能描述
deque() 默认构造函数,创建一个空的 deque 容器。
deque(size_type n) 创建一个包含 n 个默认值元素的 deque 容器。
push_back(const T& value) 在容器末尾添加 value 元素。
pop_back() 移除容器末尾的元素。
push_front(const T& value) 在容器前端添加 value 元素。
pop_front() 移除容器前端的元素。
insert(iterator pos, const T& value) pos 位置插入 value 元素。
erase(iterator pos) 移除 pos 位置的元素。
size() 返回容器中的元素个数。
clear() 清除容器中的所有元素。
at(size_type pos) 返回 pos 位置的元素,并进行范围检查。
front() 返回第一个元素的引用。
back() 返回最后一个元素的引用。
begin() 返回指向第一个元素的迭代器。
end() 返回指向末尾元素后一位置的迭代器。

dequeue示例:

/*===============================================
*   文件名称:Deque.cpp
*   创 建 者:青木莲华
*   创建日期:2025年09月04日
*   描    述:双端队列示例
================================================*/
#include <iostream>
using namespace std;
#include <deque>
class Good
{
    public:
        Good(){}
        Good(string name , string note , double price):
        g_name(name) , g_note(note) , g_price(price)
        {}
        friend ostream& operator<<(ostream& out,const Good &g)
        {
            out << "商品名:" << g.g_name << endl;
            out << "商品简介:" << g.g_note << endl;
            out << "商品价格:" << g.g_price << endl;
        
            return out;
        }


    private:
        string g_name;
        string g_note;
        double g_price;
};


int main(int argc, char *argv[])
{
    deque<Good> goods;

    Good good_a("小牛","牛牛的小牛车",20000);
    Good good_b("大牛","牛牛的大牛兰博基尼",9999999);
    Good good_c("二牛","牛牛的二手车",1000);

    goods.push_back(good_a);
    goods.push_back(good_b);
    goods.push_back(good_c);
    
    for(Good good : goods)
    {
        cout << good << endl;
        cout << "=======================" << endl;
    }

    return 0;
}

运行截图

alt