详细讲述C++各种运算符重载

1、等号运算符重载

我们需要注意的是,当我们不写等号运算符重载的时候,系统会默认生成浅赋值形式等号运算符重载,所谓的浅赋值就是将目标对象的成员属性直接赋值给当前对象(可以参考深拷贝和浅拷贝,在我的“C++——拷贝构造函数详解”这篇文章中有详细讲解)。

Object 
{
    
    
public:
//其他的构造函数、析构函数不是本篇文章重点,省略。
    void operator=(const Object& obj)
    {
    
    
         this->value=obj.value;
    }
    /*
    在主函数中使用obja=objb=objc,等价于下面的方法调用:
    obja=objb.operator=(objc);
    obja=operator=(&objb,objc);
    */
private:
    int value;
}

对于上述的赋值语句需要说明以下三点:
①这个赋值语句不能用const进行修饰,因为this->value在赋值的过程中有所改变
②这个方法不能实现连续赋值,因为返回值为void,可以将void改成Object,就能实现连续赋值
③由于对象在主函数中,通过引用传参进入当前方法,因此obj对象不受这个重载函数调用期的影响,所以可以用引用进行返回

不写等号运算符重载有两种情况:
①没有自己设计的类型和对象,都是基本类型,那么系统就会将两个对象的成员属性地址抓住,直接目标对象成员属性的数据导入当前对象成员属性中。
②如果成员属性中不止是基本类型,那么如果我们不写等号运算符重载,系统就会产生默认的浅赋值语句。

2、加号运算符重载

class Int
{
    
    
private:
   int value;
public:
    //前置加加
   Int& operator++()
   {
    
    
       this->value+=1;
       return *this;
   }
    //后置加加
   Int operator++(int)
   {
    
    
       Int tmp = *this;
       ++* this;//调用前置加加
       return tmp;
   }
    //对象+对象
   Int operator+(Int& b)const
   {
    
    
       Int c;
       c.value   = this->value + b.value;
       return c;
   }
    //对象+数值
   Int operator+(int val)const
   {
    
    
       Int c;
       c.value = this->value + val;
       return c;
   }
   
};
//数值+对象
Int operator+(const int val, const Int& a)
{
    
    
    return a + val;//调用Int operator+(int val)const 这个方法
}

3、取地址运算符重载

class Int
{
    
    
private:
   int value;
public:
   //省略构造函数、等号运算符重载,这里主要说明取地址运算符重载
   Int* operator&()
   {
    
    
        return this;
   }
   const Int* operator&()const//加了const的取地址运算符重载是一个常方法
   {
    
    
        return this;
   }
}
int main()
{
    
    
    Int a(10);
    Int b=10;//这句话会先调用构造函数构造一个value成员属性为10的对象,然后把这个对象赋值给b
    const Int c=10;
    const Int *ip=&c;//因为C是常对象,所以就会调用上面的常方法
}

4、前置++,后置++运算符重载

4.1后置++的引用问题:

因为是后置++,通过下面的代码可知,我们需要创建一个对象用来保存原来的值,然后再将当前值+1.然而创建的这个对象的生存期和重写后置++方法的生存期一样,当函数调用结束,空间就会被释放。所以不可以返回引用。

 //后置加加
   Int operator++(int)
   {
    
    
       Int tmp = *this;
       ++* this;//调用前置加加
       return tmp;
   }

但是如果我们非要返回引用,加一个static可不可以呢?如下:

Int& operator++(int)
{
    
    
    static Int old=*this;
    ++* this;
    return old;
}

上述代码因为定义成静态,那么当我们执行一次后置++没有问题,但是因为是静态,只执行一次 static Int old=*this;,当我们再次执行后置++的时候,值并不变,还是之前的值,那么我们改成下面的:

Int& operator++(int)
{
    
    
    static Int old=*this;
    old=*this;
    ++* this;
    return old;
}

加上old=*this这句话之后,那么我们每执行一次后置加加方法,就会将静态变量old重新赋值给当前的对象,从而避免上述的“再次执行后置++的时候,值并不变,还是之前的值”的问题。
举例对修改之后的代码作以说明:
在这里插入图片描述
最后d输出12,因为有 old=*this;会更新old。

4.2相关问题分析

结合下面的运算符重载,分析Int a=0;a=a++ +1;这句代码。

在这里插入图片描述
在这里插入图片描述

5、重载类型强转运算符

(1)隐式的类型转化:
所谓的隐式类型转化通过下面的例子说明:
a=b,这句话其实是先用整型b的值100进行构造,将构造的这个对象给a赋值,主函数调用结束之后析构这个对象,析构a。
(不允许隐式转换的关键字:explicit)

需要强调的是:能够进行隐式类型转换的的构造函数必须是单个参数的
在这里插入图片描述

(2)强制类型转换:
把对象强转成某一个内置类型

//把对象强转成整型
operator int() const
{
    
    
    return value;
} 

强转返回的类型就是强转之后的类型,因此重载函数不需要设置返回类型
例如下面呢的:如果返回flot,就和返回类型冲突。
在这里插入图片描述

6、括号运算符重载

class Add
{
    
    
      mutable int value;//加了mutable这个关键字之后,可以让value这个变量在常方法中也能改变
 public:
       Add(int x=0):value(x){
    
    };
       int operator()(int a,int b)const//括号运算符重载
       {
    
     
            value=a+b;
            return value;
       } 
}
int main()
{
    
    
    int a=10,b=20,c=0;
    Add add;
    c=add(a,b);//调用括号运算符重载,我们也称之为仿函数
    //c=add.operator()(a,b);
    return 0;
}

7、输出运算符重载

下面的输出运算符重载是错误的。必须通过引用来调用out参数,因为系统的代码设置的是私有的输出流,所以必须是引用调用,如果不是引用调用,就会调用拷贝构造,而拷贝构造被设置为私有,无法调用。

class String
{
    
    
private:
      char * str;
public:
      //ostream& operator<<(const String* const this,ostream& out)
      ostream& operator<<(ostream out)const
      {
    
    
           if(str!=NULL)
           {
    
    
                out<<str;
           }
           return out;
      }
      //s1<cout
      //s1.operator<<(cout)
      //operator(&s1,cout)
}

正确的如下:
把str的内容写入搭配out中,并返回
在这里插入图片描述
但是上述的输出运算符重载在使用的时候是:s1<<cout,并不符合我们逻辑的输出运算符重载:
所以:
先调用ostream& operator<<(ostream& out,const String& s)
然后再对象内部调用输出运算符重载

class String
{
    
    
private:
      char * str;
public:
      //ostream& operator<<(const String* const this,ostream& out)
      ostream& operator<<(ostream& out)const
      {
    
    
           if(str!=NULL)
           {
    
    
                out<<str;
           }
           return out;
      }
      //s1<cout
      //s1.operator<<(cout)
      //operator(&s1,cout)
}
ostream& operator<<(ostream& out,const String& s)
{
    
    
    s<<out;
    //s.operator<<(out);
    //operator<<(&s,out);
    return out;
} 
int main()
{
    
    
     String s1("hello world");
     cout<<s1<<endl<<;
     return 0;
}

8、星号运算符重载

class Int
{
    
    
private:
     int value;
};
class Object
{
    
    
    Int* ip;//Int是一个类
public:
    Object(Int* s=NULL):ip(s){
    
    }
    ~Object()
    {
    
    
         if(ip!=NULL)
         {
    
    
              delete ip;
         }
         ip=NULL;
    }
    //✳运算符重载返回这个对象所指向的地址
    Int* operator*()
    {
    
    
        return ip;
    }
    const Int* operator*()const
    {
    
    
        return ip;
    }
    //✳运算符重载返回ip所指向的对象
    Int& operator*()
    {
    
    
        return *ip;
    }
    const Int& operator*()const
    {
    
    
        return *ip;
    }
};
int main()
{
    
    
     Object obj(new Int(10));
     (*obj)->value();
}

9、指向运算符重载

class Int
{
    
    
private:
     int value;
};
class Object
{
    
    
    Int* ip;//Int是一个类
public:
    Object(Int* s=NULL):ip(s){
    
    }
    ~Object()
    {
    
    
         if(ip!=NULL)
         {
    
    
              delete ip;
         }
         ip=NULL;
    }
    //返回所指向的这个对象的地址
    const Int* operator->()const
    {
    
    
        return ip;
    }
}
int main()
{
    
    
     Object obj(new Int(10));
     obj->value();
}

注:通过星号运算符重载和指向运算符重载综合运用来实现“对象的使用自由化”问题:
问题引入:

class Int
{
    
    
private:
     int value;
};
class Object
{
    
    
    Int* ip;//Int是一个类
public:
    Object(Int* s=NULL):ip(s){
    
    }
    ~Object()
    {
    
    
         if(ip!=NULL)
         {
    
    
              delete ip;
         }
         ip=NULL;
    }
    Int& operator*()//重载*,返回ip所指对象本身
    {
    
    
         return *ip;
    }
    const Int& operator*()const
    {
    
    
         return *ip;
    }
    Int* operator->()
    {
    
    
         return &**this;
    }
    const Int* operator->()const
    {
    
    
         return &**this;
         //return ip
    }
}

上面的的&**this表示什么呢?

对于上述代码我们结合下图从右往左进行分析:this就是指向obj这个对象,*this表示obj这个对象,然后**this就调用了✳运算符重载,表示ip所指向的这个对象本身,所以这个合起来表示ip所指向的这个对象的地址
在这里插入图片描述
所以进✳运算符和->运算符的重在之后,也能够得到Int对象,并且利用Obj这种形式实现了自由化,也就是说在析构的时候就会自动析构ip所指向堆区的空间,不用我们手动释放。

猜你喜欢

转载自blog.csdn.net/m0_54355780/article/details/122638113