运算符重载
什么是运算符重载?
运算符重载的本质是一个函数
运算符重载的作用
为什么会用运算符重载机制?
用复数类举例
Complex c3 = c1 + c2;
原因 Complex是用户自定义类型,编译器根本不知道如何进行加减
编译器给提供了一种机制,让用户自己去完成,自定义类型的加减操作。。。。。
这个机制就是运算符重载机制
运算符重载入门
#include <iostream>
using namespace std;
class Complax
{
public:
Complax(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
void printC()
{
cout << "a = " << a << "\tb = " << b << endl;
}
~Complax() {}
//private:
public:
int a;
int b;
};
// 1定义全局函数
Complax myAdd(Complax &c1, Complax &c2)
{
Complax temp(c1.a + c2.a, c1.b + c2.b);
return temp;
}
// 2函数名升级
Complax operator+(Complax &c1, Complax &c2)
{
Complax temp(c1.a + c2.a, c1.b + c2.b);
return temp;
}
int main01()
{
int a = 0, b = 0;
int c; // 基础性数据类型,编译器已经知道了,如何运算
c = a + b;
// a + bi; // 复数运算规则
Complax c1(1, 2), c2(3, 4);
Complax c3; // 2c3是用户自定义数据类型,c++编译器不知道如何运算
// c3 = c1 + c2;
// 3c++编译器应该给我们提供一种机制
// 让自定义数据类型 有机会进行 运算符操作 ==> 运算符重载
// 4运算符重载机制
Complax c4 = myAdd(c1, c2);
c4.printC();
// 步骤二:Complax c4 = c1+c2;
// Complax c5 = operator+(c1, c2);
// c5.printC();
// 步骤三:
Complax c5 = c1 + c2;
c5.printC();
// 运算符重载的本质 是 函数调用
return 0;
}
运算符重载的限制
运算符重载基础
运算符重载的方法步骤
全局函数、类成员函数方法实现运算符重载步骤
- 要承认操作符重载是一个函数,写出函数名称operator+ ()
- 根据操作数,写出函数参数
- 根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务
#include <iostream>
using namespace std;
class Complax
{
private:
int a;
int b;
// 重载友元函数 全局函数 操作符重载
friend Complax operator+(Complax &c1, Complax &c2);
public:
Complax(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
void printC()
{
cout << "a = " << a << "\tb = " << b << endl;
}
// 2成员函数法 实现 - 运算符重载
Complax operator-(Complax &c2)
{
// this->a -= c2.a;
// this->b -= c2.b;
// return *this; // 这一个会改变c1的值,因为是+=
Complax temp(this->a - c2.a, this->b -c2.b);
return temp;
}
};
// 1全局函数法 实现 + 运算符重载
Complax operator+(Complax &c1, Complax &c2)
{
Complax temp(c1.a+c2.a, c1.b+c2.b);
return temp;
}
/*
全局函数,类成员函数方法实现运算符重载步骤
1:要承认运算符重载是一个函数, 写出函数名称
2: 根据操作数,写出函数参数
3:根据业务,完善函数的返回值(看函数返回引用,元素,指针),及实现函数业务
*/
int main()
{
Complax c1(1, 2), c2(3, 4);
// 1全局函数法 实现 - 运算符重载
// Complax operator+(Complax &c1, Complax &c2)
Complax c3 = c1 + c2;
c3.printC();
// 2成员函数法 实现 - 运算符重载
// Complax operator-(Complax &c2);
Complax c4 = c1 - c2;
c4.printC();
return 0;
}
重载++ 需要注意(重点)
#include <iostream>
using namespace std;
class Complax
{
private:
int a;
int b;
// 1全局函数法 实现 ++ 运算符重载
friend Complax& operator++(Complax &c1); // 这里是返回一个引用注意,前置++
friend Complax operator++(Complax &c2, int); // 后置++
public:
Complax(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
// 前置--
// 因为前置返回的是本身,所以返回一个引用
// Complax& operator--(Complax &c1) 这个是写错的,注意错在哪里
Complax& operator--()
{
this->a --;
this->b --;
return *this;
}
// 后置-- 因为后置返回的是一个副本所以不用 返回引用
Complax operator--(int)
{
Complax tem = *this;
this->a--;
this->b--;
return tem;
}
void printC()
{
cout << "a = " << a << "\tb = " << b << endl;
}
};
// 特别注意 只有成员函数才有 this指针
// 1全局函数法 实现 前置++ 运算符重载
Complax& operator++(Complax &c1)
{
c1.a++;
c1.b++;
return c1;
}
// 后置++
Complax operator++(Complax &c2, int) // int防止与前置++重载而加的占位符
{
// 因为后置++是使用, 在让c2++所以要定义一个临时变量
Complax tem = c2;
c2.a++;
c2.b++;
return tem;
}
int main()
{
Complax c1(1, 2), c2(30, 40);
// 1全局函数法 实现 前置++ 运算符重载
// Complax& operator++(Complax &c1);
++c1; // 相当于 operator++(c1);
c1.printC();
// 2成员函数 实现 前置-- 运算符重载
// 相当于c2.operator--();
--c2;
c2.printC();
// 1全局函数法 实现 后置++ 运算符重载
// operator++(c2);
c2++;
// Complax& operator++(Complax &c1); 前置++
// Complax operator++(Complax &c2, int); // int防止与前置++重载而加的占位符
c2.printC();
// 2成员函数 实现 后置-- 运算符重载
// 相当于c2.operator--();
// Complax operator--(int) 函数原型
c1--;
c1.printC();
return 0;
}
友元函数 重载 << 还有链式编程
//a)用全局函数方法实现 << 操作符
ostream& operator<<(ostream &out, Complex &c1)
{
//out<<"12345,生活真是苦"<<endl;
out<<c1.a<<" + "<<c1.b<<"i "<<endl;
return out;
}
//调用方法
cout<<c1;
//链式编程支持
cout<<c1<<"abcc";
//cout.operator<<(c1).operator<<("abcd");
//函数返回值充当左值 需要返回一个引用
b)类成员函数方法无法实现 << 操作符重载
//因拿到cout这个类的源码
//cout.operator<<(c1);
注意点
友员函数重载运算符常用于运算符的左右操作数类型不同的情况
在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择
友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
C++中不能用友员函数重载的运算符有
= () [] ->
所以这个可以在写一个operator + 或者写一个友元函数重载
简单版的复数类重载
#include <iostream>
using namespace std;
class Complax
{
private:
int a;
int b;
friend ostream& operator<<(ostream &out, const Complax &c1);
public:
Complax(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
// +运算符重载
Complax operator+(Complax &c2)
{
Complax tem(this->a + c2.a, this->b + c2.b);
return tem;
}
// -运算符重载
Complax operator-(Complax &rhl)
{
Complax tem(this->a - rhl.a, this->b - rhl.b);
return tem;
}
// 前置--
Complax& operator--()
{
this->a --;
this->b --;
return *this;
}
// 前置++
Complax& operator++()
{
this->a++;
this->b++;
return *this;
}
// 后置-- 因为后置返回的是一个副本所以不用 返回引用
Complax operator--(int)
{
Complax tem = *this;
this->a--;
this->b--;
return tem;
}
// 后置++
Complax operator++(int)
{
Complax tem = *this;
this->a++;
this->b++;
return tem;
}
void printC()
{
cout << "a = " << a << "\tb = " << b << endl;
}
};
// 这里的ostream 不能加const
ostream& operator<<(ostream &out, const Complax &c1)
{
cout <<"重载<<运算符 链式输出";
out << "c1.a = " << c1.a << "\t c1.b = " << c1.b << endl;
}
// 在<< 和 >> 运算符重载时 只能使用全局函数就是 加个友元声明
int main32()
{
Complax c1(1, 2), c2(30, 40);
int a = 10;
cout << a << endl; // 基本数据类型
// 自定义数据类型
cout << c1;
// 全局函数调用方法
// operator<<(cout ,c1);
// 函数类型声明
// friend void operator<<(const ostream &out, const Complax &c1);
cout << c2;
// 成员函数调用方法 在ostream类中添加 成员函数 operator<<
// 但是ostream类的源码拿不到,所以在这种情况下只能使用友元函数
// cout.operator<<(c2);
// 最好都写这一个
cout <<c1 << "sflkasdf";
// cout.operator<< (c1).operator<<("slkfdkls");
// 上式等价于 void.operator<<"slkfdls"; 所以
// 如果链式输入的话
// 就要把返回时改为ostroam &;
return 0;
}
int main031()
{
Complax c1(1, 2), c2(30, 40);
// 后置++
c1++;
c1.printC();
// 前置++
++c2;
c2.printC(); // 虽然最后结果显示都是一样的,但是调用的不一样,
// 因为printC函数输入的是前置,或后置之后的结果所以会输出相同
//+运算符重载
Complax c3 = c1 + c2;
c3.printC();
// -运算符重载
Complax c4 = c3 - c1;
c4.printC();
cout << "Hello world!" << endl;
return 0;
}
简单的name类重载
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
class Name
{
public:
Name (const char *mp)
{
len = strlen(mp);
p = (char *)malloc(sizeof(len+1)); // 注意细节
strcpy(p, mp);
}
//Name obj2 = obj1;
// 解决方案:手工编写拷贝构造函数,使用深拷贝
Name (const Name &obj1)
{
len = obj1.len;
p = (char *)malloc(sizeof(len+1));
strcpy(p, obj1.p);
}
// 成员函数 =运算符重载 不能写全局函数
Name& operator=(const Name &obj)
{
// 先释放旧的内存 特别注意
if(p != NULL)
{
delete []p;
len = 0;
}
len = obj.len;
p = new char[len+1];
strcpy(p, obj.p);
return *this;
}
~Name ()
{
cout << "\t析构函数" << endl;
if (p != NULL)
{
free(p);
p = NULL;
len = 0;
}
}
void printName()
{
cout <<"\tp = " << this->p << "\t len = " << this->len << endl;
}
private:
char *p;
int len;
};
// 对象析构的时候会出现,错误
// 调试存在bug
// 析构的时候会出现二次释放
//默认的拷贝构造函数,如果要对指针进行拷贝,则只是浅拷贝,拷贝过后是两个变量指向
//同一段内存,释放拷贝的obj2后,obj1的指针就是垃圾值,在释放就会出错
void objmain()
{
Name obj1("aaaa");
Name obj2 = obj1;
Name obj3("sfs");
// obj3 = obj1; // 等号操作 c++编译器会对=进行默认的重载(同样会发生二次释放的问题)
// 就需要手动的写 operator= 函数
cout << "成员函数=重载 ";
obj3 = obj1;
obj3.printName();
// obj3.operator=(const Name & obj1);
// void operator=(const Name &obj) // 函数声明
// 如果要执行 obj3 = obj2 = obj1; 就需要把void 改成 Name&
{
obj3 = obj2 = obj1;
obj3.printName();
obj2.printName();
obj1.printName();
}
}
int main04()
{
objmain();
}
// 重载 = 运算符注意点
// 1 先把旧的内存释放
// 2 返回一个引用 obj3 = obj1 = obj2;
// 3数据进行拷贝
编写复数类
main.cpp
#include <iostream>
//#include "Complex.h"
#include "Complex.cpp"
using namespace std;
// 需要注意的是 如果是在main中包含complex.cpp的话,就要在complex.cpp的函数
// 写成内联函数, 并且在其中写上#include"Complex.h" 推荐
// 或者在main中包含#include"Complex.h", 然后再complex.h中包含#include"Complex.cpp"
// Complex类
int main() {
std::cout << "Hello, World!" << std::endl;
Complex c(2, 3);
Complex c2(3, 4);
c += c2;
c -= c2;
Complex c3 = c2; // 调用拷贝构造函数
c3 = c; // 运算符重载
cout << c3 << endl;
Complex c4 = c + c2;
c2++;
cout << c << c2 << endl;
c == c2;
cout << sizeof(c)<<endl; // 16个字节
// cout << c.real() << "i+"<< c.imge() << endl;
return 0;
}
Complex.h
//
// Created by lk on 18-5-28.
//
#ifndef HOUJIE_COMPLEX_H // 防卫式声明
#define HOUJIE_COMPLEX_H
#include <iostream>
class Complex {
public:
Complex(double re = 0, double im = 0)
: re(re), im(im) {}
// 拷贝构造函数
Complex(const Complex &obj)
: re(obj.re), im(obj.im) {}
Complex operator+(const Complex &obj);
Complex operator-(const Complex &obj);
Complex &operator+=(const Complex &obj);
Complex &operator-=(const Complex &obj);
bool operator==(const Complex &obj);
Complex &operator--();
Complex &operator++();
Complex operator--(int);
Complex operator++(int);
double real() { return re; }
double imge() { return im; }
private:
double re;
double im;
friend std::ostream &operator<<(std::ostream &os, Complex &obj);
};
#endif //HOUJIE_COMPLEX_H
Complex.cpp
// Created by lk on 18-5-28.
//
#include "Complex.h"
#include <iostream>
inline
Complex &Complex::operator+=(const Complex &obj) {
this->re += obj.re;
this->im += obj.im;
return *this;
}
inline
Complex &Complex::operator-=(const Complex &obj) {
this->im -= obj.im;
this->re -= obj.re;
return *this;
}
// 前置-- --c
inline
Complex &Complex::operator--() {
this->im--;
this->re--;
return *this;
}
// 前置++ ++c
inline
Complex &Complex::operator++() {
this->im++;
this->re++;
return *this;
}
// 后置-- 因为后置返回的是一个副本所以不用返回引用 c--
inline
Complex Complex::operator--(int) {
Complex tem = *this;
this->im--;
this->re--;
return tem;
}
// 后置++ c++
inline
Complex Complex::operator++(int) {
Complex tem = *this;
this->im++;
this->re++;
return tem;
}
// 重载+ 调用者 c+c2
// 这是类内函数, 用一个参数, 如果是类外的就要用两个参数
inline
Complex Complex::operator+(const Complex &obj) {
return Complex(this->im + obj.im, this->re + obj.re);
}
// 重载- 调用者 c-c2
inline
Complex Complex::operator-(const Complex &obj) {
Complex tem(this->im - obj.im, this->re - obj.re);
return tem;
}
// 重载 == c == c2
inline
bool Complex::operator==(const Complex &obj) {
return (this->im == obj.im && this->re == obj.re);
}
// 重载<< cout << c
inline
std::ostream &
operator<<(std::ostream &os, Complex &obj) {
return os << obj.re << obj.im;
}
编写string类
main.cpp
#if 0
#include <iostream>
//#include "MyString.h"
#include "MyString.cpp"
using namespace std;
int main()
{
MyString s("aaa");
MyString s2(10);
s2 = s;
cout << s << s2 << endl;
if (s == s2)
{
cout << "s == s2" << endl;
}
else
cout << "s != s2 "<< endl;
if (s == "aab")
cout << "s == aab" << endl;
if (s != "aab")
cout << "s != aab" << endl;
s2 = "abbb00";
auto t = s > "abb00";
cout << t << endl;
cout << s[2] << endl;
// cin >> s;
// cout << s;
MyString s3 = "sjkf";
s3 = s2;
cout << s3 ;
return 0;
}
#endif
#include <iostream>
#include <cstring>
#include "MyString.cpp"
using namespace std;
int main1() {
// 这里也是一个功能
MyString s1(10);
cout << "请输入s1" << endl;
// 重载 >>
cin >> s1;
cout << s1 << endl;
return 0;
}
int main2() {
MyString s1;
MyString s2("ab");
MyString s3(s2);
s3 = "abcd";
int flag = (s3 < "bbbb");
{
if (flag > 0)
cout << "s3 > bbbb " << endl;
else if (flag == 0)
cout << "s3 = bbbb " << endl;
else
cout << "s3 < bbbb " << endl;
}
MyString s4 = "abcds";
cout << s4 << endl;
return 0;
}
int main() {
MyString s1;
MyString s2("aaa");
MyString s3(s1); // MyString s3 = s1;
MyString s4("s4444");
// 运算符重载 =
// MyString& operator=(MyString & rhs); 函数原型调用s4.operator=(MyString & rhs)
s4 = s2;
// 也是调用运算符重载 MyString& operator=(const char *p);
s4 = "2222";
s4[0]; // 所以说 []重载时 最后是返回一个引用
cout << "当右值 s4[0] = " << s4[0] << endl;
s4[0] = 'a';
cout << "当左值 s4[0] = " << s4[0] << endl;
// s4.operator[](int index);
// char& operator[](int index) // 当左值,当右值
// << 重载
cout << s4 << endl;
// MyString& operator<<(ostream &out, MyString &rhs)
// == 重载
if (s2 == "aaa") {
cout << "相等" << endl;
} else {
cout << "不相等" << endl;
}
// if (s2 != "abcd")
// s2.operator==(const char *p)
// bool operator==(const char *p)
if (s2 == s4) {
cout << "相等" << endl;
} else {
cout << "不相等" << endl;
}
// if (s2 != s4)
// bool operator==(const MyString &rhs)
cout << "Hello world!" << endl;
return 0;
}
MyString.h
//
// Created by lk on 18-6-10.
//
#ifndef CESHI_MYSTRING_H
#define CESHI_MYSTRING_H
#include <cstring>
#include <iostream>
using namespace std;
class MyString {
friend ostream &operator<<(ostream &out, const MyString &obj);
friend istream &operator>>(istream &in, MyString &obj);
public:
// 构造
MyString();
MyString(const char *data);
MyString(const int leng);
// 拷贝构造
MyString(const MyString &obj);
~MyString();
public:
MyString &operator=(const MyString &obj);
MyString &operator=(const char *data);
bool operator==(const MyString &obj) const; // const表示this->m_data不能修改
bool operator!=(const MyString &obj) const;
bool operator==(const char *data) const;
bool operator!=(const char *data) const;
public:
int operator>(const MyString &obj) const;
int operator<(const MyString &obj) const;
int operator>(const char *data) const;
int operator<(const char *data) const;
public:
char &operator[](const int index) const;
private:
char *m_data;
int len;
};
#endif //CESHI_MYSTRING_H
MyString.cpp
//
// Created by lk on 18-6-10.
//
#include "MyString.h"
//#include <cstring>
// 无参构造函数
inline MyString::MyString() {
len = 0;
m_data = new char[1];
strcpy(m_data, "");
}
inline MyString::MyString(const char *data) {
// 如果有初始值
if (data) {
len = strlen(data);
m_data = new char[len + 1];
strcpy(m_data, data);
} else {
len = 0;
m_data = new char[1];
strcmp(m_data, "");
}
}
inline MyString:: //构造一个空串
MyString(const int leng) {
/*
if (len == 0)
{
m_len = 0;
m_p = new char[m_len + 1];
strcpy(m_p, "");
}
else
{
m_len = len;
m_p = new char[m_len + 1];
// strcpy(m_p, ""); 哪一个都行
memset(m_p, 0, m_len);
}
*/
len = leng;
m_data = new char[len + 1];
strcpy(m_data, ""); // 构造空串
}
// MyString s1 = s2; 拷贝构造
inline MyString::
MyString(const MyString &obj) {
len = strlen(obj.m_data);
m_data = new char[len + 1];
strcpy(m_data, obj.m_data);
}
// 析构函数
inline MyString::~MyString() {
if (m_data != NULL) {
len = 0;
delete[]m_data;
this->m_data = NULL; // 防止出现野指针
}
}
// s1 = s2 = s3; 拷贝赋值
inline MyString &MyString::operator=(const MyString &obj) {
// 提高水平的判断, 就是如果是自己赋值给自己的话,就要加上这句,必须加
// 如果this 等于obj对象的指针, 这里的&是取地址
if (this == &obj) {
return *this;
}
// 释放旧内存
if (m_data != NULL) {
len = 0;
delete[]m_data;
m_data = NULL;
}
// 分配新内存 并赋值
len = strlen(obj.m_data);
m_data = new char[len + 1];
strcpy(m_data, obj.m_data);
return *this;
}
// 拷贝赋值 s1 ="aaa"
inline MyString &MyString::operator=(const char *data) {
// if (data != NULL)
// 释放旧内存
if (m_data == NULL) {
delete[]m_data;
len = 0;
m_data = NULL;
}
// 分配新内存
if (data == NULL) {
len = 0;
m_data = new char[1];
strcpy(m_data, "");
} else {
len = strlen(data);
m_data = new char[len + 1];
strcpy(m_data, data);
}
return *this;
}
inline bool
MyString::operator==(const MyString &obj) const {
if (len != obj.len)
return false;
else {
if (!strcmp(m_data, obj.m_data))
return true;
else
return false;
}
}
inline bool
MyString::operator!=(const MyString &obj) const {
return !(*this == obj);
}
inline bool
MyString::operator==(const char *data) const {
if (strlen(data) != len) {
return false;
} else {
if (!strcmp(data, m_data))
return true;
return false;
}
}
inline bool
MyString::operator!=(const char *data) const {
return !(*this == data);
}
inline int
MyString::operator<(const MyString &obj) const {
return strcmp(m_data, obj.m_data);
}
inline int
MyString::operator>(const MyString &obj) const {
return strcmp(obj.m_data, m_data);
}
inline int
MyString::operator>(const char *data) const {
return strcmp(m_data, data);
}
inline int
MyString::operator<(const char *data) const {
return strcmp(data, m_data);
}
// s[2]
inline char & // 多看
MyString::operator[](const int index) const {
static char sNull = '\0';
if (strlen(m_data) > index + 1 && index >= 0)
return m_data[index];
else {
cout << "下标越界" << endl;
return sNull;
}
}
// << 重载 应该是友元 注意返回值,
// cout << s4 << endl; 全局的
// friend ostream& operator<<(ostream &out, MyString &obj);
inline ostream &operator<<(ostream &out, const MyString &obj) {
return out << obj.m_data << endl; // 如果是*obj.m_data则是 第一个元素
}
inline istream &operator>>(istream &in, MyString &obj) {
return in >> obj.m_data;
}
MyString.cpp
更多C++相关知识体系,请移步C++知识目录。