C++类和对象中下篇

news/2024/11/28 21:37:43/

赋值运算符重载

运算符重载

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

函数名字为:关键字operator后面接需要重载的运算符符号。

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

注意:

1.不能通过连接其他符号来创建新的操作符:比如operator@

2.重载操作符必须有一个类类型参数

3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义。但是这点指数相对的规定,比如说你想恶搞别人,就把符号写反也是可以的。但最好不要这样做,要不然,免不了被一顿骂。

4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

5.   .* 和::和sizeof和?:和. 这5个运算符不能重载。这个经常在笔试选择题中出现。

对.*的解释

// 全局的operator==
class Date
{ 
public:Date(int year = 2024, int month = 4, int day = 14){_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(2024, 4, 13);Date d2(2024, 4, 13);cout<<(d1 == d2)<<endl;
}

 解决方法:重载为成员函数

class Date
{
public:Date(int year = 1111, int month = 11, int day = 1){_year = year;_month = month;_day = day;}bool operator==(const Date& d){return this->_year == d._year&& this->_month == d._month&& this->_day == d._day;}private:int _year;int _month;int _day;
};

赋值运算符重载

1.赋值运算符重载格式

参数类型:const T&,传递引用可以提高传参效率

返回值类型:T& ,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

检测是否自己给自己赋值

返回*this

class Date
{
public:Date(int year = 1111, int month = 11, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}bool operator==(const Date& d){return this->_year == d._year&& this->_month == d._month&& this->_day == d._day;}//这里返回值不是void针对的是多次赋值,比如d1=d2=d3//这里然后就用传引用了,如果用传值的话会返回临时变量,即多进行了拷贝,这从效率的角度上来说//是不太好的Date operator=(const Date& 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 = 2024;int _month = 4;int _day = 14;// 自定义类型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;
}

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

这里的解决方法 就是实现深拷贝,深拷贝就是开跟s1一样大的空间给s2.

前置++和后置++重载

class Date
{
public:Date(int year = 2024, int month = 4, int day = 14){_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>
using namespace std;#include<assert.h>class Date
{// 友元函数声明friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:Date(int year = 1900, int month = 1, int day = 1);void Print() const;// 直接定义类里面,他默认是inline// 频繁调用int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };// 365天 5h +if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){return 29;}else{return monthDayArray[month];}}bool CheckDate();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;// d1 += 100Date& operator+=(int day);Date operator+(int day) const;// d1 -= 100Date& operator-=(int day);// d1 - 100;Date operator-(int day) const;// d1 - d2int operator-(const Date& d) const;// ++d1 -> d1.operator++()Date& operator++();// d1++ -> d1.operator++(1)// 为了区分,构成重载,给后置++,强行增加了一个int形参// 这里不需要写形参名,因为接收值是多少不重要,也不需要用// 这个参数仅仅是为了跟前置++构成重载区分Date operator++(int);// 15:55Date& operator--();Date operator--(int);// 流插入// 不建议,因为Date* this占据了一个参数位置,使用d<<cout不符合习惯//void operator<<(ostream& out);private:int _year;int _month;int _day;
};// 重载
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

//Date.cpp
#include"Date.h"bool Date::CheckDate()
{if (_month < 1 || _month > 12|| _day < 1 || _day > GetMonthDay(_year, _month)){return false;}else{return true;}
}Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!CheckDate()){cout << "日期非法" << endl;}
}void Date::Print() const
{cout << _year << "-" << _month << "-" << _day << endl;
}// d1 < d2
bool Date::operator<(const Date& d) const
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){return _day < d._day;}}return false;
}// d1 <= d2
bool Date::operator<=(const Date& d) const
{return *this < d || *this == d;
}bool Date::operator>(const Date& d) const
{return !(*this <= d);
}bool Date::operator>=(const Date& d) const
{return !(*this < d);
}bool Date::operator==(const Date& d) const
{return _year == d._year&& _month == d._month&& _day == d._day;
}bool Date::operator!=(const Date& d) const
{return !(*this == d);
}// d1 += 50
// d1 += -50Date& Date::operator+=(int day)
{if (day < 0){return *this -= -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) const
{Date tmp = *this;tmp += day;return tmp;
}// d1 + 50
//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;
//}
//d1 += 50
//Date& Date::operator+=(int day)
//{
//	*this = *this + day;
//
//	return *this;
//}// 
// d1 -= 100
Date& Date::operator-=(int day) 
{if (day < 0){return *this += -day;}_day -= day;while (_day <= 0){--_month;if (_month == 0){_month = 12;_year--;}// 借上一个月的天数_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++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}Date& Date::operator--()
{*this -= 1;return *this;
}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)
{cout << "请依次输入年月日:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "日期非法" << endl;}return in;
}

 

//Test.cpp
#include"Date.h"void TestDate1()
{Date d1(2024, 4, 14);Date d2 = d1 + 30000;d1.Print();d2.Print();Date d3(2024, 4, 14);Date d4 = d3 - 5000;d3.Print();d4.Print();Date d5(2024, 4, 14);d5 += -5000;d5.Print();
}void TestDate2()
{Date d1(2024, 4, 14);Date d2 = ++d1;d1.Print();d2.Print();Date d3 = d1++;d1.Print();d3.Print();/*d1.operator++(1);d1.operator++(100);d1.operator++(0);d1.Print();*/
}void TestDate3()
{Date d1(2024, 4, 14);Date d2(2034, 4, 14);int n = d1 - d2;cout << n << endl;n = d2 - d1;
}void TestDate4()
{Date d1(2024, 4, 14);Date d2 = d1 + 30000;// operator<<(cout, d1)cout << d1;cout << d2;cin >> d1 >> d2;cout << d1 << d2;
}void TestDate5()
{const Date d1(2024, 4, 14);d1.Print();//d1 += 100;d1 + 100;Date d2(2024, 4, 25);d2.Print();d2 += 100;d1 < d2;d2 < d1;
}

const成员

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

 

#include<iostream>
using namespace std;
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修饰后的对象权限是较小的,未被修饰的权限较大

1.const对象可以调用const成员函数,const对象不能调用非const成员函数
2.非const对象可以调用const成员函数,非const对象可以调用非const成员函数
3.const成员函数不能调用非const成员函数
4.非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.ppmy.cn/news/1418538.html

相关文章

数据结构——Java

数组 Java数组是一种基础且重要的数据结构&#xff0c;用于存储一组相同类型的数据元素。 在Java中&#xff0c;数组有以下关键特性&#xff1a; 同质性&#xff1a;数组中的每个元素都具有相同的数据类型。有序性&#xff1a;数组中的元素是有序排列的&#xff0c;可以通过…

C++:运算符与表达式 (信奥赛练习)

运算符与表达式 1. 运算符基本算术运算符赋值运算符比较运算符逻辑运算符 2. 表达式算术表达式逻辑表达式赋值表达式总结 1. 运算符 运算符是用于执行特定操作的符号。在编程和数学中&#xff0c;运算符是表达式的重要组成部分。它们可以用于执行加减乘除等基本算术运算&#…

webrtc m98编译问题记录

webrtc m98编译问题 执行命令 gn gen --targetx64 --idevs2019 --args"is_debugtrue rtc_enable_protobuffalse is_clangfalse target_cpu\"x64\" enable_iterator_debuggingtrue use_custom_libcxxfalse symbol_level0 rtc_include_testsfalse" out/d…

Bash 编程精粹:从新手到高手的全面指南之逻辑控制

在 Unix 和 Linux 系统中&#xff0c;Bash&#xff08;Bourne-Again Shell&#xff09;是一种广泛使用的 shell&#xff0c;提供了强大的脚本编程能力。本文将详细介绍 Bash 脚本中的逻辑控制结构&#xff0c;包括条件判断、分支选择、循环控制以及退出控制等内容。 条件判断&…

hibernate缓存

hibernate缓存 hibernate提供了两个级别的缓存 一级缓存 session级别的缓存&#xff0c;属于事务范围的缓存&#xff0c;由hibernate来管理 二级缓存 sessionFactory级别的缓存&#xff0c;属于进程范围内的缓存 一级缓存 一级缓存是session级别的缓存&#xff0c;且是一种强制…

EasyUI Jquery 学习笔记 ——DataGrid(数据网格)与 Tree(树)详细版

1. DataGrid(数据网格)与 Tree(树) 1.1 Datagrid 数据网格 扩展自 $.fn.panel.defaults。通过 $.fn.datagrid.defaults 重写默认的 defaults。 数据网格(datagrid)以表格格式显示数据,并为选择、排序、分组和编辑数据提供了丰富的支持。数据网格(datagrid)的设计目…

【C++题解】1601. 挖胡萝卜

问题&#xff1a;1601. 挖胡萝卜 类型&#xff1a;基本运算、小数运算 题目描述&#xff1a; 小兔朱迪挖了 x 个胡萝卜&#xff0c;狐狸尼克挖到胡萝卜数量是小兔挖到的 3 倍&#xff0c;小羊肖恩挖到胡萝卜的数量比狐狸尼克少 8 个。 请你编程计算一下狐狸尼克和小羊肖恩分别…

GET与POST:详述HTTP两大请求方法的语义、数据处理机制、安全特性与适用场景

GET和POST方法在HTTP请求中具有明确的角色分工和特性差异。GET适用于读取操作和不敏感数据的传递&#xff0c;强调可缓存性和安全性&#xff0c;而POST适用于写入操作和敏感数据的提交&#xff0c;提供了更大的数据承载能力和更强的隐私保护。本文详细介绍了GET与POST请求方法的…