可重置和不可重载的运算符
- 运算符重载的本质:是一种特殊的函数重载,函数名称由关键字"operator"和后面的运算符组成
- 可重载的运算符
- 不可重载的运算符
- 必须重载为成员函数的运算符
- 不应该被重载的运算符
逻辑与(&&)逻辑或(||)的运算,据说会丢失短路属性; ","和"&"不要重载,因为C++对这两个运算符的作用有明确 规定,一个是逗号运算符,一个是取地址
重载示例
下面写一个CMyString类,并重载各种运算符,这里先给出一些比较基础的成员函数:
class CMyString { TCHAR * m_pBuff; int m_nBuffSize; int m_nUsedSize; public: CMyString(); CMyString(TCHAR * pszStr); int GetCountOfCharInBuff()const; const TCHAR* GetBuffPointer() const; CMyString(const CMyString & SrcObj); ~CMyString(); private: bool CoverBuffContent(const TCHAR* pszString, int nSizeFactor); };
源文件中实现如下:
#include "stdafx.h" #include "MyString.h" #include <tchar.h> CMyString::CMyString(): m_pBuff(nullptr), m_nUsedSize(0), m_nBuffSize(0) { } CMyString::CMyString(TCHAR * pszStr): m_pBuff(nullptr), m_nUsedSize(0), m_nBuffSize(0) { if (pszStr == nullptr) { return; } CoverBuffContent(pszStr, 2); } CMyString::CMyString(const CMyString & SrcObj): m_pBuff(nullptr), m_nUsedSize(0), m_nBuffSize(0) { int nBytesOfStr = SrcObj.GetCountOfCharInBuff()*sizeof(TCHAR); if (0 == nBytesOfStr) { return; } CoverBuffContent(SrcObj.GetBuffPointer(), 2); } const TCHAR* CMyString::GetBuffPointer() const { return m_pBuff; } CMyString::~CMyString() { if (m_pBuff != nullptr) { delete[] m_pBuff; m_pBuff = nullptr; m_nBuffSize = 0; m_nUsedSize = 0; } } int CMyString::GetCountOfCharInBuff() const { return m_nUsedSize / sizeof(TCHAR); } //************************************************************************ // 函数名称: CMyString::CoverBuffContent // 访问权限: private // 函数功能: 将pszString复制到m_pBuff中,并重置m_nUsedSize和m_nBuffSize // 返回值: bool:成功返回true // 参数: TCHAR * pszString:待复制的字符串 // 参数: int nSizeFactor:m_pBuff的尺寸放大因子 // 注意: //************************************************************************ bool CMyString::CoverBuffContent(const TCHAR* pszString, int nSizeFactor) { int nCountOfBytes = _tcsclen(pszString) * sizeof(TCHAR); nSizeFactor = (nSizeFactor == 0) ? 1 : nSizeFactor; if (nCountOfBytes > m_nBuffSize) { /*如果pszString内容长度大于当前m_pBuff的长度则,则重新分配内存*/ if (m_pBuff != nullptr) { delete[] m_pBuff; } m_nBuffSize = 0; m_nUsedSize = 0; m_nBuffSize = nCountOfBytes * nSizeFactor; m_pBuff = new TCHAR[m_nBuffSize]; if (m_pBuff == nullptr) { m_nBuffSize = 0; return false; } } memset(m_pBuff, 0, m_nBuffSize); memcpy(m_pBuff, pszString, nCountOfBytes); m_nUsedSize = nCountOfBytes; return true; } //************************************************************************ // 函数名称: CMyString::AppendToBuff // 访问权限: private // 函数功能: 追加内容到m_pBuff尾部 // 返回值: bool;成功返回true // 参数: const TCHAR * pszString;要追加的内容 // 参数: int nSizeFactor:m_pBuff的尺寸放大因子 // 注意: //************************************************************************ bool CMyString::AppendToBuff(const TCHAR* pszString, int nSizeFactor) { int nCountOfBytes = _tcsclen(pszString) * sizeof(TCHAR); nSizeFactor = (nSizeFactor == 0) ? 1 : nSizeFactor; /*判断buff的剩余大小是否够用,不够用则暂存原有内容,然后重新分配内存, 在将原有内容复制过来,在将pszString内容追加到尾部 */ if (nCountOfBytes > m_nBuffSize - m_nUsedSize) { int nNewBuffSize = nCountOfBytes + m_nUsedSize; nNewBuffSize *= nSizeFactor; TCHAR* pNewBuff = new TCHAR[nNewBuffSize]; if (pNewBuff == nullptr) { return false; } memset(pNewBuff, 0, nNewBuffSize); memcpy(pNewBuff, m_pBuff, m_nUsedSize); m_nBuffSize = nNewBuffSize; delete[] m_pBuff; m_pBuff = pNewBuff; } memcpy(m_pBuff+m_nUsedSize/sizeof(TCHAR), pszString, nCountOfBytes); m_nUsedSize += nCountOfBytes; return true; }
-
重载输入输出运算符
-
输入运算符的第一个参数必须是istream流的非常量引用,输出运算符的第一个参数必须是ostream流的非常量引用, 因为输入输出会向流内读写内容会改变流的状态,所以该参数不能是常量,通过观察这两个流的源码发现:
这个两个流都禁用了拷贝构造和赋值运算符,所以流对象无法复制和赋值,所以该参数只能使用引用 -
输出输出运算符的返回值必须得是形参中流对象的引用
这个是为了实现在一个流对象上进行多个对象的连续输入或者输出 -
输入输出运算符一般要重载为类的非成员函数
例如:标准库中的string类的输入输出运算符就被重载为string的非成员函数,所以一般输出的时候都是这样写:std::string str = "xxxx"; std::cout << str;
如果将输入输出重载为string的成员函数那么代码得这么写:
std::string str = "xxxx"; string << std::cout;
是不是感觉到有点别扭
-
输入输出运算符通常要操作类的非公有数据成员,并且输入输出运算符又不能是类的成员函数,所以得是类的友元函数
为了同时支持宽字符和窄字符,在头文件中加入下列宏定义:
#if defined UNICODE #define IN_STREAM std::wistream #define OUT_STREAM std::wostream #else #define IN_STREAM std::istream #define OUT_STREAM std::ostream #endif
将输入输出运算符声明为全局函数:
IN_STREAM & operator >> (IN_STREAM & in, CMyString & obj); OUT_STREAM & operator << (OUT_STREAM & out, CMyString & obj);
并在CMyString类中将这两个运算符声明为友元:
并在源文件中实现:
IN_STREAM& operator >> (IN_STREAM & in, CMyString & obj) { TCHAR chBuff[4096] = { 0 }; memset(chBuff, 0, sizeof(chBuff)); in.getline((TCHAR*)chBuff, 4096); int nInputCount = in.gcount(); if (nInputCount > 1) { obj.AppendToBuff(chBuff, 2); } return in; } OUT_STREAM & operator<<(OUT_STREAM & out, CMyString & obj) { out << obj.m_pBuff; return out; }
测试代码如下:CMyString str; CMyString str1; std::wcin >> str1 >> str; std::wcout << str1 << " " << str << std::endl;
测试结果:
-
-
重载"+"运算符
加号运算符要重载两个版本,一个是将其重载为成员函数,另一个将其重载为非成员函数,如果只重载了成员函数的版本,那么
调用时运算符左侧对象必须是类对象
对于CMyString类来说,这里重载+运算符实现两个字符串的拼接:CMyString operator+(const TCHAR* pszStr, const CMyString & obj) { CMyString tmp(const_cast<TCHAR*>(pszStr)); tmp.AppendToBuff(obj.GetBuffPointer(),2); return tmp; } CMyString CMyString::operator+(const CMyString & obj) { CMyString tmp = *this; tmp.AppendToBuff(obj.GetBuffPointer(), 2); return tmp; } CMyString CMyString::operator+(const TCHAR * pszStr) { CMyString tmp = *this; tmp.AppendToBuff(pszStr, 2); return tmp; }
测试结果:
-
重载赋值运算符
赋值后可能直接会调用其它函数,所以重载后的赋值运算符要返回调用对象的引用,不然调用对象为返回的临时对象
CMyString类的赋值运算符实现:CMyString& CMyString::operator=(const CMyString & obj) { CoverBuffContent(obj.GetBuffPointer(), 2); return *this; }
-
重载下标运算符
下标运算符必须得重载为成员函数,并且需要重载两个版本,一个为常量成员函数返回常量引用,供常量类对象调用, 另一个为普通成员函数,返回引用;TCHAR& CMyString::operator[](int nIndex) { return m_pBuff[nIndex]; } const TCHAR& CMyString::operator[](int nIndex) const { return m_pBuff[nIndex]; }
其它运算符就不在一一实现