C++资源管理:零规则和五规则

本博客只是对于自己学习的一个总结,有错误希望指出


RAII机制: 这些标准库组件使用了成对的 构造函数 / 析构函数来管理资源,确保资源依存于其所属的对象,而不会超过对象的生命周期,通过构造函数获取资源,析构函数释放资源达到防止资源泄漏的目的

对象的动态内存分配: 编写类时,有时并不知道某个对象需要占用多少内存,在此情况下,应该为这个对象动态分配内存。对象的动态内存分配提出了一些挑战,包括释放内存、处理对象复制和对象赋值这就是“五”规则

“五”规则

Class类:

class Class{
public:
    Class(std::string class_name);

    ~Class();//destructor
    Class(const Class &src); //copy constructor
    Class(Class &&src) noexcept; //move constructor

    Class &operator=(const Class &src); //copy assignment
    Class &operator=(Class &&src) noexcept; //move assignment
private:
    std::string _name;
    std::vector<Student *> _students;
};
//noexcept标记函数,指示不会抛出异常

Student类:

class Student{
public:
    Student(std::string student_name);
    std::string getName();
private:
    std::string _name;
};

析构函数

析构函数的执行:
类对象在销毁某个成员时,发生什么完全依赖于成员的类型:
如果是类成员就会调用类成员的析构函数
如果是基本类型就会直接收回
如果是原始指针类型不会delete它所指向的对象
在这里插入图片描述

这里有班级类和学生类,我们知道一个班级有很多学生。当班级需要销毁时,我们调用班级类的析构函数会删除学生的指针,但是不会删除学生对象(指针没了,对象还在这就造成了内存泄漏),所以我们需要手动添加析构函数删除指针指向的对象

Class::~Class()
{
    for(auto &p:_students){//将学生对象一一删除
        delete p;
    }
    _students.clear();
}

复制构造函数

隐式复制构造函数的默认行为是实现对象之间数据成员的逐成员复制,这种对象的复制称为浅复制
在这里插入图片描述

如果班级C1有一个学生“王五“,班级C2复制构造班级C1,那么他们的“王五“是同一个”王五“(因为只是复制指针),这样不符合生活实际(假设学生可以复制),我们更希望的是每个班级拥有的是独立的个体(如果不独立,班级C1对“王五“的操作同样也会产生在班级C2的“王五”上),所以我们需要自己写复制构造函数

Class::Class(const Class &src)
{
    _name=src._name;
    for(auto &p:src._students){//将班级src的学生一一复制过来
        _students.push_back(new Student(*p));
    }
}

复制赋值函数

赋值:擦除左操作数中的当前值,然后存入右操作数的值。
赋值 = 擦除 + 复制。对于基本类型的变量来说,出于优化的目的,赋值通常可以省去擦除操作,因此与复制没有多大的差异;但是我们将看到,对于类对象来说,二者有非常大的差别。
所以复制赋值跟复制差不多多了一个擦出操作

Class &Class::operator=(const Class &src)
{
    _name=src._name;//擦除
    for(auto &p:_students){
        delete p;
    }
    _students.clear();

    for(auto &p:src._students){//将班级src的学生一一复制过来
        _students.push_back(new Student(*p));
    }
    return *this;
}

为啥需要移动语义:
如果一个对象将来不会再使用,那么我们为什么还要深复制它呢?(这样很浪费性能)我们可以直接将它拿过来呀!
(标准库有一个 std::move() 函数,可以将一个预计不再使用的左值x 标记为可以移走其资源的程序对象)
在这里插入图片描述

移动构造函数

举个例子:如果班级C1全部都要去新建的班级C2中去,那么意味着班级C1将销毁,所以我们可以直接将班级C1的学生全部移动到班级C2中去

Class::Class(Class &&src) noexcept
{
    _name=src._name;
    _students=std::move(src._students);//移动
}

Class c3{std::move(c1)};//构造需要加move函数

移动赋值函数

Class &Class::operator=(Class &&src)noexcept
{
    _name=src._name;
    for(auto &p:_students){//擦除
        delete p;
    }
    _students.clear();

    _students=std::move(src._students);//移动
    return *this;
}

c4=std::move(c3);//赋值需要加move函数

“零”规则

其实有更加简单的方法
为了消除在实现 owner 指针时编写 5 种函数的需求, C++ 标准库提供了管理存储资源的组件——动态容器。动态容器是将所管理的对象作为资源来实现的,所以它提供了实现 owner 指针所需的 5 个函数
因为容器他做了资源管理(它自身会有这5个函数)所以我们可以将对象直接放入容器中,这样就只需要写一个构造函数即可(这样当然效率啥的比较低)
注意:std::vector <Student> _students里面不再是指针而是对象

class Class{
public:
    Class(std::string class_name);
private:
    std::string _name;
    std::vector<Student> _students;
};
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗吧!骚年!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值