C++中的运算符重载详解

作者:看书就头疼 时间:2022-03-22 07:20:13 

1、引例

class Complex
{
private:
   double Real,Image;
public:
   Complex():Real(0),Image(0)  {}
   Complex(double r, double i) : Real(r),Image(i) {}
   ~Complex()  {}
};

int main()
{
   Complex c1(1.2,2.3);
   Complex c2(45,56);

Complex c3;
   c3 = c1.Add(c2);        

}

类非常简单,下面我们要讨论如何写一个Add函数,使得两个对象的属性相加,返回一个新的对象。

第一种:

Complex::Complex Add(const Complex &c)
{
   Complex co;
   co.Real = this->Real + c.Real;
   co.Image = this->Image + c.Image;
   return co;
}

问题1:如何写出最节省空间的Add函数?

第二种:

Complex::Complex Add(const Complex &c) const
{
   return Complex(c.Real + this->Real, c.Image + this.Image);
}

由于不需要改变调用对象的属性值,我们给this指针添加const 修饰。

分析过程如下:

C++中的运算符重载详解

问题2:为什么第一种方式不节省空间呢?

首先第一种的代码比较繁琐,并且在函数栈帧中又创建了一个对象(第3行)。并且函数类型是值传递类型(第6行),返回的是一个将亡值对象。那么整个Add函数空间会产生两个对象,造成空间的浪费。

第二中代码创建的是无名对象,减少了一个co对象的创建,并且将无名对象直接作为将亡值对象传递给main函数中的c3。

问题3:我们能否将Add函数改为引用类型,这样来减少将亡值对象的创建

Complex::Complex &Add(const Complex &c) const
{
   return Complex(c.Real + this->Real, c.Image + this.Image);
}

VS2019发现报错,不能返回引用对象:

C++中的运算符重载详解

我们进行分析:

C++中的运算符重载详解

问题4:我们能否将这个Add函数名改为 + 运算符?

//Complex::Complex Add(const Complex &c) const
Complex::Complex +(const Complex &c) const
{
Complex co;
co.Real = this->Real + c.Real;
co.Image = this->Image + c.Image;
return co;
}

int main()
{
   ...
   //c3 = c1.Add(c2);  
   c3 = c1.+(c2);    //将原先Add的地方改变为加号。
   ...
}

这样使用,编译器又会报错,操作符不可作为一个有效的函数名来被使用

问题5:如何使 +预算符 作为函数名使用?

这就引出了今天的关键,函数运算符重载

在C++中,为了使操作符作为一个有效的函数名,我们在操作符前面添加一个operator

Complex operator+(const Complex &c) const
{
    return Complex(c.Real + this->Real,c.Image + this->Image);
}

int main()
{
    Complex c1(1.2,2.3);
    Complex c2(10,10);
    Complex c3;
    c3 = c1 + c2;

//上面一行实际上是
    //c3 = c1.operator+ (c2);
    //c3 = operator+(&c1,c2);  //编译器还会经过一次编译
}

前面几篇博客已经分析了第15行的由来,是将c1的地址传递给this指针,将c2作为形参c的别名传递给函数。

2、类中自动建立的函数

在C++中,如果我们定义一个类,它会给我们自动创建六个缺省函数

构造函数析构函数拷贝构造函数赋值函数普通对象的&(取地址符)的重载常对象的&(取地址符)重载

代码示例如下:

class Object
{
public:
   Object()    {}//构造函数
   ~Object()   {}//析构函数
   Object(const Object &obj)   {}//拷贝构造函数
   Object &operator=() {const Object &obj} //赋值函数
   {
       return *this;
   }

Object *operator&()//普通对象的&(取地址符)的重载
   {
       return this;
   }

const Object *operator&() const//常对象的&(取地址符)重载
   {
       return this;
   }

};

然后,在C11标准下,又增添了两个缺省函数,这里不做深究:

移动构造函数移动赋值函数

3、重载赋值运算符解析

回到最初的例子:

class Object
{
   int value;
public:
   Object ()   {
       cout << "create:" << this << endl;
   }                  //普通构造函数
   Object (int x = 0):value(x) {cout << "create:" << this << endl;}  //缺省构造函数
   ~Object()                       //析构函数
   {
       cout << "~Objcet() " << this << endl;
   }
   Object(Object &obj):value(obj.value)            
   {
       cout << "Copy create:" << this << endl;
   }

int & Value()
   {
       return value;
   }

const int &Value() const
   {
       return value;
   }  

Object &operator=(const Object& obj)        //此处加引用
   {
       this->value = obj.value;
       return *this;       //this指针指向objb的地址。赋值函数结束后,objb不会被消亡,所以可以以引用返回
   }

void operator=(const Object& obj)       //赋值语句不可给this指针加const
   {
       this->value = obj.value;
   }

};

int main()
{
   Object objx(0);
   Object objy(0);
   objy = fun(objx);
   cout << objy.Value() << endl;
   return 0;
}

我们在34行添加一个等号运算符重载函数: void operator=(const Object& obj)

此处不可添加const修饰this指针,因为需要使用this指针作为左值被修改。

问题6:void operator=(const Object& obj) 只能用于 obja = objb,为什么不可以这样使用 obja = objb = objc;

我们逐一分析:

obja = objb = objc;

//当调用等号运算符函数的时候。
obja = objb.operator = (objc);
obja = operator = (&objb,objc);
//如果此处是调用的是 void operator=(const Object& obj) ;
//等号从右向左指向,我们不能把一个void 类型赋给一个obja对象类型。

我们将赋值运算符进行再次重载,丢弃 void 版本:

Object &operator=(const Object& obj)        //此处加引用
{
this->value = obj.value;
return *this;       //this指针指向objb的地址。赋值函数结束后,objb不会被消亡,所以可以以引用返回
}

这样就可以使用了。

我们接着上次的深入分析:

obja.operator=(operator=(&objb,objc));
operator=(&obja,operator=(&objb,objc));

问题7:如果遇到obja = obja这种情况,如何赋值呢?

回答:对this指针和形参引用进行判断。

Object &operator=(const Object &obj)
{
   if(this != &obj)
   {
       this->value = obj.value
   }
}

问题8:为什么函数是在栈区构建的,以引用返回打印的不是一个随机值?

运行程序,VS2012中,打印的是一个随机值。

VS2019打印的是一个正常值。

c));


> 问题7:如果遇到obja = obja这种情况,如何赋值呢?
>
> 回答:对this指针和形参引用进行判断。

```cpp
Object &operator=(const Object &obj)
{
   if(this != &obj)
   {
       this->value = obj.value
   }
}

问题8:为什么函数是在栈区构建的,以引用返回打印的不是一个随机值?

运行程序,VS2012中,打印的是一个随机值。

VS2019打印的是一个正常值。

在WIN10系统中,VS2019与操作系统完全结合,安全性更高。当程序多次运行的时候,它的逻辑地址都不一样,这样做的好处是:当病毒入侵时,由于程序的逻辑地址是变化的,病毒不好寻找入侵的入口。

来源:https://blog.csdn.net/weixin_46401837/article/details/122555466

标签:C++,运算符,重载
0
投稿

猜你喜欢

  • Maven是什么?Maven的概念+作用+仓库的介绍+常用命令的详解

    2022-01-21 01:12:24
  • Java语言描述二叉树的深度和宽度

    2021-12-02 10:59:34
  • 详解如何从原生Android 跳转到hbuilder项目

    2021-08-31 19:37:39
  • Lucene单值编码压缩算法源码解析

    2022-12-30 18:00:39
  • Spring Security 实现用户名密码登录流程源码详解

    2023-05-31 11:13:59
  • android 实现APP中改变头像图片的实例代码

    2021-11-02 20:39:58
  • log4j使用教程详解(怎么使用log4j2)

    2022-06-18 23:48:47
  • java开发实现订阅到货通知帮我们买到想买的东西

    2022-05-01 18:55:11
  • 简单谈谈java的异常处理(Try Catch Finally)

    2021-08-01 12:40:02
  • Android下拉刷新控件SwipeRefreshLayout源码解析

    2023-04-03 20:42:16
  • spring Bean创建的完整过程记录

    2022-04-14 03:57:51
  • C#模拟http 发送post或get请求的简单实例

    2023-11-05 13:32:18
  • C语言中fchdir()函数和rewinddir()函数的使用详解

    2022-02-08 20:08:08
  • Idea springboot如何实现批量启动微服务

    2022-09-08 09:04:07
  • C#更改tabControl选项卡颜色的方法

    2022-09-07 19:30:14
  • 完美解决springboot项目出现”java: 错误: 无效的源发行版:17“问题(图文详解)

    2023-08-29 02:15:32
  • Java Config下的Spring Test几种方式实例详解

    2022-12-17 00:31:36
  • 学习Winform分组类控件(Panel、groupBox、TabControl)

    2021-10-09 17:41:11
  • Android 4.4.2 横屏应用隐藏状态栏和底部虚拟键的方法

    2022-07-14 13:19:41
  • Android实现简单实用的垂直进度条

    2023-10-22 19:10:13
  • asp之家 软件编程 m.aspxhome.com