浅谈C++里面的操作符重载

来源:互联网 发布:北京计算机编程培训学校 编辑:程序博客网 时间:2024/06/10 03:49
Q: 什么是运算符重载?


A:C++默认支持基本数据类型的运算操作; (int char double float 等等),
但是不默认支持自定义的类的运算, 比如自定义一个Name类, 
你可以使用 "+" 运算符对两个基本数据类型进行运算
但是你无法使用"+"运算符对两个Name类进行运算。
因为C++编译器不知道你要定义什么类,所以根本就无法事先做好支持你需要的类的运算
但是C++编译器提供了重载机制:就是让你自己写代码来支持你需要的类的运算 -- 这就是运算符重载


Q:如何进行运算符重载?


A: 首先承认运算符是一个函数, 然后用operator关键字加你需要重载的运算符来重载;
因为是一个函数,所以肯定会有参数列表和返回值,参数列表就是操作数的个数以及类型
返回值需要根据场景来设置。像“<<”、"==" 这些支持链式编程的一般返回自身的引用。
例如:重载加号:void operator+(Name &n1, Name &n2);// 假设不需要返回值


Q:自己重载了某个运算符之后该运算符是不是只支持重载后的数据类型?


A: 不是的, 重载了运算符之后c++编译器在“看到”这个运算符之后会首先进入这个函数,
如果参数类型、个数、顺序都一致才会使用这个你重载过的运算符,如果不是
那么C++编译器会调用默认的运算符。


Q:为什么说"[]"、"="、"<<"、">>" 运算符的重载难一些?


A:因为这些运算符都是可以做左值的, 也就是需要返回一个引用(一般是自身)
可能是因为这点比较难想到吧。


Q:重载运算符的一般步骤是什么?


A:假设以Name类为例:重载"+"号。
首先重载运算符先写出:operator
然后是重载加号:operator+
又因为是一个函数所以加上返回值和参数列表
返回值 operator+(参数列表)


Q:为什么不要重载 && 和 || ?


A: 1> && 和 || 是C++中非常特殊的操作符
2> && 和 ||有短路规则
3>操作符重载是靠函数重载来完成的
4>操作数作为函数参数传递
5>C++的函数参数都会被求值, 无法实现短路规则
例如: if (t1 && (t2+t3))
如果将"()"和"&&"都重载, 那么程序运行应该是:
t1.operator&&(t2.operator()(t3));
所以, 还是先执行t2+t3, 没办法实现短路
同理:if ((t2+t3) && t1) 也是如此。
注意:&&和||结合性是从左向右


Q:如何重载前置"++"、后置"++"等运算符?


A:以Name 类为例:前置++函数头为: Name & operator++(Name & n1);
按步骤写下来会发现 后置++函数头:Name & operator++(Name & n1);
没错, 按照顺序写下来两种函数的函数头是一样的;那么你使用的时候C++编译器如何判断
是前置++还是后置++呢? 这样是判断不出来的。为了解决这个问题,后置++的函数头的
参数列表里面会加一个占位参数 int , 因此后置++的函数头为:
Name & operator++(Name & n1, int)
还有后置++要注意是先用再加。

Q:关于运算符重载还有什么需要注意的吗?


A:"<<"、">>"运算符只能通过友元函数来重载, 因为按照写法,"<<"函数头为:
ostream & operator<<(ostream & out, Name & n1)
如果要写成成员函数形式, 那么这个函数应该写在ostream类里面,但是
It's impossible...

注意深拷贝和浅拷贝吧, 因为象"="这种运算符默认的都是浅拷贝。


下面举一个MyString类的例子:

MyString.h 文件

#pragma once# include <iostream>// 这一行不能省略using namespace std;class MyString{private:int m_len;char * m_p;friend ostream & operator<<(ostream & out, MyString & s1);// 重载 <<friend istream & operator>>(istream & out, MyString & s1);// 重载 >>public:MyString(int len = 0 );MyString(const char *p);~MyString();MyString(MyString &);MyString& operator =(const char * p);// 重载 = MyString& operator =(const MyString & s1);char & MyString::operator[](int index);// 重载 []bool operator == (const MyString & s2) const;// 重载 == // const:this 指向的内存空间不能被改变bool operator != (const MyString & s2) const;// 重载 != bool operator == (const char * p) const;bool operator != (const char * p) const;int operator > (const char * p) const;// 重载 >int operator < (const char * p) const;// 重载 <int operator > (const MyString& s1) const;int operator < (const MyString& s1) const;char * pStr();// 将指针露出来};

MyString.cpp 文件

# define _CRT_SECURE_NO_WARNINGS# include "MyString.h"# include <iostream>// 函数声明和函数定义的默认参数只能写一个, 且只能写在声明函数的地方MyString::MyString(int len)// 多加一个 int len 有什么好处?  可以重载  ">>" {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];memset(m_p, 0, m_len);// 将指针空间清空}}MyString::MyString(const char * p){if (NULL == p){m_len = 0;m_p = new char[m_len + 1];}else{m_len = strlen(p);m_p = new char[m_len + 1];strcpy(m_p, p);}}MyString::MyString(MyString & s1){// 记住copy构造函数里面不需要下面注释掉的东西/*if (m_p != NULL){delete[] m_p;m_len = 0;}*/m_len = s1.m_len;m_p = new char[m_len + 1];strcpy(m_p, s1.m_p);}MyString::~MyString(){if (NULL != m_p){delete[] m_p;m_p = NULL;}m_len = 0;}// 重载 "=" 赋值符号, 注意:赋值和初始化不一样MyString& MyString::operator =(const char * p){// 先释放旧内存if (NULL != m_p){delete[] m_p;m_len = 0;}// 根据p分配内存if (p == NULL){m_len = 0;m_p = new char[m_len + 1];strcpy(m_p, p);}else{m_len = strlen(p);m_p = new char[m_len + 1];strcpy(m_p, p);}return *this;}// 重载 " = " 符号, 参数不一样MyString& MyString::operator =(const MyString & s1){if (m_p == NULL){delete[] s1.m_p;m_len = 0;}m_len = s1.m_len;m_p = new char[m_len + 1];strcpy(m_p, s1.m_p);return *this;}// s4[0] = 'a';// 重载 "[]" char & MyString::operator[](int index){return this->m_p[index];}// 重载 "<<"ostream & operator<<(ostream & out, MyString & s1){out << s1.m_p;return out;}// 重载 ">>"istream & operator>>(istream & in, MyString & s1){in >> s1.m_p;return in;}// 重载 "=="bool MyString::operator == (const MyString & s2) const{if (m_len != s2.m_len){return false;}return strcmp(m_p, s2.m_p);}// 重载 "==", 参数列表不一样bool MyString::operator == (const char * p) const{if (NULL == p){if (0 == m_len){return true;}else{return false;}}else{if (m_len == strlen(p)){return !strcmp(m_p, p);}else{return false;}}}// 重载 "!="bool MyString::operator != (const char * p) const{return !(*this == p);}// 重载 "!=":参数列表不一样bool MyString::operator != (const MyString & s2) const{return !(*this == s2);}//重载 ">"int MyString::operator > (const char * p) const{return strcmp(this->m_p, p);}//重载 ">",参数列表不一样int MyString::operator > (const MyString& s1) const{return strcmp(this->m_p, s1.m_p);}// 重载 "<"int MyString::operator < (const char * p) const{return strcmp(p,this->m_p);}//重载 "<",参数列表不一样int MyString::operator < (const MyString& s1) const{return strcmp(s1.m_p, this->m_p);}// 将指针暴露出来char *  MyString::pStr(){return m_p;}


-----------------------------------分割线-------------------------------------------------

部分内容参考:http://blog.csdn.net/qingdujun/article/details/75578208

黑马程序员 c/c++ 视频