C++初阶——拷贝构造和运算符重载(const成员)

目录

1. 拷贝构造函数

1.2 拷贝构造函数特征:

2.  默认拷贝构造函数

2.1 未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

3. 运算符重载

3.1 运算符重载的思考

注意:赋值运算符只能重载成类的成员函数不能重载成全局函数

3.2 前置++和后置++

C++为了区分前置和后置++在后置++的函数中多加一个int类型的参数来区别前置++

4. const成员

 4.1 取地址及const取地址操作符


1. 拷贝构造函数

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存
在的类类型对象创建新对象时由编译器自动调用
 

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2023,7,30);Date d2(d1);//用d1初始化d2return 0;
}

注意:我们这里是用一个已经存在的对象初始化

1.2 拷贝构造函数特征:

  • 拷贝构造是构造函数的一个重载形式
  • 拷贝构造函数只能有一个参数且必须是类类型对象的引用使用传值方式编译器直接报错,
    因为会引发无穷递归调用
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// Date(const Date& d) // 正确写法Date(const Date d) // 错误写法:编译报错,会引发无穷递归{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(d1);return 0;
}

如果错误写的话,这里面是一层一层的调用下去就变成死循环了 

所以拷贝构造时一定要加上引用


2.  默认拷贝构造函数

如果用户不显示写拷贝构造函数编译器会自动生成一个默认构造函数

默认构造函数会对内置类型完成:

值拷贝(浅拷贝)

调用自定义类型的构造函数完成拷贝

2.1 未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time(const Time& t){_hour = t._hour;_minute = t._minute;_second = t._second;cout << "Time::Time(const Time&)" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
int main()
{Date d1;// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数Date d2(d1);return 0;
}

注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定
义类型是调用其拷贝构造函数完成拷贝的


下面我们看一下这样一个例子:

class Stack
{
public:Stack(size_t capacity = 10)//构造函数{_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const int& data)//插入函数{_array[_size] = data;_size++;}~Stack()//析构函数{if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}private:int *_array;size_t _size;size_t _capacity;
};int main()
{Stack s1(10);s1.push(1);s1.push(2);s3.push(3);s3.push(4);s3.push(5);Stack s2(s1);retrn 0;
}

 array指向的空间是动态开辟的是在堆区存储的,使用值拷贝的方式
s1和s2中的array指向的空间相同!

当s1和s2生命周期结束时,会分别调用它们各自的析构函数
然而两个对象中的指针指向的空间相同
析构函数会调用两个free释放空间!同一份空间释放两个就会出错!


3. 运算符重载

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

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@        
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现

函数原型:

返回值类型    operator操作符(参数列表)

Date operator+(Date d1, int x);

3.1 运算符重载的思考

运算符重载是针对自定义类型的所以函数参数中必须有一个类类型参数

如果运算符重载写在类外它就不能访问类中的私有成员

class Date
{ 
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}bool operator==(const Date& d2){return _year == d2._year;&& _month == d2._month&& _day == d2._day;}
private:int _year;int _month;int _day;
};

因为类中函数默认有this指针指针this就代表了此类对象

注意:赋值运算符只能重载成类的成员函数不能重载成全局函数
 

// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}// 编译失败:
// error C2801: “operator =”必须是非静态成员

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现
一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值
运算符重载只能是类的成员函数

3.2 前置++和后置++

C++为了区分前置和后置++在后置++的函数中多加一个int类型的参数来区别前置++
//前置++
Date& operator++();//后置++
Date& operator++(int);

注意:虽然后置++多了一个参数但是这个参数完全没用!只用于区别前置++

下面我们来个例子:

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 前置++:返回+1之后的结果// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率Date& operator++(){_day += 1;return *this;}// 后置++:// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
自动传递// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
一份,然后给this+1// 而temp是临时对象,因此只能以值的方式返回,不能返回引用Date operator++(int){Date temp(*this);_day += 1;return temp;}private:int _year;int _month;int _day;
};int main()
{Date d;Date d1(2022, 1, 13);d = d1++; // d: 2022,1,13 d1:2022,1,14d = ++d1; // d: 2022,1,15 d1:2022,1,15return 0;
}

4. const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改

 4.1 取地址及const取地址操作符

这两个默认成员函数一般不用重新定义 ,编译器默认会生成
 

class Date
{
public :Date* operator&(){return this ;}const Date* operator&()const{return this ;}
private :int _year ; // 年int _month ; // 月int _day ; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容!
 

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

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

相关文章

无涯教程-Perl - bless函数

描述 此函数告诉REF引用的实体,它现在是CLASSNAME包中的对象,如果省略CLASSNAME,则为当前包中的对象。建议使用bless的两个参数形式。 语法 以下是此函数的简单语法- bless REF, CLASSNAMEbless REF返回值 该函数返回对祝福到CLASSNAME中的对象的引用。 例 以下是显示其…

用户体验旅程地图:全程体验设计最强工具

用户体验旅程地图&#xff1a;全程体验设计最强工具 上天视角看全程 提升关键环节&#xff0c;优化薄弱环节 趣讲大白话&#xff1a;不懂窍门&#xff0c;冤枉浪费 【趣讲信息科技248期】 **************************** 用户体验旅程地图&#xff1a;将用户通过某一特定的产品或…

项目实战 — 消息队列(4){消息持久化}

目录 一、消息存储格式设计 &#x1f345; 1、queue_data.txt&#xff1a;保存消息的内容 &#x1f345; 2、queue_stat.txt&#xff1a;保存消息的统计信息 二、消息序列化 三、自定义异常类 四、创建MessageFileManger类 &#x1f345; 1、约定消息文件所在的目录和文件名…

MFC第二十六天 CRgn类简介与开发、封装CMemoryDC类并应用开发

文章目录 CRgn类简介与开发CRgn类简介CRgn类区域管理开发CRgn类区域管理与不规则形状的选取 封装CMemoryDC类并应用开发CMemoryDC.h封装CMemoryDC开发游戏透明动画CFlashDlg.hCFlashDlg.cpp 封装CMemoryDC开发游戏动画 附录四大窗口CDC派生类 CRgn类简介与开发 CRgn类简介 CR…

车载软件架构 —— 闲聊几句AUTOSAR OS(十一)

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人生在世,最怕的就是把别人的眼光当成自己生活的唯一标…

【编译原理】五、简单四则运算的代码实现

1. 前言 前面说了那么多BNF的相关理论知识&#xff0c;实际上就是为了一个目的&#xff1a; 描述语法规则 描述语法规则是一切的开始。最终&#xff0c;还是要用代码来实现。 如果对于BNF仍然是一头雾水&#xff0c;也没关系&#xff0c;因为我们的最终目的是编写解析器&…

sklearn中使用决策树

1.示例 criterion可以是信息熵&#xff0c;entropy&#xff0c;可以是基尼系数gini # -*-coding:utf-8-*- from sklearn import tree from sklearn.datasets import load_wine from sklearn.model_selection import train_test_split wineload_wine()# print ( wine.feature_…

XXL-JOB定时任务框架(Oracle定制版)

特点 xxl-job是一个轻量级、易扩展的分布式任务调度平台&#xff0c;能够快速开发和简单学习。开放源代码并被多家公司线上产品使用&#xff0c;开箱即用。尽管其确实非常好用&#xff0c;但我在工作中使用的是Oracle数据库&#xff0c;因为xxl-job是针对MySQL设计的&#xff…

Java中HashMap原理与分析

HashMap的底层数据结构 HashMap是以Key-Value的方式进行数据结构存储的一种数据结构。 JDK1.7采用的是数组链表&#xff0c;使用Entry类存储key和value JDK1.8采用的是数组链表/红黑树&#xff0c;使用Node类存储key和value。 HashMap扩容为什么总是2的次幂 HashMap扩容主要…

原型链污染

文章目录 1. javascript 原型链2. 原型链变量的搜索3. prototype 原型链污染4. 原型链污染例题4.1 题1&#xff1a;4.2.题2&#xff1a; 1. javascript 原型链 js在ECS6之前没有类的概念&#xff0c;之前的类都是用funtion来声明的。如下 可以看到b在实例化为test对象以后&…

【ArcGIS Pro二次开发】(56):界址点导出Excel

界址点成果表是地籍测绘中的一种表格&#xff0c;用于记录地块的界址点坐标和相关属性信息。 这个工具的目的就是为了将地块要素导出为界址点成果表。 一、要实现的功能 如上图所示&#xff0c;在【数据处理】组—【Excel相关】面板下&#xff0c;点击【界址点导出Excel】工具。…

File 类和 InputStream, OutputStream 的用法总结

目录 一、File 类 1.File类属性 2.构造方法 3.普通方法 二、InputStream 1.方法 2.FileInputStream 三、OutputStream 1.方法 2.FileOutputStream 四、针对字符流对象进行读写操作 一、File 类 1.File类属性 修饰符及类型属性说明static StringpathSeparator依赖于系统的路…