IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++资源管理:零规则和五规则 -> 正文阅读

[C++知识库]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;
};
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 15:59:31  更:2022-04-06 16:00:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:54:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码