C++运算符重载

 

可重置和不可重载的运算符

  • 运算符重载的本质:是一种特殊的函数重载,函数名称由关键字"operator"和后面的运算符组成
     
  • 可重载的运算符
    wps493F.tmp.jpeg
  • 不可重载的运算符
    wps4950.tmp.jpeg
  • 必须重载为成员函数的运算符
    wps4951.tmp.jpeg
  • 不应该被重载的运算符
    逻辑与(&&)逻辑或(||)的运算,据说会丢失短路属性; ","和"&"不要重载,因为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流的非常量引用, 因为输入输出会向流内读写内容会改变流的状态,所以该参数不能是常量,通过观察这两个流的源码发现:

      20190725082050.png
      这个两个流都禁用了拷贝构造和赋值运算符,所以流对象无法复制和赋值,所以该参数只能使用引用

    • 输出输出运算符的返回值必须得是形参中流对象的引用
      这个是为了实现在一个流对象上进行多个对象的连续输入或者输出

    • 输入输出运算符一般要重载为类的非成员函数
      例如:标准库中的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类中将这两个运算符声明为友元:
      20190725105338.png

      并在源文件中实现:

      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 << "\n" << str << std::endl;
      

      测试结果:
      20190725143501.png

  • 重载"+"运算符
    加号运算符要重载两个版本,一个是将其重载为成员函数,另一个将其重载为非成员函数,如果只重载了成员函数的版本,那么
    调用时运算符左侧对象必须是类对象
    对于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;
    }
    

    测试结果:
    20190725163033.png

  • 重载赋值运算符
    赋值后可能直接会调用其它函数,所以重载后的赋值运算符要返回调用对象的引用,不然调用对象为返回的临时对象
    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];
     }
    

其它运算符就不在一一实现

posted @ 2019-07-25 16:46  CodeMaker+  阅读(546)  评论(0编辑  收藏  举报