一文详解C++运算符重载【自己动手封装一个xxx.h库】

简介: 一文详解C++运算符重载【自己动手封装一个xxx.h库】

前言


默认的运算符只能进行简单的运算操作;操作符重载是为了应对复杂数据类型的运算;也是为了后期的泛型编程进行前置准备。操作符重载表面上看来是对复杂类型进行操作,实际上是疏通了复杂类型之间计算的方式,将他内部各个击破,并把最终的答案呈现到我们面前;不能用友元函数重载的操作符有:= () [] ->、流运算符只能由友元函数进行重载


一、对运算符重载的初步认知


早期我们只知道1+1=2,“Hell”+“o World”=“Hello World”,那么自己实现一个类如果创建两个对象能不能进行相加呢?没有实现运算符重载的时候答案是不能,实现了运算符重载就可以轻松达到我们的目的。


运算符重载一般有两种方式,运算函数为成员函数或者友元函数【友元函数本专栏前面提到过】
运算符函数外表长这样
  该对象类型 operator-(该对象引用类型 b);
  上面那个是减运算符的重载。


1.成员函数运算符重载


顾名思义就是将operator函数写进类的成员函数,前面提到过友元函数不一定安全,
所以能够使用成员函数进行运算符重载就不使用友元函数。


test operator-(test& b){
  test c(this->a - b.a, this->b - b.b);
  return c;
}


2.友元函数运算符重载


流运算符只能使用友元函数进行重载。


//声明
friend test operator+(test& a, test& b);
//实现
test operator+(test& a, test& b) {
  test c(a.a + b.a, a.b + b.b);
  return c;
}


二、二元运算符重载


通俗的来讲二元运算符重载是为了解决两个数据类型复杂的变量之间的运算操作;
操作符关键字使用语法:
       类型  operater(要重载的运算符)(参数列表){实现方法};
       可以分为:1.全局函数操作符重载    2.成员函数操作符重载;
具体实现方法如下:
(友元函数可以对类的成员属性直接操作,有时结合使用更方便)

代码实现


代码如下:


#include<iostream>
using namespace std;
class test {
private:
  int a;
  int b;
public:
  test(int a = 0, int b = 0) {
    this->a = a;
    this->b = b;
  }
  int getA() {
    return a;
  }
  int getB() {
    return b;
  }
  friend test operator+(test& a, test& b);
  test operator-(test& b);
  void print() {
    cout << "a:" << a << "b;" << b << endl;
  }
  ~test() {
    cout << "这里是析构函数,over" << endl;
  }
};
//友元函数二元操作符重载;
test operator+(test& a, test& b) {
  test c(a.a + b.a, a.b + b.b);
  return c;
}
//非友元函数非成员函数操作符重载;
test operator*(test& a, test& b) {
  test c(a.getA() * b.getA(), a.getB() * b.getB());
  return c;
}
//成员函数操作符重载;
test test::operator-(test& b) {
  test c(this->a - b.a, this->b - b.b);
  return c;
}
int main_caozuofu() {
  test a(1, 2), b(3, 4);
  cout << "a.a:" << a.getA() << " a.b" << a.getB() << " b.a" << b.getA() << " b.b" << b.getB() << endl;
  test c = a + b;//重载加操作符;(友元函数)
  c.print();
  test d = a * b;//重载加操作符;(全局非友元,非成员)
  d.print();
  test e = a - b;//重载减法操作符;(成员函数)
  e.print();
  return 0;
}


三、一元运算符重载


一元运算符重载包括:前置加加减减,后置加加减减
• 1


前置加加前置减减:


1.友元函数
2.成员函数
直接对传进来的对象操作,操作后直接返回(如果用引用将对象传递进来可以不用返回
,因为引用已经对原来的对象进行了操作)操作完后直接使用结果


后置加加减减:(比前置加加减减多一部操作)


  1.友元函数
  2.成员函数
对传进来的对象AAAA进行备份,因为本次操作还使用对象改变前的变量,当操作完后将备份
的对象返回出去;其实此时的对象AAAA已经改变了,但是只要我们操作的好 表面看来就符
合了后置加加的语法;


⚠️注意:返回类型不是判断函数重载的标准,而一元运算符重载常常会有很多函数名相同函数参数相同的重载函数


此时可以用占位符化解它们之间的矛盾;使他们构成重载;


代码实现


代码如下:


#include<iostream>
using namespace std;
class test_YY {
private:
  int a;
  int b;
public:
  test_YY(int a=0,int b=0){//对象的初始化
    this->a = a;
    this->b = b;
  }
  friend test_YY& operator++(test_YY& b);
  friend test_YY& operator--(test_YY& b, int);//由于与前置--运算符不构成重载所以要用占位符
  test_YY& operator--();
  test_YY& operator++(int);
  void print() {
    cout << "a:" << a << " b:" << b << endl;
  }
};
test_YY& operator++(test_YY& b) {//友元函数进行前置加加的运算符符重载;
  b.a++;
  b.b++;
  return b;
}
test_YY& operator--(test_YY& b,int) {//友元函数进行后置减减运算符重载;
  test_YY t = b;
  b.a--;
  b.b--;
  return t;
}
test_YY& test_YY::operator++(int) {//成员函数进行后置加加运算;int是占位符
  test_YY t = *this;
  this->a++;
  this->b++;
  return t;
}
test_YY& test_YY::operator--() {//成员函数进行前置减减运算符的重载
  this->a--;
  this->b--;
  return *this;
}
int main() {
  test_YY a1(1, 2), a2(3, 4);
//  --a1;//如果没有定义后置++函数这里就不可以写成a1--;如果写成就找不到相应的函数
//原来a1的成员变量是1,2经过前置减减后变为0,1;
  test_YY t = a1++;
  t.print();
  a1.print();
//  ++a2;//前置++前a2的成员变量是3,4进行前置++后成员变量变成4,5
  test_YY t1 = a2--;
  t1.print();
  a2.print();
  return 0;
}


四、流运算符与友元函数的密不可分

a8eae2ad5a694144b57468dabe6e8b08.png


代码实现


代码如下:


#include<iostream>
using namespace std;
class test3 {
private:
  int a;
  int b;
public:
  test3 (int a = 0, int b = 0) {
    this->a = a;
    this->b = b;
  }
  void print() {
    cout << "a:" << a << "b:" << b << endl;
  }
  friend ostream& operator<<(ostream & out,test3& a);
  friend ostream& operator>>(ostream& in, test3& a);
  friend test3& operator+(test3& a, int b);
};
ostream& operator<<(ostream& out,test3& a) {//友元函数对左移操作符重载;
  out << a.a << " ";//如果用成员函数对流操作符进行重载那么操作符的左边就必须变成对象
  out << a.b <<"  哈哈,我在这里"<< endl;//因为默认的指针传进成员函数时总是在参数列表的
  return out;//第一位;
}
test3& operator+(test3& a, int b) {
  a.a = a.a + b;
  a.b = a.b + b;
  return a;
}
/*ostream& operator>>(ostream& in, test3& a) {//左移操作符重载,失败;
  cin >> a.a;
  cin >> a.b;
  return in;
}*/
int main_FriendAndWeiyi() {
  test3 a1(200, 700),a3;
  test3 a2(300, 800),a4;
  a4 = a4 + 3;
//  a4 = 3 + a4;  //····错误写法  运算符重载操作有一定的顺序性'+'号前面的是第一个参数
  a4.print();//······这也是流操作符为什么只能用友元函数进行重载的原因;
  cout << a4 ;
  cout << a1<<a2;
//  cin >> a3 >> a4;
  return 0;
}


五、赋值运算符与成员函数的密不可分

5ae050a51e134f5595f2589dad9e88ab.png


代码实现


代码如下:


#include<iostream>
#include<cstring>
using namespace std;
class qwe123 {
private:
  int lenth;
  char* Fhao;
public:
  qwe123() {
    cout << "我是构造函数,但我不会初始化" << endl;
  }
  qwe123(char a[]) {//赋值操作重载实际上是深拷贝
    this->lenth = strlen(a)+1;
  //  this->Fhao = a;//错误写法在此没有给this->指向的空间开辟内存不能在下面delete;
    this->Fhao = new char[this->lenth];//在堆区给this-》申请一块空间
    strcpy_s(Fhao, lenth, a);//将a数组中的东西拷贝到Fhao数组内
  }
  void print() {
    cout << this->Fhao << endl;
  }
  qwe123& operator=(qwe123 &a);
  ~qwe123() {//析构函数无论对象有无初始化,无论对象怎样初始化,创建了对象就一定会析构
    //所以析构函数判断如何析构对象的标准应是每一个对象的特征;
    if (this->Fhao != NULL) {//有初始化的对象析构方式;因为创建对象时手动申请了内存
      delete[] this->Fhao;//所以就在析构时手动关闭;
      this->lenth = 0;
      cout << "这里是析构函数,over" << endl;
    }
    else {//没有初始化对象的析构方式
      cout << "这里是没有被初始化的对象被析构了" << endl;
    }
  }
};
qwe123& qwe123::operator=(qwe123 &a) {//赋值操作符的重载与深拷贝原理相同
  if (this->Fhao != NULL) {
    delete [] Fhao;//delete之前要有空间,如果是系统自己分配的空间就不能用delete
    this->lenth = 0;
  }
  this->lenth = strlen(a.Fhao) + 1;
  this->Fhao = new char[this->lenth];
  strcpy_s(Fhao,this->lenth,a.Fhao);
  return *this;
}
int main() {
  char pp[] = "I am big man";
  char dd[] = "I am very kkl";
  qwe123 a1(pp),a2(dd),a3;
  a1.print();
  a1 = a2;
  a2.print();
  return 0;
}


六、索引运算符与等号不等号运算符

135fe2ba611e4d3eb00e71942f214e3a.png


代码实现


代码如下:


//流运算符,赋值运算符,下标运算符,等号不等号运算符的重载
#include<iostream>
using namespace std;
class MyClass
{
public:
  MyClass(int a,int b);
  MyClass(char a[]);
  ~MyClass();
  friend ostream& operator<<(ostream &out, MyClass& a);
  bool operator==(MyClass& a);
  bool operator!=(MyClass& a);
  char& operator[](int i);
  MyClass& operator=(MyClass& a);
  void print() {
    cout << (*this)[4] << endl;//运行成功实例,这里的(*this)等效于一个对象;
    cout << "哟西 " << this->p<<endl ;
  }
  void printp(MyClass& a) {
    int i = 0;
    i = strlen(a.p) + 1;
    for (int j = 0;j < i;j++) {
      cout << a[j] << " ";
    }
  }
private:
  int a;
  int b;
  int len;
  char* p;
};
MyClass::MyClass(char a[]) {
  this->len = strlen(a)+1;
  this->p = new char[this->len];
  strcpy_s(this->p, this->len, a);
}
MyClass::MyClass(int a=0,int b=0)
{
  this->a = a;
  this->b = b;
}
MyClass::~MyClass()
{
  if (this->p != NULL) {
    delete[] p;
  }
  cout << "这里是析构函数,over,over" << endl;
}
ostream& operator<<(ostream &out,MyClass &a) {//重载了输出流操作符
  out << a.a << "" << a.b << endl;
  return out;
}
MyClass& MyClass::operator=(MyClass &a) {
  if (this->p != NULL) {
    delete[] p;
    this->len = 0;
  }
  this->len = strlen(a.p) + 1;
  this->p = new char[this->len];
  strcpy_s(this->p, this->len, a.p);
  return *this;
}
//等号与不等号均只拿类里面一个成员变量(len)展开来说;
bool MyClass::operator==(MyClass& a) {//等号操作符重载;
  if (this->len == a.len) {
    return true;
  }
  else return false;
}
bool MyClass::operator!=(MyClass& a) {//不等号操作符重载;
  if (this->len != a.len) {
    return true;
  }
  else return false;
}
char& MyClass::operator[](int i) {//调用方式:对象名[]//得到的东西可以经过操作设置,
  //在这里得到的是字符数组里面的某个字符
  //cout << "this ok"<<endl;这句话用于测试该函数有没有被调用;
  return (this->p)[i];//this->p代表this对象里面的一个成员变量,在这里是一个字符数组
  //并且在这里return 时调用了默认的[]操作函数
}
int main_HH() {
  char aa[] = "I am big shuai" ;
  char bb[] = "I am zhu zhi chong";
  char dd[] = "Shuai bi";
  MyClass a1(1, 2), a2(3, 4);
  MyClass b1(aa), b2(bb),b3(dd);
  cout << a1 << a2;
  b1 = b2=b3;//经过这里赋值函数之后,b1,b2,b3的len均相等;
//  b1.print();
//  b1.printp(b1);
  cout << b1[4] << endl;//成功将对象里面的字符拿出来,并调用了重载的[]函数
  if (b1 == b2) {//b1b2相等调用了这里
    cout << "等号函数,重载成功over" << endl;
  }
   if (b1 != a1) {//b1,.a1的len不相等所以这里也进行了输出
    cout << "这里是不等号,over" << endl;
  }
  return 0;
}


七、小括号与逻辑运算符


4606b7cfa01047ebae1b830a8268565c.png

代码实现


代码如下:


#include<iostream>
using namespace std;
class XLJ {
private:
  int a;
  int b;
public:
  XLJ(int a=0, int b=0) {
    this->a = a;
    this->b = b;
    cout << "这里是,构造" << endl;
  }
  void print() {
    cout << "a:" << a << "b:" << b<<endl;
  }
  int operator()(int a, int b);
  bool operator&&(XLJ& a) {//两个对象重的a都为0
    if (this->a == 0 && a.a == 0) {
      return true;
    }
    else {
      return false;
    }
  }
  bool operator||(XLJ& a) {//逻辑或只要两个对象里面有一个a是0就为真;
    if (this->a == 0 || a.a == 0) {
      return true;
    }
    else return false;
  }
};
int XLJ::operator()(int a=0, int b=0) {
  cout << "这里调用了,小括号运算符" << endl;
  return a + b;
}
int main() {
  XLJ a1(1, 2),a2,a3;//构造时调用的是构造函数;
  a1.print();//构造出来对象后再用()就调用括号运算符
  cout << a2(3,4) << endl;
  cout << a1(1, 2) << endl;
  if (a1 && a2) {
    cout << "this cure &&" << endl;
  }
  if (a1 || a2) {
    cout << "this || 运算" << endl;
  }
  if (a3 && a2) {
    cout << "this && victor" << endl;
  }
  return 0;
}


八、不等号运算符重载


可以根据需求对大于小于运算符进行重载;
这样可以很直观的对比出来两个对象的某一特征,找出哪个更胜一筹;
成员函数语法 bool operator(类名 &对象名){函数体进行对象的某一特征进行比较};
友元函数语法 bool operator(类名 &对象名,类名 &对象名){函数体同上};


代码实现


代码如下:


//大于小于运算符重载
//判断条件运算符返回值一般就0,1两种;
//并且符合返回1,也就是真,因为调用的场景一般就是if语句:
//if语句中只有判断条件为真才会执行代码块里面的语句;
//如果if语句为真那么下面附属的else 语句将不再执行;
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyClass1
{
public:
  MyClass1(char* b);
  ~MyClass1();
  bool operator<(MyClass1& a);
  bool operator>(MyClass1& a);
  bool operator<=(MyClass1& a);
  bool operator>=(MyClass1& a);
private:
  int a;
  char* m_p;
};
MyClass1::MyClass1(char *b)
{
  a = strlen(b) + 1;
  m_p = new char[a];
  strcpy(m_p, b);
}
MyClass1::~MyClass1()
{
  if (this->m_p != NULL) {
    delete[] m_p;
    a = 0;
    cout << "这里是析构函数,over" << endl;
  }
}
bool MyClass1::operator<(MyClass1& a) {//bool类型非零即1,strcmp()函数有三种返回值正,零,负数;
  //也就是说,如果直接返回strcmp得到的结果肯定不正确:
  //return strcmp(this->m_p, a.m_p);//(错误写法);
  int k;
  k = strcmp(this->m_p, a.m_p);
  if (k > 0) {
    return false;
  }
  else if (k < 0) {
    return true;
  }
}
bool MyClass1::operator<=(MyClass1& a) {
  int k = 0;
  k = strcmp(this->m_p, a.m_p);
  if (k <= 0) {
    return true;
  }
  else {
    return false;
  }
}
bool MyClass1::operator>(MyClass1& a) {
  return !(*this < a);//用<号,对>运算符重载进行简化;如果*this<a那么两者就不满足>直接返回与
}//小于操作符的返回值也就是   !(*this<a);
bool MyClass1::operator>=(MyClass1& a) {
  return !(*this <= a);
}
int main_DX() {
  char aa[] = "aaaaa";
  char dd[] = "bb2222";
  MyClass1 a1(aa), a2(dd);
  if (a1 > a2) {
    cout << "这里是大于号" << endl;
  }
  if (a1 < a2) {
    cout << "这里是小于号" << endl;
  }
  if (a1 <= a2) {
    cout << "这里是小于等于号" << endl;
  }
   if ( a2>=a1 ) {
    cout << "这里是大于等于号" << endl;
  }
  return 0;
}


九、综合小练习与类的分文件管理


对含有字符串的对象进行赋值,下标不等号等号以及输入输出流运算符进行重载;
• 1


⚠️类定义的头文件 mystring.h


只有类的属性与方法的声明


#pragma once
#include<iostream>
using namespace std;
class mystring
{
private:
  int lenth;
  char* p;
public:
      mystring();
      mystring(char *q);//构造
    mystring(mystring &q);//拷贝构造
      ~mystring();//析构函数
    mystring& operator=(mystring& q);//赋值操作符的重载;
    char& operator[](int i);//下标运算符重载
    bool operator==(mystring &q);//等号运算符重载
    bool operator!=(mystring &q);//不等号运算符重载
    bool operator<(mystring& q);//小于运算符重载
    bool operator <= (mystring & q);//小于等于
    bool operator>(mystring& q);//大于
    bool operator>=(mystring& q);//大于等于
    friend ostream& operator<<(ostream& out,mystring &q);//输出流
    friend istream& operator>>(istream& in, mystring& q);//输入流
};


⚠️类实现的文件 mystring.cpp


对前面类的声明中的方法进行了实现。


#define _CRT_SECURE_NO_WARNINGS
#include "mystring.h"
#include<iostream>
using namespace std;
ostream& operator<<(ostream& out, mystring& q) {//输出流
  out << q.p << endl;
  return out;
}
istream& operator>>(istream& in, mystring& q) {//输入流
  cin >> q.p;
  q.lenth = strlen(q.p) + 1;
  return in;
}
char& mystring::operator[](int i) {//下标运算符重载
  return this->p[i];
}
bool  mystring::operator==(mystring & q) {//等号运算符重载(字符串里面的内容是否相同为判断标准)
  if (this->lenth != q.lenth) {
    return false;
  }
  else {
    return !strcmp(this->p, q.p);//相同为为零,但相同时返回值应为1所以是!strcmp(this->p,q.p);
  }
}
bool  mystring::operator!=(mystring & q) {//不等号运算符重载----利用==重载函数;
  return !(*this == q);
}
bool  mystring::operator<(mystring & q) {//小于运算符重载以P(字符串里面的ASCLL值)为判断标准
  int k = 0;
  k = strcmp(this->p, q.p);
  if (k < 0) {
    return true;
  }
  else if (k > 0) {
    return false;
  }
}
bool  mystring::operator <= (mystring & q) {//小于等于
  int k = 0;
  k = strcmp(this->p, q.p);
  if (k <= 0) {
    return true;
  }
  else if (k > 0) {
    return false;
  }
}
bool  mystring::operator>(mystring & q) {//大于
  return !(*this<q);
}
bool  mystring::operator>=(mystring & q) {//大于等于
  return !(*this<=q);
}
mystring &mystring::operator=(mystring& q){//赋值运算符重载;
  if (this->p != NULL) {
    delete[] this->p;
    this->lenth = q.lenth;
    this->p = new char[this->lenth];
    strcpy(this->p, q.p);
  }
  else {
    this->lenth = 0;
    p = new char[this->lenth + 1];
    strcpy(p, "");
  }
  return *this;
}
mystring::mystring() {
}
mystring::mystring(char *q) {//传进来字符数组的构造函数
  if (q == NULL) {
    this->lenth = 0;
    p = new char[this->lenth + 1];
    strcpy(p, "");
  }
  else {
    this->lenth = strlen(q) + 1;
    p = new char[this->lenth];
    strcpy(p, q);
  }
}
mystring::mystring(mystring& q) {//拷贝构造函数
  this->lenth = strlen(q.p) + 1;
  p = new char[this->lenth];
  strcpy(p, q.p);
}
mystring::~mystring() {
  if (this->p != NULL) {
    delete[] this->p;
    this->lenth = 0;
  }
}


⚠️使用类的文件 testmain.cpp


测试分文件后实现类的效果【引入自己封装好的.h文件】


#include<iostream>
#include"mystring.h"
using namespace std;
int main() {//测试区;
  char aa[] = "I am a boy";
  char dd[] = "I am ZSC";
  mystring a1(aa),a2(dd),a3(aa);
  if (a1 > a2) {
    cout << "大于号" << endl;
  }
  if (a1 >= a2) {
    cout << "这里是>=号" << endl;
  }
  if (a2 < a1) {
    cout << "小于号" << endl;
  }
  if (a2 <= a1) {
    cout << "这里是<=号" << endl;
  }
  if (a1 != a2) {
    cout << "不等于号" << endl;
  }
  a1 = a2;//测试等号
  if (a1 == a2) {
    cout << "这里是==" << endl;
  }
  cout << a1;
  a1 = a2 = a3;//测试赋值符号的链式使用;//转换完应该输出I am a boy;
  cout << a1 << a2;//测试输出流的链式使用
  cin >> a1;//输入流测试
  cout << a1<<endl;
  return 0;
}


总结


这些运算符重载是博主在大一刚入门c++的时候记录的学习笔记,由于重载的运算符较多,所以大家可以对比学习,找出相同点与不同点。


目录
相关文章
|
22天前
|
算法 C++ 容器
C++标准库(速查)总结
C++标准库(速查)总结
53 6
|
1月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
89 10
|
22天前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
60 5
|
22天前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
44 1
|
2月前
|
编译器 API C语言
超级好用的C++实用库之跨平台实用方法
超级好用的C++实用库之跨平台实用方法
39 6
|
2月前
|
安全 C++
超级好用的C++实用库之环形内存池
超级好用的C++实用库之环形内存池
45 5
|
2月前
|
缓存 网络协议 Linux
超级好用的C++实用库之套接字
超级好用的C++实用库之套接字
31 1
|
2月前
|
存储 算法 安全
超级好用的C++实用库之sha256算法
超级好用的C++实用库之sha256算法
76 1
|
2月前
|
存储 算法 安全
超级好用的C++实用库之国密sm4算法
超级好用的C++实用库之国密sm4算法
50 0
|
2月前
|
网络协议 Linux C++
超级好用的C++实用库之网络
超级好用的C++实用库之网络
44 0