c++笔记-动态内存(shared_ptr)

  1. 静态内存用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量。由编译器自动创建和销毁。
  2. 栈内存用来保存定义在函数内的非static对象。对于程序块运行时才存在;static在使用之前分配,程序结束时销毁。
  3. 除了静态内存和栈内存,每个程序还有一个内存池。称作heap(堆内存)。程序用堆来存储动态分配的对象–即哪些在程序运行时分配的对象。这些动态对象的生存周期由程序来控制,所以我们代码必须显示地销毁它们。
  4. 动态内存的管理是通过一对运算符来完成:new,delete。new在动态内存中为对象分配空间,并返回一个指向该对象的指针。delete接受一个动态对象的指针,销毁该对象并释放与之关联的内存。
新的标准库提供了两种智能指针类型来管理动态对象。
它可以负责自动释放所指向的对象。定义在memory头文件中
shared_ptr  允许多个指针指向同一对象;
unique_ptr  “独占”所指向的对象
weeak_ptr   这是一个弱引用,指向shared_ptr所管理的对象
  1. shared_ptr类的操作
对于shared_ptr指针的创建,需要指出所指向对象的类型
//默认初始化的智能指针中保存着一个空指针
shared_ptr<string> p1;      //shared_ptr,可以指向string
shared_ptr<list<int>> p2;   //shared_ptr,可以指向int的list

//检查p1是否为空指针,检查p1指向的string对象是否为空
if(p1 && p1->empty())
    *p1 = "hi"; //p1不为空,指向一个空string的话,解引用p1,赋值为string
    
//适用于shared_ptr和unique_ptr的操作
shared_ptr<T> sp
unique_ptr<T> up    空智能指针,可以指向类型为T的对象

p       可以作为一个判断条件,获得它所指向的对象
*p      解引用p,获得它指向的对象

p->men  等价于(*p).mem
p.get() 返回p中保存的指针。若智能指针释放了其对象,返回的指针所指向的对象也就消失

swap(p, q)  交换p和q中的指针
p.swap(q)
适用于shared_ptr的独有操作
make_shared<T> (args)   函数返回一个shared_ptr指针,指向一个T类型的动态对象,使用args初始化对象
shared_ptr<T> q(p)      q是shared_ptr(p)的拷贝;此操作会递增p中的计数器。

p = q   p和q都是shared_ptr类型,所保存的指针必须能相互转换。
        此操作会减少一次p的引用次数,增加一个q的引用次数。若p的次数减为0,会释放其管理的内存
p.unique()      若p.use_count()为1,返回true;否则返回false
p.use_count()   返回与p共享对象的智能指针数量;可能很慢,用于调试。

最安全的分配和使用动态内存的方法是调用make_shared函数
//指向一个值为42的int的shared_ptr
shared_ptr<int> p = make_shared<int>(42);
//指向一个“99999”的字符串
shared_ptr<string> ps = make_shared<string>(s, '9');
//指向一个值初始化的int,即值为0
shared_ptr<int> pi = make_shared<int>();

auto p = make_shared<int>(42);  //此时p指向的对象只有p一个引用
auto q(p);  //p和q指向相同对象,此对象有两个引用者

auto r = make_shared<int>(42);  //r指向的int只有一个引用者
r = q;  //此操作给r赋值,使其指向另一地址
        //r原来指向的对象的引用数减一,若减为零,则原来指向的对象会被释放
        
shared_ptr销毁所管理的对象时,调用的时析构函数完成
  1. 程序出于以下三种原因使用动态内存:1. 程序不知道自己需要使用多少个对象 2. 程序不知道所需要对象的准确类型。3. 程序需要在多个对象间共享数据。
容器类时出于第一种原因而使用动态内存的例子
例如每个vector拥有自己的元素,当我们拷贝一个vector时,原vector和副本vector中的元素时相互分离
vector<int> v1;
{
	vector<int> v2 = { 1, 2, 3 };//v2属于块作用域
	v1 = v2;
}
//v2不再起作用,v2及其包含的元素也被销毁

但某些类分配的资源具有与原对象相独立的生存期。
一般而言,如果两个对象共享底层的数据,当某个对象被销毁时,不能单方面销毁底层数据
假设存在Blob类
Blob<string> b1;    //空Blob
{
    Bolb<string> b2 = {"a", "bs", "scs"};
    b1 = b2;    //b2和b1共享元素
}
//b2被销毁了,但是共享的元素不能被销毁,b1指向b2创建的元素
  1. 定义StrBlob类示例
尝试定义一个管理string的类,命名为StrBlob,本例子将使用vector来保存元素
思路:
    我们不能在Blob对象内直接保存vector,因为一个对象的成员在对象销毁时也会被销毁
    例如,有两个Blob类b1,b2共享相同的vector,加入vector保存在b2中,当b2销毁时,vector也会被销毁
    为了保证b2销毁时,vector仍然存在,我们将其保存在动态内存中
为了实现所希望的数据共享,为每个StrBlob设置一个shared_ptr来管理动态分配的vector。
此shared_ptr的成员将记录有多少个StrBlob共享相同的vector,并在vector的最后一个使用者销毁时释放vector。
class StrBlob
{
public:
    typedef std::vector<std::string>::size_type size_type;
    StrBlob();
    StrBlob(std::initializer_list<std::string> il);
    size_type size() const { return data->size(); } //指针用箭头,实体用点
    bool empty() const { return data->empty(); }
    //添加和删除元素
    void push_back(const std::string &t) { data->push_back(t); }
    void pop_back();
    //元素访问
    std::string& front();
    std::string& back();
private:
    std::shared_ptr<std::vector<std::string>> data;
    void check(size_type i, const std::string &msg) const;
};

//两个构造函数都使用列表初始化data成员
StrBlob::StrBlob() : data(make_shared<vetor<string>>()) {}
StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {}
//此类中我们实现了size,empty和push_back成员。这些成员通过指向底层vector的data成员来完成。
//例如对一个StrBlob对象调用size,会调用data->size()。
//当有些操作访问元素时,必须检查元素是否存在。因此定义了一个check工具函数,检查索引是否合法
void StrBlob::check(size_type i, const std::string &msg) const
{
    if(i>=data->size())
        throw out_of_range(msg);
}
//pop_back和元素访问成员函数首先调用check。
//如果成功,则继续利用底层的vector的操作来完成工作
string& StrBlob::front()
{
    check(0, "front on empty StrBlob!");
    return data->front()
}
string& StrBolb::back()
{
    check(0, "back on empty StrBolb!");
    return data->back();
}
void StrBlob::pop_back()
{
    check(0, "pop_back on empty StrBolb!");
    return data->pop_back();
}
//front和back应该对const进行重载
std::shared_ptrC++标准库中的一个智能指针类,用于管理动态分配的资源。它具有引用计数机制,可以实现多个指针共享同一个对象的所有权。 在给std::shared_ptr赋值时,有三种方式可以使用: 1) 拷贝赋值:使用一个std::shared_ptr初始化另一个std::shared_ptr,这将导致引用计数加1。 2) 移动赋值:使用std::make_shared或者直接赋值一个临时创建的std::shared_ptr,这将导致原来的std::shared_ptr失去对资源的所有权,引用计数转移。 3) 使用std::move:将一个std::unique_ptr移动给std::shared_ptr,这将导致原来的std::unique_ptr失去对资源的所有权,引用计数转移。 对于std::shared_ptr的成员函数功能,具体可以参考STL的文档或笔记。 关于std::shared_ptr的初始化,当使用裸指针初始化std::shared_ptr时,如果指针为nullptr,则std::shared_ptr的_M_ptr和_M_refcount都将为nullptr;否则,将分配内存并初始化控制块。 所以,std::shared_ptr可以用于管理动态分配的资源,并且可以共享资源的所有权。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [std::shared_ptr 详解](https://blog.csdn.net/baidu_31541363/article/details/95802210)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [[STL] std::shared_ptr笔记](https://blog.csdn.net/weixin_38734472/article/details/126486549)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值