C++初阶-类和对象(中)2

类和对象(中)2

  • 一、赋值运算符重载
    • 运算符重载
    • 赋值运算符重载
    • 前置++和后置++重载
  • 二、日期类的实现
  • 三、const成员
  • 四、取地址及const取地址操作符重载

一、赋值运算符重载

运算符重载

  C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
5.    .*   ::   sizeof   ?:   .注意以上5个运算符不能重载。这个经常在笔试选择题中出现

// 全局的operator==
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;
};// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}void Test ()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout<<(d1 == d2)<<endl;
}
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// bool operator==(Date* this, const Date& d2)// 这里需要注意的是,左操作数是this,指向调用函数的对象bool operator==(const Date& d2){return _year == d2._year;&& _month == d2._month&& _day == d2._day;
}
private:int _year;int _month;int _day;
};

赋值运算符重载

1. 赋值运算符重载格式
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this :要符合连续赋值的含义

class Date
{
public :Date(int year = 1900, 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;}Date& operator=(const Date& d){if(this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}private:int _year ;int _month ;int _day ;
};

2. 赋值运算符只能重载成类的成员函数不能重载成全局函数

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};// 赋值运算符重载成全局函数,注意重载成全局函数时没有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. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time& operator=(const Time& t){if (this != &t){_hour = t._hour;_minute = t._minute;_second = t._second;}return *this;
}
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;Date d2;d1 = d2;return 0;
}

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
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 DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}private:DataType *_array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;return 0;
}

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

前置++和后置++重载

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;
}

二、日期类的实现

Date.h

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;//类里面短小函数,适合做内联的函数,直接是在类里面定义的
class Date
{//友元friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:Date(int year = 1, int month = 1, int day = 1);int GetMonthDay(int year, int month)const;bool operator==(const Date& d)const;bool operator<(const Date& d)const;bool operator<=(const Date& d)const;bool operator>(const Date& d)const;bool operator>=(const Date& d)const;bool operator!=(const Date& d)const;Date& operator=(const Date& d);void Print()const;Date& operator+=(int day);Date operator+(int day)const;Date& operator-=(int day);//d1-100Date operator-(int day)const;//d1-d2int operator-(const Date& d)const;//++d1    //d1.operator++();Date& operator++();//d1++    //d1.operator++(0);//int 参数 仅仅是为占位,跟前置重载区分Date operator++(int);//--d1 ->d1.operator--()Date& operator--();//d1-- ->d1.operator--(0)Date operator--(int);void operator<<(ostream& out);private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in,  Date& d);inline ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}inline istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"int Date::GetMonthDay(int year, int month)const
{assert(month > 0 && month < 13);int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)){return 29;}else{return monthArray[month];}
}Date::Date(int year, int month, int day)
{if (month > 0 && month < 13&& (day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;}
}/*Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}*///d1==d2
bool Date::operator==(const Date& d)const
{return _year == d._year&& _month == d._month&& _day == d._day;
}//d1<d2
bool Date::operator<(const Date& d)const
{/*if (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}else{return false;}*/return (_year < d._year)|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day);
}//d1<=d2
bool Date::operator<=(const Date& d)const
{return *this < d || *this == d;
}//d1>d2
bool Date::operator>(const Date& d)const
{return !(*this <= d);
}//d1>=d2
bool Date::operator>=(const Date& d)const
{return !(*this < d);
}//d1!=d2
bool Date::operator!=(const Date& d)const
{return !(*this == d);
}//返回值为了支持连续赋值,保持运算符的特性
//d3=d1=d2
//d1=d1
Date& Date::operator=(const Date& d)
{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}void Date::Print()const
{cout << _year << "-" << _month << "-" << _day << endl;
}private:
//int _year;
//int _month;
//int _day;//+=
//Date& Date::operator+=(int day)
//{
//	_day += day;
//	while (_day > GetMonthDay(_year, _month))
//	{
//		_day -= GetMonthDay(_year, _month);
//		_month++;
//		if (_month == 13)
//		{
//			++_year;
//			_month = 1;
//		}
//	}
//	return *this;
//}//+
//Date Date::operator+(int day)
//{
//	Date tmp(*this);
//
//	tmp._day += day;
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//	{
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//		tmp._month++;
//		if (tmp._month == 13)
//		{
//			++tmp._year;
//			tmp._month = 1;
//		}
//	}
//	return tmp;
//}//+复用+=
//Date Date::operator+(int day)
//{
//	Date tmp(*this);
//	tmp += day;
//	
//	return tmp;
//}//int main()
//{
//	Date d1(2023, 1, 4);
//	Date d2(2023, 3, 4);
//
//	cout << d1.operator==(d2) << endl;
//	cout << (d1 == d2) << endl; //转换成d1.operator==(d2);
//
//	cout << (d1 < d2) << endl;
//	//d1 < d2;
//
//	d1 = d2;//d1.operator=(d2);
//	d1.Print();
//
//	Date d5 = d1;  //拷贝构造
//	Date d6(d1);    //拷贝构造
//
//	return 0;
//}//+
Date Date::operator+(int day)const
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(tmp._year, tmp._month)){tmp._day -= GetMonthDay(tmp._year, tmp._month);tmp._month++;if (tmp._month == 13){++tmp._year;tmp._month = 1;}}return tmp;
}//+=复用+
Date& Date::operator+=(int day)
{if (day < 0){*this -= -day;return *this;}*this = *this + day;return *this;
}//++d1
Date& Date::operator++()
{*this += 1;return *this;
}//d1++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}Date& Date::operator-=(int day)
{if (day < 0){*this += -day;return *this;}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}Date Date::operator-(int day)const
{Date tmp(*this);tmp -= day;return tmp;
}//--d1
Date& Date::operator--()
{*this -= 1;return *this;
}//d1--  ->d1.operator--(1)
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}//d1-d2
int Date::operator-(const Date& d)const
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}//void Date::operator<<(ostream& out)
//{
//	out << _year << "年" << _month << "月" << _day << "日" << endl;
//}//ostream& operator<<(ostream& out, const Date& d)
//{
//	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
//	return out;
//}
//
//
//istream& operator>>(istream& in, Date& d)
//{
//	in >> d._year >> d._month >> d._day;
//	return in;
//}

test.cpp

void TestDate1()
{Date d1(2023, 2, 4);d1.Print();/*Date d2(2023, 2, 29);d2.Print();*/Date d2 = d1 + 1000;d2.Print();d1.Print();Date d3 = d1;//d1=d3+=100;d3 += 1000;d3.Print();d1.Print();}void TestDate2()
{Date d1(2023, 2, 4);d1.Print();Date ret1 =++d1;  //d1.operator++();d1.Print();ret1.Print();Date ret2 = d1++;  //d1.operator++(0);d1.Print();ret2.Print();
}void TestDate3()
{Date d1(2023, 2, 4);d1.Print();d1 -= 100;d1.Print();Date d2(2023, 2, 7);d2 += -100;d2.Print();Date d3(2023, 2, 7);d3 -= -100;d3.Print();--d1;d1--;}void TestDate4()
{Date d1(2023, 2, 4);d1.Print();Date d2(2023, 4, 5);//d2 += -100;d2.Print();cout << (d1 - d2) << endl;cout << (d2 - d1) << endl;}void TestDate5()
{//流插入Date d1(2023, 2, 4);Date d2(2022, 1, 1);//cout << d1;//d1.operator<<(cout);//d1<<cout;operator<<(cout, d1);cout << d1;cout << d1 << d2 << endl;d1 -= 100;//cout << d1;int i = 1;double d = 1.11;//运算符重载+函数重载//cout << i;//cout.operator<<(i)//int//cout << d;//cout.operator<<(d)//double
}void TestDate6()
{Date d1;cin >> d1;cout << d1;}//int main()
//{
//	//TestDate1();
//	//TestDate2();
//	//TestDate3();
//	//TestDate4();
//	//TestDate5();
//	TestDate6();
//
//	return 0;
//}

三、const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
在这里插入图片描述
我们来看看下面的代码

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
void Test()
{Date d1(2022,1,13);d1.Print();const Date d2(2022,1,13);d2.Print();
}

请思考下面的几个问题:

  1. const对象可以调用非const成员函数吗?
  2. 非const对象可以调用const成员函数吗?
  3. const成员函数内可以调用其它的非const成员函数吗?
  4. 非const成员函数内可以调用其它的const成员函数吗?
    const 修饰this,this的类型变成const A,内部不改变成员变量的成员函数,最好加上const,const对象和普通对象都可以调用。

四、取地址及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/161393.html

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

相关文章

python 机器学习 常用函数

一 np.random.randint "randint" 是 "random integer" 的缩写&#xff0c;表示生成随机整数。 np.random.randint 是 NumPy 库中的一个函数&#xff0c;用于生成随机整数。以下是该函数的一般语法&#xff1a; np.random.randint(low, high, size)其中…

开发一款直播弹幕游戏需要多少钱?

开发一款直播弹幕游戏需要多少钱&#xff1f;有好多朋友在咨询过弹幕游戏的开发价格后&#xff0c;都会比较吃惊&#xff0c;一款体量这么小的游戏为什么动辄就要几万块甚至十几万&#xff1f; 我来给你们说分析一下原因&#xff0c;这种游戏如果脱离开直播间&#xff0c;可以…

MQTT协议零基础快速入门

MQTT协议零基础快速入门 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的发布/订阅消息传输协议&#xff0c;广泛应用于物联网&#xff08;IoT&#xff09;和机器对机器&#xff08;M2M&#xff09;通信场景。它具有简单、开放、易于实现等优…

Qt登录界面

头文件&#xff1a; #ifndef QDLGLOGIN_H #define QDLGLOGIN_H#include <QDialog>namespace Ui { class dlgLogin; }class QDlgLogin : public QDialog {Q_OBJECTprivate:bool m_movingfalse;//表示窗口是否在鼠标操作下移动QPoint m_lastPos; //上一次的鼠标位置Q…

MySQL进阶_5.逻辑架构和SQL执行流程

文章目录 第一节、逻辑架构剖析1.1、服务器处理客户端请求1.2、Connectors1.3、第1层&#xff1a;连接层1.4、第2层&#xff1a;服务层1.5、 第3层&#xff1a;引擎层1.6、 存储层1.7、小结 第二节、SQL执行流程2.1、查询缓存2.2、解析器2.3、优化器2.4、执行器 第三节、数据库…

GNU ld链接器 lang_process()(二)

一、ldemul_create_output_section_statements() 位于lang_process()中11行 。 该函数用于创建与目标有关的输出段的语句。这些语句将用于描述输出段的属性和分配。 void ldemul_create_output_section_statements (void) {if (ld_emulation->create_output_section_sta…

FFmpeg 硬件加速视频转码指南

基于 Windows 下演示&#xff0c;Linux 下也可以适用。 所使用 ffmpeg 版本为 BtbN 编译的 win64-gpl 版&#xff08;非 gpl-share&#xff09;&#xff0c;项目地址&#xff1a;BtbN / FFmpeg-Builds 也可以使用 gyan.dev 编译的 git-full 版&#xff0c;地址&#xff1a;gyan…

YOLO目标检测数据集大全【含voc(xml)、coco(json)和yolo(txt)三种格式标签+划分脚本+训练教程】(持续更新建议收藏)

一、作者介绍&#xff1a;资深图像算法工程师&#xff0c;YOLO算法专业玩家&#xff1b;擅长目标检测、语义分割、OCR等。 二、数据集介绍&#xff1a; 真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;分享的绝大部分数据集已应用于各种实际落地项目。所有数据…

喜讯!极限科技成功签约中国一汽搜索数据库三年许可订阅合同!

中标喜讯&#xff01;极限科技 INFINI Easysearch 成功签约中国第一汽车股份有限公司三年订阅合同&#xff01; 一汽集团作为国内汽车行业龙头企业&#xff0c;数字化转型伴随业务发展不断深化&#xff0c;非结构化数据日益成为各类组织数据的增长主力&#xff0c;逐渐成为数据…

Dubbo远程调用

分布式系统中,各个系统间远程调用的性能决定了这个分布式系统好坏 Dubbo是专门用来解决各个服务间调用的RPC框架,解决分布式系统中的远程调用问题 而Zookeeper(注册调度中心)的作用是:比如说50台用户服务器,与50台订单服务器,但是上线后发现用户服务器使用率较低,那么Zookeep…

软件测试/测试开发丨ChatGPT能否成为PPT最佳伴侣

点此获取更多相关资料 简介 PPT 已经渗透到我们的日常工作中&#xff0c;无论是工作汇报、商务报告、学术演讲、培训材料都常常要求编写一个正式的 PPT&#xff0c;协助完成一次汇报或一次演讲。PPT相比于传统文本的就是有布局、图片、动画效果等&#xff0c;可以给到观众更好…

WPF布局控件之DockPanel布局

前言&#xff1a;博主文章仅用于学习、研究和交流目的&#xff0c;不足和错误之处在所难免&#xff0c;希望大家能够批评指出&#xff0c;博主核实后马上更改。 概述&#xff1a; DockPanel 位置子控件基于子 Dock 属性&#xff0c;你有 4 个选项停靠&#xff0c;左 (默认) &…