C++基础学习笔记----第十三课(操作符重载-下)
本节主要讲使用成员函数重载操作符,包括[],=,(),->四种操作符的重载以及&&和||的问题。
类的成员函数进行操作符重载
基本概念
类的成员函数也可以进行操作符的重载。类的普通成员函数是被C++编译器默认的隐藏了一个默认的参数this指针,这里的这个this指针指向的是当前的对象。所以在使用类的成员函数在进行操作符重载的时候要比全局函数少一个参数,左操作数。不需要使用friend关键字。
全局函数重载操作符和成员函数重载操作符的使用场景
①当无法修改左操作数的类时,使用全局函数进行重载。
②=,[],(),->这四个操作符只能通过成员函数进行重载。
基本例程:
#include <iostream> using namespace std; struct A
{
private:
int a;
int b;
public:
A(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
} /*这里使用的成员函数进行操作符重载,因为成员函数有一个默认this指针,所以这里只要求一个参数*/
A operator+ (A& a2);
/*{
A ret;
ret.a = this->a + a2.a;
ret.b = this->b + a2.b;
return ret;
}*/ friend ostream& operator<< (ostream& out, const A& d);
}; A A::operator+ (A& a2)
{
A ret;
ret.a = this->a + a2.a;
ret.b = this->b + a2.b;
return ret;
} ostream& operator<< (ostream& out, const A& d)
{
out<<d.a<<"+"<<d.b<<"i"<<endl;
return out;
} int main()
{
A a1 = A(1,2);
A a2 = A(3,4); A a3 = a1 + a2; cout<<a1<<endl;
cout<<a2<<endl;
cout<<a3<<endl; return 0;
}
注:在上面的程序中,ostream& operator<< (ostream& out, const A& d)这个<<操作符函数并不能够进行重载,因为这个函数的左操作数ostream& out实际上是一个ostream类的对象,但是这个时候无法修改这个左操作数,如果要以成员函数的方式去重载这个操作符,但是不能够修改ostream这个类,所以无法重载。
通过上面使用成员函数进行了+这个操作符的重载,实际上主函数中的A a3 = a1 + a2;就是调用了operator+这个函数,实际的形式如下:
A a3 = a1.operator+(a2);
这里实际上就是调用A类对象的a1的operator+这个函数,参数是a2这个对象。来完成两个类的相加的操作符重载。
=,[],比较操作符的重载
[]数组操作符的重载
数组操作符的重载实际上是为了编程的方便,同时也是为了方便程序的查看和使用。
以第九课的博文点击打开链接中的数组类为例:
在A类中定义数组操作符([])的重载函数,具体代码如下:
int& A::operator[] (int i)
{
return ap[i];
}
通过上面的代码,在主函数中不需要再使用b.getdata(j)getdata函数不需要继续使用了,因为可以直接通过b[j]的数组操作符重载函数得到j这个位置所对应的数组成员。
在主函数中a[i]的实际上就是:
a.operator [](i)
通过上面的程序可以看出, 数组操作符的重载函数的返回值是int& 而不是int,如果这里的数组返回值是int,那么其实这里返回的是一个整数,无法作为左值使用。所以如果一个函数的返回值想要作为左值使用就必须返回引用。
=赋值操作符的重载
b = a;
这个时候程序是可以编译通过的,但是当调用拷贝函数的时候将会挂掉。原因如下: C++编译器为每一个类提供了默认的赋值操作符的重载,这个重载的机制就是当类的对象使用赋值操作符的时候只是进行简单的值复制。所以这个时候实际上a对象和b对象指向同一块内存空间,所以在调用析构函数的程序会释放相同的一块内存空间,所以程序将会挂掉。
在A类中定义赋值操作符(=)的重载函数,具体代码如下:
A& A::operator= (A& aj)
{
/*
这里先释放ap指针原来指向的空间,这里的ap指针属于这个类,也就是外界的对象都是通过这个指针
来申请空间的,实际上这个指针可以说是公用的。
*/
delete []ap; /*重新为数组分配空间,然后再进行赋值操作*/ alength = aj.alength;
ap = new int[alength]; for (int i = 0; i < alength; i++)
{
ap[i] = aj.ap[i];
} /*返回this指针所对应的内容,实际上就是自己这个对象*/
return *this;
}
注:这里的重载函数返回到是A这个类的引用,如果这里的函数返回值的类型是void,那么如果在主函数中调用a = b = c。根据C语言赋值规则,从右向左进行赋值,a = b = c的中的赋值符号经过重载后的原型如下:
c.operator= (b.operator= (a));
或者
c = b.operator= (a);
通过c = b.operator= (a);所以如果这个函数返回的类型是void,所以这里将会给void类型赋值给一个A类的对象c,所以程序将会崩溃。
比较操作符的重载
代码如下:
bool A::operator== (A& aa)
{
bool ret = true;
if (alength == aa.alength)
{
for (int i = 0; i < alength; i++)
{
if (ap[i] != aa.ap[i])
{
ret = false;
break;
}
}
}
else
{
ret = false;
}
return ret;
}
附上经过改进的数组类:
main.cpp
#include <stdio.h>
#include "a.h" int main()
{
A a = 5;
//A b = a; A b = 0; A c = 0;
/*A c = 5;*/ for (int i = 0; i < a.getlength(); i++)
{
/*
通过[]重载后这里a[i]返回的是ap指向的这块内存中第i个元素的引用
然后再使用for循环中的i进行初始化。
*/
a[i] = i;
printf ("%d\n",a[i]); /*数组操作符函数重载的原型*/
/*printf ("%d\n",a.operator [](i));*/
} c = b = a; /*经过操作符的重载之后,可以直接对比这两个数组的对象来比较这两个数组是否相等*/
if (a == b)
{
printf ("123\n");
}
else
{
printf ("CYX\n");
} /*c.operator= (b.operator= (a));
c = b.operator= (a);*/ /*A b = a;*/
for (int j = 0; j < b.getlength(); j++)
{
printf ("%d\n",b[j]);
} for (int k = 0; k < c.getlength();k++)
{
printf("%d\n",c[k]);
} return 0;
}
a.cpp
#include <stdio.h>
#include "a.h" A::A(int length)
{
if (length < 0)
{
length = 0;
}
alength = length;
ap = new int[alength];
} A::A(const A& aj)
{
alength = aj.alength;
ap = new int[alength]; for (int i = 0; i < alength; i++)
{
ap[i] = aj.ap[i];
}
} int A::getlength()
{
int ret = 0; ret = alength; return ret;
} /*
这里返回的是int类型的引用,因为如果返回的int类型,那么将是具体的数值,这个具体
数值将不能够作为左值使用的,如果要是引用就可以作为左值使用。
*/
int& A::operator[] (int i)
{
return ap[i];
} A& A::operator= (A& aj)
{
/*
这里先释放ap指针原来指向的空间,这里的ap指针属于这个类,也就是外界的对象都是通过这个指针
来申请空间的,实际上这个指针可以说是公用的。
*/
delete []ap; /*重新为数组分配空间,然后再进行赋值操作*/ alength = aj.alength;
ap = new int[alength]; for (int i = 0; i < alength; i++)
{
ap[i] = aj.ap[i];
} /*返回this指针所对应的内容,实际上就是自己这个对象*/
return *this;
} /*返回值只要求分辨这两个数组是否相等*/
bool A::operator== (A& aa)
{
bool ret = true;
if (alength == aa.alength)
{
for (int i = 0; i < alength; i++)
{
if (ap[i] != aa.ap[i])
{
ret = false;
break;
}
}
}
else
{
ret = false;
}
return ret;
} A::~A()
{
alength = -1; delete []ap;
}
a.h
#ifndef _A_H
#define _A_H struct A
{
private:
int alength;
int* ap;
public:
A(int length);
A(const A& aj);
int getlength();
int& operator[] (int i);
A& operator= (A& aa);
bool operator== (A& aa);
~A();
}; #endif
++,--操作符的重载
++操作符只有一个操作数,而且操作符有前缀(++a)和(a++)之分。如果要重载这个操作符,可以通过一个函数的占位参数来区分前缀和后缀。 后缀的操作符带有占位参数,前缀的操作符不具有占位参数。
根据C语言中的规则,a++的含义是先返回a变量的值,然后再对a变量进行自加操作。
例程如下:
#include <stdio.h> class A
{
private:
int a;
int b;
public:
A(int i)
{
a = i;
b = i;
}
void plus()
{
a++;
b++;
}
void print()
{
printf("a = %d, b = %d\n",a,b);
}
A& operator++(int);
A& operator++();
A& A::operator--(int);
A& A::operator--();
}; A A::operator++(int)
{
/*首先将当前的对象值返回,这是a++的规则*/
A ret = *this;
/*然后分别对a成员和b成员进行自加*/
a++;
b++; return ret;
} A& A::operator++()
{
++a;
++b; /*最后都要返回操作后的本身的值*/
return *this;
} A A::operator--(int)
{
A ret1 = *this;
--a;
--b; return ret1;
} A& A::operator--()
{
a++;
b++; return *this;
} int main()
{
A a = 1;
A b = 1; /*a.plus();
a.print();
b.plus();
b.print();*/ /*a++;
b++;
a.print();
b.print();*/ /*++a;
++b;
a.print();
b.print();*/ /*a--;
b--;
a.print();
b.print();*/ --a;
--b;
a.print();
b.print(); return 0;
}
注:重载++运算符的时候,前置比后置的++效率高很多。类的成员函数,隐含了一个this指针。操作符重载函数也不例外,也隐含了一个this指针。这个this指针是一个引用类型,所以,在返回的时候,也返回这个引用类型。而后置++操作,在函数中进行了一次值的拷贝,返回的不再是this指针指向的对象,而是一个副本。所以返回的就是一个类对象。
不要重载&& 和 ||运算符
1.从C++语法的角度来说,&&和||这两个运算符可以进行重载。
2.&&和||这两个运算符内置了短路规则(短路规则就是当某一个条件满足的时候就不再执行,在单个变量的时候不会发生太多问题,但是涉及到函数返回值的时候将会发生问题)。操作符重载是依靠函数重载来完成的。C++的函数参数都会被返回一个值,所以无法实现短路规则。
基本例程如下:
#include <stdio.h> class A
{
private:
int a;
public:
A(int i)
{
a = i;
}
void print()
{
printf("a = %d\n",a);
}
bool operator|| (A& aa)
{
printf("123\n"); return this->a && aa.a;
}
A& operator+ (A& aa)
{
A ret = 0;
ret.a = this->a + aa.a;
printf ("bugai\n");
return ret;
}
}; int main()
{
A a = 1;
A b = 1; if (a || (a + b))
{
printf ("CYX\n");
}
return 0;
}
程序打印结果如下:
当程序运行时首先执行if(a && (a + b)),如果a,b是两个真正的变量,那么当判定a为真的时候程序将会直接打印CYX,并不会再继续判定(a + b),但是将||运算符进行重载之后,if(a && (a + b))的实际格式如下:
if (a.operator|| (a.operator+ (b)))
所以这个判定是先从内层进行判定,然后根据+重载函数的返回值作为参数再调用||运算符重载函数,所以程序打印结果如上图。
C++基础学习笔记----第十三课(操作符重载-下)的更多相关文章
- Python基础学习笔记(十三)异常
参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-exceptions.html Python用异常对象(excep ...
- C++基础学习笔记----第四课(函数的重载、C和C++的相互调用)
本节主要讲了函数重载的主要概念以及使用方法,还有C和C++的相互调用的准则和具体的工程中的使用技巧. 函数重载 1.基本概念 函数重载就是用同一个函数名来定义不同的函数.使用不同的函数参数来搭配同一个 ...
- python基础学习笔记(十三)
re模块包含对 正则表达式.本章会对re模块主要特征和正则表达式进行介绍. 什么是正则表达式 正则表达式是可以匹配文本片段的模式.最简单的正则表达式就是普通字符串,可以匹配其自身.换包话说,正则表达式 ...
- C++基础学习笔记----第七课(面向对象的基本概念)
主要讲面向对象的基本概念和一些概念,以及实现简单的面向对象C++程序. 类和对象 基本概念 类和对象是面向对象中的两个基本概念,类是指一类事物,是一个抽象的概念.对象是指某一个类的实体,是一个具体存在 ...
- Java基础学习笔记二十三 Java核心语法之反射
类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任 ...
- Redis学习笔记(十三) 复制(下)
上一篇写了Redis复制功能的简单应用,下面我们看下Redis复制功能的实现过程.下面基本上是理论部分,枯燥乏味,但希望大家能看看,毕竟知识不都是感兴趣的.耐得住寂寞,经得起诱惑,方能守得住繁华 ~. ...
- Java基础学习笔记总结
Java基础学习笔记一 Java介绍 Java基础学习笔记二 Java基础语法之变量.数据类型 Java基础学习笔记三 Java基础语法之流程控制语句.循环 Java基础学习笔记四 Java基础语法之 ...
- VSTO 学习笔记(十三)谈谈VSTO项目的部署
原文:VSTO 学习笔记(十三)谈谈VSTO项目的部署 一般客户计算机专业水平不高,但是有一些Office水平相当了得,尤其对Excel的操作非常熟练.因此如果能将产品的一些功能集成在Office中, ...
- Mysql数据库基础学习笔记
Mysql数据库基础学习笔记 1.mysql查看当前登录的账户名以及数据库 一.单表查询 1.创建数据库yuzly,创建表fruits 创建表 ) ) ,) NOT NULL,PRIMARY KEY( ...
随机推荐
- (转)jQuery Validate 表单验证
在做网页表单时时常需要在客户端对表单填写的数据进行验证一番才能提交,我们可以通过自己编写JavasScript代码来验证,但是有时数据量过多时就会有些难度了.基于jQuery的jquery.valid ...
- Android Studio 实用快捷键
ctrl + alt + 方向键 跳转到上次或下次编辑位置 ctrl + alt +n 查找文件 shift + f6 重构之重命名 ctrl + f12 导航到类方法 ...
- jQuery旋转插件
jQuery旋转插件,支持Internet Explorer 6.0 + .Firefox 2.0.Safari 3.Opera 9.Google Chrome,高级浏览器下使用Transform,低 ...
- JavaWeb学习笔记之JSP(一)
1. JSP: 1.1. 为什么需要 JSP ? 如果使用Servlet程序来输出只有局部内容需要动态改变的网页,但是其中的静态网页内容也需要程序员使用Java语言来进行输出,这就造成了大量代码的冗余 ...
- String类扩展
String s1=new String("fsdfsd"); String s2=new String("fsdfsd"); String a1=" ...
- C#中的委托(Delegate)和事件(Event)
原文地址:C#中的委托(Delegate)和事件(Event) 作者:jiyuan51 把C#中的委托(Delegate)和事件(Event)放到现在讲是有目的的:给下次写的设计模式--观察者(Obs ...
- [转]C++智能指针的创建
zero 坐在餐桌前,机械的重复“夹菜 -> 咀嚼 -> 吞咽”的动作序列,脸上用无形的大字写着:我心不在焉.在他的对面坐着 Solmyr ,慢条斯理的吃着他那份午餐,维持着他一贯很有修养 ...
- JS encode decode
网上查到的全都是escape,和需要的编码不是一回事,好不容易找到的结果 保存下来以备以后使用 js对文字进行编码涉及3个函数:escape,encodeURI,encodeURIComponent, ...
- idHTTP最简洁的修改和取得Cookie例子
procedure TForm1.Button1Click(Sender: TObject); var HTTP: TidHTTP; html, s: string; i: integer; begi ...
- lua编码转换
lua编码转换, 这个要记录下:http://www.lpfrx.com/archives/4918/ ,总是觉得lua没python甘方便,应该说没这么顺手吧,可能先入为主吧,python库多, 编 ...