【Effective C++】第三章资源管理

13-以对象形式管理资源

目的:

确保控制流在离开 单一区块函数 的时候, 保存对象的资源 会自动调用析构函数释放此资源,避免内存泄漏。手动使用delete会很不安全且容易忘记。

方法

把一个资源放到对象里面,依赖析构函数自动调用机制,在其离开作用域后自动调用delete 删除
在申请后立即将资源放到智能指针 管理对象中,智能指针销毁时会自动删除它所指之物所申请的资源。

  1. auto_ptr :一个资源永远只有一个auto_ptr指向

当有第二个auto_ptr 指向的时候,第一个会失效

  1. shared_ptr:一个资源可以有多个shared_ptr指向

内部包含了一个计数器,当无人指向资源时会调用析构函数

智能指针缺点:不能指向数组,因为内部调用的是delete 而不是 delete[ ]。
我们可以用string 或者 vector 来代替

知识点扩展

RAII: 在资源获取时就对其进行初始化

环状引用 :两个对象互相引用对方,彼此互指share_ptr 无法打破这个状态
需要再使用weak_ptr :如果一个对象仅仅需要引用另一个对象而不拥有它,它不会增加对象的引用计数,也不会影响对象的生命周期,从而避免循环引用导致的内存泄漏。
[overfold]

#include <iostream>
#include <memory>

class B; // 前置声明

class A {
private:
    std::weak_ptr<B> b_ptr; // 使用 weak_ptr 避免循环引用
public:
    void setB(const std::shared_ptr<B>& ptr) {
        b_ptr = ptr;
    }
    void display() {
        std::cout << "Class A" << std::endl;
    }
};

class B {
private:
    std::weak_ptr<A> a_ptr; // 使用 weak_ptr 避免循环引用
public:
    void setA(const std::shared_ptr<A>& ptr) {
        a_ptr = ptr;
    }
    void display() {
        std::cout << "Class B" << std::endl;
    }
};

int main() {
    // 创建 shared_ptr 对象
    std::shared_ptr<A> objA = std::make_shared<A>();
    std::shared_ptr<B> objB = std::make_shared<B>();

    // 设置对象间的引用
    objA->setB(objB);
    objB->setA(objA);

    // 当 main 函数结束时,由于使用了 shared_ptr 和 weak_ptr,不会产生循环引用导致的内存泄漏
    return 0;
}

14-RAII 资源管理类中要小心copying行为

知识点扩展

heap-based资源:可以指任何在堆上动态分配的资源,比如动态分配的内存块、对象、数据结构等
Reference counting copy:是指在引用计数智能指针(如 std::shared_ptr)的实现中,当进行对象的拷贝时,不是简单地复制指针本身,而是复制指针所指向对象的引用计数,并增加该引用计数。

目的

如果一个资源并不是 heap_based 类 那么就不适合用智能指针。需要自己建立自己的资源管理类

方法

复制RAII 会选择两种处理

  1. 禁止复制:调用class lock : private Uncopyable隐藏RAII类的复制行为

  2. 使用shared_ptr 对管理的资源进行计数

//shared_ptr 可以指定“删除”方式,可以是一个函数或函数对象)

void lock(Mutex* pm) {} // 锁定pm所指的互斥器
void unlock(Mutex *pm){}; // 将互斥器解除锁定

class Lock2 {
public:
  explicit Lock2(Mutex* pm) : mutexPtr(pm, unlock) { // 以某个Mutex初始化shared_ptr,并以unlock函数为删除器
    lock(mutexPtr.get()); // 获得资源
  }

private:
  std::tr1::shared_ptr<Mutex> mutexPtr; // 使用shared_ptr替换raw pointer
};

int main()
{
    // -----Demo 1.-----
    Mutex m; // 定义你需要的互斥器
    Lock2 m1(&m);
    Lock2 m2(m1);
	//m1 m2 同时指向 Mutex m当 main 函数结束时,m1 和 m2 对象会自动销毁,从而导致 Mutex 对象 m 的引用计数减少。由于指定了 unlock 函数作为删除器,所以在 Mutex 对象的引用计数减少到零时,unlock 函数会被调用来解锁并释放 Mutex 资源
    return 0;
}

复制RAII对象必须复制它所管理的资源,包括指针和所指的内存。进行的是"深拷贝"

例:复制指针就必须将指针所指的东西也复制,不然就成空悬指针了

15-在资源管理类种提供原始资源的访问

目的

因为许多API 需要直接引用原始资源,所以不得不跳过RAII (生产者模式)

使用原始资源的方法

给RAII提供一个访问原始资源的方法 。返回值为原始资源对原始资源访问

  • 显示转换比较安全(推荐)
  • 隐式转换比较方便:但是不安全
    智能指针重载了指针取值 -> * 允许隐式地转换至原始指针
void releaseFont(FontHandle fh) {}
class FontHandle {
};

class Font {
public:
  explicit Font(FontHandle fh) : f(fh) { // 获得资源,采用pass-by-value,因为C API这样做
  }
  ~Font() {
    releaseFont(f); // 释放资源
  }
  FontHandle get() const {
    return f; // 显式转换函数
  }
  operator FontHandle() const {
    return f; // 隐式转换函数
  }

private:
  FontHandle f; // 原始字体资源
};

FontHandle getFontHandle() {
    FontHandle fh;
    return fh;
}

int main()
{
    Font f(getFontHandle());
    // int newFontSize = changeFontSize(f.get()); // 将Font显式转换为FontHandle
    int newFontSize = changeFontSize(f); // 将Font隐式转换为FontHandle

    Font f1(getFontHandle());
    FontHandle f2 = f1; // 错误!原意是要拷贝一个Font对象,却反而将f1隐式转换为其底部的FontHandle,然后才复制它。
}

所有代码

16-使用new[ ] 的时候 也要使用delete[ ]

扩展
typedef string address[4];//它创建了一个类型别名 address,用于表示一个包含四个 string 元素的数组
address myAddress;
myAddress[0] = "123 Main St.";
myAddress[1] = "Unit 4";
myAddress[2] = "Anytown";
myAddress[3] = "Anystate";

目的:

要告诉delete删除的是一个数组还是单个对象

方法

如题

造成后果:太少析构函数为被调用。或者更多无关内存中的对象被删除

17-以独立的语句将对象置入智能指针

processWight(std::tr1::share_ptr<Wight>(new Wight) pw, priority());
//引用上述代码有三种方式

processWidget(new Widget, priority()); // 错误,函数要求是shared_ptr<Widget>,而new生成的是Widget*


 processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority()); // 容易造成内存泄露

当程序以 new Wight -> priority() -> share_ptr Wight(new Wight) 的顺序执行形参的时候

一旦priority()出现错误,就会导致 new 出来的 Wight 内存泄漏。(不同编译器有不同的顺序 ,不易察觉)

所以要分开创建,也可以直接使用make_shared 隐式转换指针

[processWidget(std::make_shared<Widget>(), priority()); // 使用 std::make_shared 创建智能指针,并进行隐式转换
 
std::tr1::shared_ptr<Widget> pw(new Widget); // 在单独语句内以智能指针存储new所得对象](<std::share_ptr Wight pw (new Wight);>)

processWight(pw, priority());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值