1、赋值运算符函数介绍
运算符重载是C++一种形式的多态,允许赋予C++运算符多种含义。
例如我们有一个Time类,包括小时和分钟,平常我们是没法直接让两个Time类相加获取总的小时分钟的。这是我们可以使用对+运算符重载。
1.1 未使用重载形式的写法
MyTime.h 头文件
#ifndef MYTIME_H_
#define MYTIME_H_
class Time
{
private:
int hour;
int minute;
public:
Time();
Time(int h,int m);
//和另一个类时间相加
Time Sum(const Time& t) const;
//输出当前类表示的时间
void Show();
};
#endif // !MYTIME_H_
MyTime.cpp 头文件方法实现
#include <iostream>
#include <string.h>
#include "MyTime.h"
Time::Time() {
hour = minute = 0;
}
Time::Time(int h, int m) {
hour = h;
minute = m;
}
Time Time::Sum(const Time& t) const {
Time sum;
sum.minute = minute + t.minute;
sum.hour = hour + t.hour + sum.minute / 60;
sum.minute %= 60;
return sum;
}
void Time::Show() {
std::cout << hour << " " << minute << std::endl;
}
main.cpp
#include <iostream>
#include "MyTime.h"
int main() {
Time t1(2, 30);
Time t2(2, 30);
Time total = t1.Sum(t2);
total.Show();
//输出时间 5 0
return 0;
}
1.2 使用重载形式的写法
MyTime.h 头文件
#ifndef MYTIME_H_
#define MYTIME_H_
class Time
{
private:
int hour;
int minute;
public:
Time();
Time(int h,int m);
//和另一个类时间相加
Time operator+(const Time& t) const;
//输出当前类表示的时间
void Show();
};
#endif // !MYTIME_H_
MyTime.cpp 头文件方法实现
#include <iostream>
#include <string.h>
#include "MyTime.h"
Time::Time() {
hour = minute = 0;
}
Time::Time(int h, int m) {
hour = h;
minute = m;
}
Time Time::operator+(const Time& t) const
{
Time sum;
sum.minute = minute + t.minute;
sum.hour = hour + t.hour + sum.minute / 60;
sum.minute %= 60;
return sum;
}
void Time::Show() {
std::cout << hour << " " << minute << std::endl;
}
main.cpp
#include <iostream>
#include "MyTime.h"
int main() {
Time t1(2, 30);
Time t2(2, 30);
//这种写法会被自动转换成下面
Time total = t1 + t2;
//Time total = t1.operator+ (t2);
total.Show();
//输出时间 5 0
return 0;
}
1.3 一个较完整的例子
下面例子主要演示用法,不在细算小时与分钟。
后面会对此例子进行完整的讲解。
MyTime.h 头文件
#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED
#include <iostream>
class Time
{
private:
int hour;
int minute;
public:
Time();
Time(int h, int m);
///重载+运算符
Time operator+(const Time& t) const;
///重载-运算符。一个减法、一个取反
Time operator-(const Time& t) const;
Time operator-() const;
///重载*运算符。
Time operator*(int m) const;
friend Time operator*(int m, const Time& t);
///输出类时间
friend std::ostream& operator<<(std::ostream& os, const Time& t);
};
#endif // HEAD_H_INCLUDED
MyTime.cpp 头文件方法实现
#include <iostream>
#include <string.h>
#include <string>
#include "head.h"
Time::Time() {
hour = minute = 0;
}
Time::Time(int h, int m) {
hour = h;
minute = m;
}
Time Time::operator+(const Time& t) const
{
Time sum;
sum.minute = minute + t.minute;
sum.hour = hour + t.hour + sum.minute / 60;
sum.minute %= 60;
return sum;
}
Time Time::operator-(const Time& t) const
{
Time temp;
temp.minute = minute - t.minute;
temp.hour = hour - t.hour;
temp.minute %= 60;
return temp;
}
Time Time::operator-() const
{
return Time(-hour,-minute);
}
Time Time::operator*(int m) const
{
Time temp;
temp.hour = hour*m + minute*m / 60;
temp.minute = minute*m % 60;
return temp;
}
Time operator*(int m, const Time& t) {
Time temp;
temp.hour = t.hour*m + t.minute*m / 60;
temp.minute = t.minute*m % 60;
return temp;
}
std::ostream& operator<<(std::ostream& os,const Time& t)
{
os<<"小时:"<<t.hour<<" 分钟:"<<t.minute<<std::endl;
return os;
}
main.cpp
#include "head.h"
#include <iostream>
using std::cout;
using std::string;
using std::endl;
int main()
{
Time t1(2,30),t2(2,30);
t1 = t1+t2;
cout<<t1;
t1 = t1-t2;
cout<<t1;
t1 = -t1;
cout<<t1;
t1 = 2*t1;
cout<<t1;
t1 = t1*2;
cout<<t1;
return 0;
}
输出:
重载 * 运算符:
对于乘法,可以注意到我们加了一个友元函数对其进行重载。这是为什么呢?
//当运算符在对象后面时,t1*2会自动被识别为t1.operator*(2),调用函数
t1 = t1*2;
cout<<t1;
//但是当运算符在对象前面时,其并不能被识别为t1.operator*(2)
//这时就需要使用友元函数*进行重载
t1 = 2*t1;
cout<<t1;
重载 - 运算符:
还有一种比较特殊的情况我们并不需要友元函数也能让运算符在前面进行重载。对于 - 运算符,平常我们会有两种情况的用法。
1、减法运算符,使用两个操作数。(x-y)
2、负号运算符,使用一个操作数。(-x)
通过例子可以发现,当我们重载第二种负号运算符的用法时,就可以在对象前面使用了。
重载 << 运算符:
friend std::ostream& operator<<(std::ostream& os, const Time& t);
对于<<比较特殊,cout是std命名空间中ostream的一个输出流对象,并且因为ostream没有默认复制函数,所以我们使用&获取ostream的引用。
另外因为cout可以连续使用<<进行输出,所以我们返回os的引用
分析下面语句:
cout<<t1<<t2<<"hello world"; 等价于
( (cout<<t1) <<t2) <<"hello world";
第一步cout<<t1,调用Time类中的重载,并返回cout对象。
第二步cout<<t2,调用Time类中的重载,并返回cout对象。
第三步cout<<"hello world",调用ostream中的<<定义,并返回cout对象。
2、剑指Offer面试题1
剑指Offer-面试题1
给如下类型添加赋值运算符函数。
class CMyString {
private:
char* m_pData;
public:
CMyString(const char* pData = nullptr);
CMyString(const CMyString& str);
~CMyString();
};
//使用&,跟参数+地址符一样,可以节省内存
CMyString& CMyString::operator=(const CMyString& str)
{
//判断参数str是否是当前对象实例,是就直接返回
if (this == &str)
return *this;
//重新给data赋值
delete []m_pData;
m_pData = nullptr;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy_s(m_pData, strlen(str.m_pData) + 1, str.m_pData);
return *this;
}
3、疑问
3.1 常量引用参数
//如果传入的参数不是引用而是实例,对于形参str,会调用类的复制构造函数复制一份实参给str。
CMyString& operator=(CMyString str);
//这样节约了一份复制过程,可以提高代码效率。并且我们不需要修改str的内容,所以加上const关键字。
CMyString& operator=(const CMyString& str);
3.2 在VS中调用strcpy()函数
在VS中调用strcpy()函数会出现微软的警告,微软修改了原来的函数,改写了之后变得更安全和方便,我们使用新的就好了。(警告会有提示,不记住也么事)
库函数改写例子:
mkdir改写为 _mkdir
fopen”改写为 fopen_s
stricmp改写为 stricmp_s
strcpy改写为strcpy_s
3.3 c++的null和nullptr
C++null就是个字面常量0,nullptr则可以转换为void* 再隐式转换为char*