C++:类的默认成员函数------拷贝构造函数赋值运算符重载

目录

一、前言

二、拷贝构造函数 

💦拷贝构造函数概念 

💦拷贝构造函数特性 

🍎 解释特性2:拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用

🍐解释特性3:若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝

🍓产生拷贝构造的三种形式

 💦拷贝构造的总结

三、赋值运算符重载 

💦运算符重载 

💦赋值运算符重载 

四、共勉 


一、前言

        在我们前面学习的中,我们会定义成员变量成员函数,这些我们自己定义的函数都是普通的成员函数,但是如若我们定义的类里什么也没有呢?是真的里面啥也没吗?如下:

class Date {};

        如果一个中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的任何一个类在我们不写的情况下,都会自动生成6个默认成员函数。


【默认成员函数概念】:用户没有显式实现,编译器会生成的成员函数称为默认成员函数


       

        ⭐其中上次的博客已经详细的讲解了构造函数&&析构函数的使用方法,所以本次博客将继续深度的讲解拷贝构造和赋值运算符的重载问题。
        

二、拷贝构造函数 

💦拷贝构造函数概念 

         我们在创建对象时,可否创建一个与另一个对象一摸一样的新对象呢?

int main()
{Date d1(2022, 5, 18);// 将 d1 的 数据拷贝给 d2 让 d2 进行初始化Date d2(d1);return 0;
}

        能否让d2的值跟d1一样呢?也就是说我拿d1去初始化d2,此时调用的函数就是拷贝构造函数。
        🔑拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类 类型对象创建新对象时由编译器自动调用
 

💦拷贝构造函数特性 

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
  3. 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。

如下即为拷贝构造函数:

Date(Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}

🍎 解释特性2:拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用

为什么传值传参会引发无穷递归呢?
我们先举一个普通的func函数作为例子:

//传值传参
void Func(Date d)
{}
int main()
{Date d1(2022, 5, 18);Func(d1);return 0;
}

        此函数调用传参是传值传参。C语言中把实参传给形参是把实参的值拷贝给形参,而我的实参d1是自定义类型的需要调用拷贝构造,传值传参是要调用拷贝构造的,但是我如果不想调用拷贝构造呢?就需要引用传参,因为此时d就是d1的别名

void Func(Date& d) {}

        此时再回到我们刚才的例子:如若我不传引用传参,就会疯狂的调用拷贝构造:

Date(Date d)
{_year = d._year;_month = d._month;_day = d._day;
}

        为了避免出现无限递归调用拷贝构造,所以要加上引用,加上引用后,d就是d1的别名,不存在拷贝构造了。同类型的传值传参是要调用拷贝构造的

Date(const Date& d) {} 
//最好加上const,对d形成保护

🍐解释特性3:若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝

 看如下代码:

class Date
{
public:
//构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
//拷贝构造函数/*Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}*/void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
void Func(Date& d)
{d.Print();
}
int main()
{Date d1(2023, 10, 23);Date d2(d1);Func(d1);d2.Print();
}

运行效果图展示:

        为什么我这里没有写拷贝构造函数,它也会自动完成拷贝构造呢?由此我们要深思,拷贝构造与构造和析构是不一样的,构造和析构都是针对自定义类型才会处理而内置类型不会处理,而默认拷贝构造针对内置类型的成员会完成值拷贝,浅拷贝也就是像把d1的内置类型成员按字节拷贝给d2。


        由此得知,对于日期类这种内置类型的成员是不需要我们写拷贝构造的,那是不是所有的类都不需要我们写拷贝构造呢?来看下面的例子:

       🔑 栈类(需要深拷贝的类

class Stack
{
public://构造函数Stack(int capacity = 10){_a = (int*)malloc(sizeof(int) * capacity);assert(_a);_top = 0;_capacity = capacity;}
//不写拷贝构造,编译器调用默认拷贝构造/*Stack(const Stack& st){_a = st._a;_top = st._top;_capacity = st._capacity;}*///析构函数~Stack(){cout << "~Stack():" << this << endl;free(_a);_a = nullptr;_top = _capacity = 0;}
private:int* _a;int _top;int _capacity;
};
int main()
{Stack st1(10);Stack st2(st1);
}

运行结果出现报错:

         我们通过调试看到运行崩溃了,可见栈的拷贝构造函数不能像日期类一样不写而让编译器去调用默认拷贝构造(就是按照日期类的模式写了拷贝构造也会出错),因为此时的st1(指针)和st2(指针)指向的就是同一块空间,通过调试可以看出:

        st1和st2指向同一块空间会引发一个巨大的问题:析构函数那出错,因为我st2会先析构,析构完后我st1再析构,不过我st1指向的空间已经被st2析构过了,因为它俩指向同一块空间,同一块空间我释放两次就会有问题。 出了析构存在问题,增删查改那也会有问题,这个后续会谈到。
        其实刚才写的栈的拷贝构造就是浅拷贝,真正栈的拷贝构造应该用深拷贝来完成,此部分内容我等后续会专门出一篇博文详解,这里大家先简单接触下。
        综上,我们可以得知,浅拷贝针对日期类这种是没有问题的,而类的成员若是指向一块空间的话就不能用浅拷贝了。

        🔑 字符串类(需要深拷贝的类

class MyString {
public:// 默认构造函数MyString(const char* str = "winter") {_str = (char*)malloc(sizeof(char)*(strlen(str) + 1));if (_str == nullptr){perror("malloc fail!");exit(-1);}strcpy(_str, str);}// 析构函数~MyString() {cout << "~String()" << endl;free(_str);}void MyPrintf(){cout << _str << endl;//printf("%s\n", _str);}private:char* _str;
};int main() 
{MyString s1("hello C++");MyString s2(s1);s1.MyPrintf();cout << endl;s2.MyPrintf();cout << endl;
}

如图:指向了同一块空间

那么会引发什么问题呢?会导致 _str 指向的空间被释放两次,引发程序崩溃。

加入深入拷贝构造函数:

class MyString {
public:// 默认构造函数MyString(const char* str = "winter") {_str = (char*)malloc(sizeof(char)*(strlen(str) + 1));if (_str == nullptr){perror("malloc fail!");exit(-1);}strcpy(_str, str);}// 析构函数~MyString() {cout << "~String()" << endl;free(_str);}//拷贝构造函数MyString(const MyString& s) {// 给新对象申请一段和原对象一样大小的空间_str = (char*)malloc(sizeof(char) * (strlen(s._str) + 1));if (_str == nullptr){perror("malloc fail!");exit(-1);}// 把原对象的数据一一拷贝给新对象strcpy(_str, s._str);}void MyPrintf(){cout << _str << endl;//printf("%s\n", _str);}private:char* _str;
};int main() 
{MyString s1("hello C++");MyString s2(s1);s1.MyPrintf();cout << endl;s2.MyPrintf();cout << endl;
}


 

⭐总结: 

 1️⃣:你可以观察在当前这这个类中是否存在显式的析构函数,若是存在的话,表示当前这个类涉及资源管理了【资源管理指得就是去堆中申请空间了】,此时你一定要自己去是实现拷贝构造以达到一个深拷贝;若是不涉及资源管理的话,直接使用编译器自动生成的进行浅拷贝就可以了

2️⃣: 像Date日期类这种只存在【年】、【月】、【日】这种内置类型的浅拷贝就可以了;像是复杂一些的,例如:链表、二叉树、哈希表这些都会涉及资源的管理,就要考虑到深拷贝了

🍓产生拷贝构造的三种形式

深刻理解了拷贝构造之后,我们再来看看产生拷贝构造的三种形式 

1.当用类的对象去初始化同类的另一个对象时

Date d1;
Date d2(d1);
Date d3 = d2;	//也会调用拷贝构造

 在实例化对象d2和d3的时候都去调用了拷贝构造,最后它们初始化后的结果都是一样的

2.当函数的形参是类的对象,调用函数进行形参和实参结合时

void func(Date d)	//形参是类的对象
{d.Print();
}int main(void)
{Date d1;func(d1);	//传参引发拷贝构造return 0;
}

函数func()的形参是类的对象,此时在外界调用这个函数并传入对应的参数时,就会引发拷贝构造,

3.当函数的返回值是对象,函数执行完成返回调用者时

Date func2()
{Date d(2023, 3, 24);return d;
}int main(void)
{Date d1 = func2();d1.Print();return 0;
}

 可以看到,这一种方式也会引发拷贝构造,当函数内部返回一个Date类的对象时,此时外界再使用Date类型的对象去接收时,就会引发拷贝构造。

 💦拷贝构造的总结

总结:

1. 拷贝构造算是六大默认成员函数中较难理解的了。主要就是要理清【内置类型】和【自定义类型】是否会调用拷贝构造的机制。还有在实现这个拷贝构造时要主要的两点:一个就是在形参部分要进行引用接收,否则会造成无穷递归的现象;还有一点就是在前面加上const进行修饰,可以防止误操作和权限放大的问题
2一般的类,自己生成拷贝构造就够用了,只有像Stack这样自己直接管理资源的类,需要自己实现深拷贝。

三、赋值运算符重载 

💦运算符重载 

如下的日期类:

class Date
{
public://构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};

         我能否按照如下的方式对日期类的对象进行大小比较呢?

         很明显是不可以的,从波浪线提示的警告就能看出。我们都清楚内置类型是可以直接进行比较的,但是自定义类型是不能直接通过上述的运算符进行比较的,为了能够让自定义类型使用各种运算符,于是就提出了运算符重载的规则。


        C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

  • 函数名字为:关键字operator后面接需要重载的运算符符号。
  • 函数参数:运算符操作数
  • 函数返回值:运算符运算后结果
  • 函数原型:返回值类型 operator操作符(参数列表)
  • 注意:
  • 运算符重载函数的参数由运算符决定,比如运算符==应有两个参数,双操作数运算符就有两个参数,单操作数运算符(++或--)就有一个参数
     

就比如我现在写一个日期比较相等的运算符重载(传值传参会引发拷贝构造,所以要加上引用,最好再加上const,以便于提高效率):

bool operator==(const Date& d1, const Date& d2) //避免传值传参调用拷贝构造
{return d1._year == d2._year &&d1._month == d2._month &&d1._day == d2._day;
}

         仔细观察我的截图:毕竟我都写好了运算符重载,可是我调用运算符重载的方式怎么还能跟调用普通函数一样呢?与其这样还取名运算符重载又有何意义,所以真正的调用应该如下:

        调用的时候直接和内置类型进行运算符操作那样,编译器会自动处理成调用运算符重载的样子 

  • 注意:上述的运算符重载就算完成了吗?当然不是,按理说我们要把运算符重载函数放成类里的成员函数。

并且,这里的参数也不能像如上的方式写:

        如若直接把运算符重载函数放到类里,编译器会报错(运算符函数的参数太多)。报错的原因就在于成员函数存在隐含的this指针。 这也就意味着实际的参数有3个,因此我们要少写一个参数:

bool operator==(const Date& d)//编译器会处理成 bool operator(Date* const this, const Date& d)
{return _year == d._year &&_month == d._month &&_day == d._day;
}

         并且我在调用成员函数的时候也要做出改变:

if (d1.operator==(d2))
{cout << "==" << endl;
}

        和刚才一样,为了凸显出运算符重载的意义,我们调用的时候可以直接像内置类型一样操作运算符,因为编译器会帮我们处理:

if (d1 == d2)//编译器会处理成对应重载运算符调用if (d1.operator==(d2))或者if (d1.operator==(&d1, d2))
{cout << "==" << endl;
}

  • 现在,我们来写一个日期类的比较大小来练练手:
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//日期比较大小bool operator<(const Date& d){if (_year > d._year ||_year == d._year && _month > d._month ||_year == d._year && _month == d._month && _day > d._day)return false;elsereturn true;}void Printf(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2023, 10, 23);cout << "d1 的日期为: " << endl;d1.Printf();Date d2(2022, 10, 22);cout << "d2 的日期为: " << endl;d2.Printf();cout << endl;cout << "d1与d2日期的大小比较:";if (d1 < d2){cout << "<" << endl;}else{cout << ">" << endl;}Date d3(d1);d3 = d2;return 0;
}

接下来,再来总结下运算符重载的注意点:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型(对自定义类型成员才可运算符重载)或者枚举类型的操作数
  3. 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
  4. 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
  5. .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

💦赋值运算符重载 

        前面我们已经学习了拷贝构造,是拿同类型的对象去初始化另一个对象,那如果我不想用拷贝构造呢?

int main()
{Date d1(2022, 5, 17);Date d2(2022, 5, 20);Date d3(d1);//拷贝构造 -- 一个存在的对象去初始化另一个要创建的对象d2 = d1; //赋值重载/复制拷贝 -- 两个已经存在的对象之间赋值
}

        可不可以直接拿d1去赋值给d2呢?这就是我们要谈的赋值运算符重载。赋值运算符重载和上文的运算符重载是有点相似的。有了运算符重载的基础,写一个赋值重载还是很简单的。

//d2 = d1;  -> d2.operator=(&d2, d1);
void operator=(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}

        但是这里的赋值重载是存在一定问题的,我们C语言的赋值是支持连等赋值的,如下:

int i = 0, j, k;
k = j = i;

        我们把i赋值给j,随后把j作为返回值再赋值给k。要知道C++是建立在C的基础上的,刚刚我们写的赋值重载支持连等吗?


       很显然是不支持的,原因就是当我把d1赋值给d2后,没有一个返回值来赋给d3,这就导致出错。改正如下: 

        此外:这里的赋值重载还可进一步改进:

  • 改进1:刚才我们写的赋值重载是传值返回,传值返回会生成一个拷贝,会调用拷贝构造。如果出了作用域要让其对象还在我们就可以用传引用返回:
  • 改进2:有可能会存在这样的情况:d1=d1,像这样自己给自己赋值的情况还要再调用赋值重载函数属实没必要,所以我们还可以加个if条件判断。

修正如下:

	//d2 = d1;  -> d2.operator=(&d2, d1);Date& operator=(const Date& d){if (this != &d) //不推荐写成if (*this != d) ,怕的是万一没有重载!=呢?,因为这里是对象的比较{_year = d._year;_month = d._month;_day = d._day;}return *this;}
  • 注意:

        operator赋值也是默认成员函数,我们不写赋值重载,编译器也会默认生成,不过编译器完成的依旧是值拷贝或浅拷贝,像这个日期类就可以不写赋值重载。

        赋值重载和拷贝构造一样,我们不写,它会对内置类型完成值拷贝,而像栈这样的就不能不写了,因为我们要写一个深拷贝的赋值重载才可以,理由和拷贝构造类似。 具体实现等真正谈到深拷贝再来。

 
  •  补充:
void TestDate2()
{Date d1(2022, 5, 18);Date d3 = d1; //等价于 Date d3(d1);
}
        Date d3 = d1 是拷贝构造,不是赋值, 拿一个对象初始化另一个对象是拷贝构造。如下d2 =d1才是赋值:
 
void TestDate2()
{Date d1(2022, 5, 18);Date d2(2022, 5, 20);Date d3 = d1; //等价于 Date d3(d1); 是拷贝构造d2 = d1; //两个已经存在的对象才是赋值
}

四、共勉 

         以下就是我对C++类的默认成员函数--------拷贝构造函数&&赋值运算重载的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++ 类的默认成员函数-------const成员&&const取地址操作符重载的理解,请持续关注我哦!!!      

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/119415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt之自定义QStringListModel设置背景色和前景色

一.效果 二.实现 QStringListModel里只实现了Qt::EditRole和Qt::DisplayRole,不能直接设置背景色和前景色,所以我们要继承QStringListModel,重写其中的data和setData方法,使其支持Qt::ForegroundRole和Qt::BackgroundRole。 QHStringListModel.h #ifndef QHSTRINGLISTMO…

P1966 [NOIP2013 提高组] 火柴排队

洛谷的一道原题&#xff0c;方法有很多&#xff0c;树状数组以及排序&#xff0c;对刚学树状数组的人来说用排序会比较好理解。 本题最重要的结论就是&#xff0c;要保证两个数组中相同位置的差最小&#xff0c;但是不一定两个数组中数值相同&#xff0c;所以只需要保证相同位…

C语言每日一题(20)最大公因数等于 K 的子数组数目

力扣 2447 最大公因数等于 K 的子数组数目 题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 nums 的子数组中元素的最大公因数等于 k 的子数组数目。 子数组 是数组中一个连续的非空序列。 数组的最大公因数 是能整除数组中所有元素的最大整数。 …

王道p40 1.设计一个递归算法,删除不带头结点的单链表L中的所有值为x的结点(c语言代码实现)图解递归

视频讲解(献丑了)&#xff1a;p40 第1题 王道数据结构课后代码题c语言代码实现_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Xa4y1Q7ui/?spm_id_from333.999.0.0 首先它是一个不带头结点的单链表 我们就得特殊处理 我们先让*LNULL&#xff1b; 然后为s开辟一个新…

大数据-Storm流式框架(二)--wordcount案例

一、编写wordcount案例 1、新建java项目 2、添加storm的jar包 storm软件包中lib目录下的所有jar包 3、编写java类 WordCountTopology.java package com.bjsxt.storm.wc;import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.genera…

Pytorch代码入门学习之分类任务(三):定义损失函数与优化器

一、定义损失函数 1.1 代码 criterion nn.CrossEntropyLoss() 1.2 损失函数简介 神经网络的学习通过某个指标表示目前的状态&#xff0c;然后以这个指标为基准&#xff0c;寻找最优的权重参数。神经网络以某个指标为线索寻找最优权重参数&#xff0c;该指标称为损失函数&am…

IP地址规划的基本方法

IP地址规划是构建和管理网络的关键步骤&#xff0c;它涉及到为网络中的设备分配合适的IP地址&#xff0c;以确保网络的高效性、安全性和可管理性。在本文中&#xff0c;我们将探讨IP地址规划的基本方法&#xff0c;以帮助网络管理员和工程师更好地设计和维护网络。 1. 理解IP地…

基于JAVA的天猫商场系统设计与实现,springboot+jsp,MySQL数据库,前台用户+后台管理,完美运行,有一万五千字论文

目录 演示视频 基本介绍 论文目录 系统截图 演示视频 基本介绍 基于JAVA的天猫商场系统设计与实现&#xff0c;springbootjsp&#xff0c;MySQL数据库&#xff0c;前台用户后台管理&#xff0c;完美运行&#xff0c;有一万五千字论文。 本系统在HTML和CSS的基础上&#xf…

Antv G6入门之旅--combo图

目录 什么是AntV G6 G6 的特性 G6 文档 安装 1 在项目中使用 NPM 包引入 2 在 HTML 中使用 CDN 引入 使用 Step 1 创建容器 Step 2 数据准备 Step 3 创建关系图 Step 4 配置数据源&#xff0c;渲染 React 中使用 G6 Combo图 什么是AntV G6 G6 是一个图可视化引擎…

OpenCV学习(二)——OpenCV中绘图功能

2. OpenCV中绘图功能2.1 画线2.2 画矩形2.3 画圆2.4 画多边形2.5 添加文本 2. OpenCV中绘图功能 绘图可以实现画线、画矩形、画圆、画多边形和添加文本等操作。 import cv2 import numpy as np# 读取图像 img cv2.imread(lena.jpg)# 画直线 cv2.line(img, (0, 0), (512, 512…

还不知道光场相机吗?

1.什么是光场&#xff1f; 光场&#xff08;light field&#xff09;&#xff1a;就是指光在每一个方向通过每一个点的光量。 从概念里&#xff0c;你至少可以得到两点信息&#xff1a; 光场包含光的方向光场包含一个点的光量 2.什么是光场相机 我们知道普通的相机拍照成像…

Windows环境下Apache安装部署说明及常见问题解决

一、软件准备 1.1 Python的下载与安装 见博客 链接: Python下载安装 1.2 Pycharm的下载与安装 见博客 链接: pycharm安装 1.3 Mysql的下载与安装 见博客 链接: MySQL安装 1.4 Navicat的下载与安装 可参考软件安装管家。 解释说明:Pycharm是Python的集成编译环境&#xff0c;Nav…

SpringBoot2.7.14整合redis7

需要的依赖库&#xff1a; <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</gro…

中文编程开发语言工具编程实际案例:台球棋牌混合计时计费软件使用的编程构件说明

中文编程开发语言工具编程实际案例&#xff1a;台球棋牌混合计时计费软件使用的编程构件说明 上图说明&#xff1a;该软件可以用于桌球和棋牌同时计时计费&#xff0c;在没有开台的时候&#xff0c;图片是处于等待状态&#xff0c;这使用编程工具中的固定图像构件&#xff0c;在…

基于Java的音乐网站管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

vue3中使用svg并封装成组件

打包svg地图 安装插件 yarn add vite-plugin-svg-icons -D # or npm i vite-plugin-svg-icons -D # or pnpm install vite-plugin-svg-icons -D使用插件 vite.config.ts import { VantResolver } from unplugin-vue-components/resolvers import { createSvgIconsPlugin } from…

操作系统【OS】I/O核心子系统

定义 举例 用户层软件 用户层软件实现了与用户交互的接口用户层软件将用户请求翻译成格式化的I/O请求&#xff0c;并通过“系统调用”请求操作系统内核的服务用户可直接使用该层提供的、与I/0操作相关的库函数对设备进行操作 如发送read命令如讲二进制整数转换为ascii码的…

基于opencv的selenium滑动验证码的实现

这篇文章主要介绍了基于opencv的selenium滑动验证码的实现&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面随着小编来一起学习学习吧 基于selenium进行动作链 由于最近很多人聊到滑动验证码…

解决javascript报错:SyntaxError: Invalid Unicode escape sequence

在处理cookie时报这个错&#xff1a; 网上搜了一圈都说是反斜杠问题&#xff0c;要把\替换成/ 但是试了网上的replace替换&#xff1a; replace(/\\/g, /) 结果没有用&#xff01;&#xff01;&#xff01; 然后我干脆直接做了一个最简单字符串赋值&#xff0c;再打印出来。…

C++ stack 的使用

目录 1. 无参构造函数 2. void push(const T& x) 3. void pop() 4. T& top() 5. bool empty() 6. size_t size() 7. 总结 1. stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行 元素的插入与…