C++运算符重载
2021/8/7 14:06:32
本文主要是介绍C++运算符重载,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
运算符重载
什么是运算符重载?
运算符重载的本质是一个函数
运算符重载的作用
为什么会用运算符重载机制?
用复数类举例
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++知识目录。
这篇关于C++运算符重载的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享