C++ 重载运算符(详)
C++ 重载运算符
运算符重载实质是函数的重载,所以我们先来了解一下重载函数。
一、重载函数
重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个函数名完成不同的功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。
1.1、例程
#include <iostream>
using namespace std;
class DemoClass{
public:
void printNumber(int num);
void printNumber(float num);
};
void DemoClass::printNumber(int num)
{
cout << "int: " << num << endl;
}
void DemoClass::printNumber(float num){
cout << "float: " << num << endl;
}
int main(void)
{
DemoClass demoClass;
demoClass.printNumber(1);
demoClass.printNumber(1.1f);
return 0;
}
输出:
int: 1
float: 1.1
1.2、备注
- 函数名必须一致
- 参数类型必须不同
- 如果参数类型相同,参数个数必须不同
二、重载运算符
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
又是那句话:运算符重载实质是函数的重载
2.1.1、 二元运算符重载
#include <iostream>
using namespace std;
class DemoClass
{
public:
/*构造函数*/
DemoClass(){}
DemoClass(int data);
/*重载 + 号*/
DemoClass operator +(const DemoClass &obj);
/*输出data*/
void printData();
private:
int data;
};
int main(void)
{
DemoClass demo1(1);
(demo1+demo1).printData();
return 0;
}
DemoClass::DemoClass(int data)
{
this->data = data;
}
DemoClass DemoClass::operator +(const DemoClass &obj)
{
return DemoClass(obj.data+data);
}
void DemoClass::printData()
{
cout << this->data << endl;
}
输出:
2
2.1.1、 一元运算符重载
此处一元运算符指的是 `++
--
?
!
&
-
(负号)等
2.1.1.1 ++
--
分为前置和后置方式,需要引入一个无用的参数作为区别。作为前置可以返回引用,作为后置只能返回临时对象(然后再自增或者自减)。
const X& operator ++ ();//前置
const X operator ++ (int);//后置
const X& operator -- ();
const X operator -- (int);
自加函数友元方式
由于后置返回的是一个临时对象,所以需要一个新建的对象存起来,就有必要定义一个拷贝构造函数。
其他一元运算符基本上没有什么特别的。大家自己体会,如果想不明白请留言。
2.2、备注
- 由于运算符重载本质是函数的重载,所以遵循函数重载的要求。
- 不得创造c++标准中没有的运算符。
- 不得改变优先级和结合性,以及操作数的个数
- 当运算符单独出现时依然是本来的用法,重载运算符只能用来进行类间的操作。
- 除了类属关系运算符
.
、成员指针运算符.*
、作用域运算符::
、sizeof
运算符和三目运算符?:
此外,C++中的所有运算符都可以重载。(比如:关系运算符<
,==
,<=
,>
,>=
,!=
,逻辑运算符,自增自减运算符,位运算运算符,赋值运算符+=
,-=
,<<=
,^=
等但不包括=
,内存运算符new
,delete
,new[]
,delete[]
还有一些特殊的运算符=
,[]
,()
,->
)
2.3 特殊运算符重载
2.3.1 =
号运算符
每当一个类中含有指针成员的时候要特别注意。如果使用已经创建的对象进行初始化时会自动调用拷贝构造函数,如果两个对象都已经初始化好了,要用=
符号进行对象间的拷贝就会出问题。什么问题呢?
上代码:
#include <cstring>
#include <iostream>
using namespace std;
class MyString
{
public:
MyString(const char* str){count = strlen(str);pstr = new char[count+1];strcpy(pstr, str);}
~MyString(){delete []pstr;}
void display(void){cout<<pstr<<endl;}
private:
char *pstr;
int count;
};
int main(void)
{
MyString s1("Hello");s1.display();
MyString s2("World");s2.display();
s2 = s1;//glibc detected : double free or corruption
s2.display();
return 0;
}
/** valgrind内存泄露工具检测结果
Invalid free() / delete / delete[] / realloc()
LEAK SUMMARY:
definitely lost: 6 bytes in 1 blocks
**/
解释:使用valgrind内存泄露检测工具检测以后发现,出现了二次释放的问题,这个问题出在代码20行,在这个位置s1指针的值被赋给了s2,但是s2原来申请的堆空间没有被释放,s1和s2指向同一块堆空间,程序结束的时候s1与s2的析构函数被调用,于是就double free了。
如何解决这个问题?答案就是重载=
,当对象使用=
赋值处理指针的时候释放原有的堆空间,新申请一段等长的堆空间进行拷贝,而不是两者指向同一个空间,不同的堆空间仅仅是内容一样就不会double free。
//核心代码 重载 '=' 号运算符
MyString& operator=(MyString& ref)
{
if (this != &ref)
{
delete []pstr;//删除自己申请的空间
count = ref.count;
pstr = new char[count+1];//申请等长的新空间
strcpy(pstr, ref.pstr);//拷贝内存
}
return (*this);
}
2.3.2 []
下标运算符
[]
符号原规则是用来当作数组运算符,所以只允许有一个参数,不得没有参数,或者有多个参数,允许用户自定义功能。
#include <iostream>
using namespace std;
class MyArrary
{
public:
MyArrary(int a1,int a2,int a3, int a4){arr[0]=a1;arr[1]=a2;arr[2]=a3;arr[3]=a4;}
//operator overload : []
int& operator[](int num)//返回引用才能进行赋值操作
{
if ((num < 0) || (num > 4))
{
cout <<"error:";
num = 0;
}
return arr[num];
}
private:
int arr[4];
};
int main(void)
{
MyArrary ma(1,2,3,4);
cout << ma[0]<<endl; //取值
ma[0] = 5; //赋值
cout << ma[5]<<endl;
return 0;
}
输出:
TODO
[]
可以做左值又可以做右值,所以返回为引用,便于赋值。
2.3.2 ()
括号运算符
括号通常用来访问多维数组,也可以自定义作用。同()
一样可以做左值又可以做右值,所以返回引用。没有参数个数限制,允许没有参数。
#include <iostream>
using namespace std;
class DArrary
{
public:
DArrary(int num){int i = 0; for (i = 0; i< 9;i++) arr[i/3][i%3] = num++;}
void display(void){int i = 0;for (i = 0; i< 9;i++) cout<<arr[i/3][i%3]<<" ";cout<<endl;}
//operator overload : () Multiple Parameters
int& operator()(int a, int b)
{
return arr[a][b];
}
//operator overload : () Singal Parameter
int& operator()(int a)
{
return arr[a/3][a%3];
}
//operator overload : () None Parameter
int& operator()(void)
{
return arr[0][0];
}
private:
int arr[3][3];
};
int main(void)
{
DArrary arr(1);arr.display();
cout << arr(0,0) << endl; //取值
cout << arr(2,2) << endl; //取值
arr(0,0) = 11; //赋值
arr(2,2) = 22; //赋值
cout << arr(0,0) << endl;
cout << arr(2,2) << endl;
cout << arr(7) << endl; //取值
arr(7) = 33; //赋值
cout << arr(7) << endl;
cout << arr() << endl;
arr() = 111;
arr.display();
return 0;
}
注意!特殊符号只能重载为非静态的成员函数,不能是友元函数和普通函数
三、重载运算符之友元函数
友元函数形式的重载运算符由于友元的特性,没有this指针,所以只要多一个参数就好了。
一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
例程
friend A operator+(A&,A&);//友元函数
自加友元
返回
friend const CounterB operator++(CounterB& ref, int dump);//后置自加
重载运算符请尽可能的同原来的含义相同或相近,符合软件工程可理解性、可维护性的要求
参考材料:
百度百科之重载函数
百度百科之运算符重载
c++运算符重载总结_Wuyuan’s Blog
C++运算符重载——重载特殊运算符
C++运算符重载——重载一元运算符
C++ 重载运算符(详)的更多相关文章
- c++的重载运算符
c++中允许重载运算符: 这是我辛苦的结果 #include"iostream"using namespace std;class aaa{ int x;public: aaa() ...
- 008-Scala主构造器、私有构造器、构造器重载实战详解
008-Scala主构造器.私有构造器.构造器重载实战详解 Scala主构造器实战 无参数的主构造器 分析 1.name 需要赋初值,一般通过占位符来代表空值 2.private 声明私有的age 生 ...
- C# 重载运算符
如果你想让自己定义的类型可以用运算符进行运算,那么可以通过重载运算符来实现: 示例: class Salary { public int RMB { get; set; } public static ...
- 【STL】重载运算符
重载运算符 为什么要重载运算符: C++中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如结构体),也需要类似的运算操作.这时就必须在C++中重新定义这些运算符,赋予 ...
- c++中有些重载运算符为什么要返回引用
事实上,我们的重载运算符返回void.返回对象本身.返回对象引用都是可以的,并不是说一定要返回一个引用,只不过在不同的情况下需要不同的返回值. 那么什么情况下要返回对象的引用呢? 原因有两个: 允许进 ...
- C++ Primer : : 第十四章 : 重载运算符与类型转换之类型转换运算符和重载匹配
类型转换运算符 class SmallInt { public: SmallInt(int i = 0) : val(i) { if (i < 0 || i > 255) throw st ...
- C++ Primer : 第十四章 : 重载运算与类型转换之重载运算符
重载前须知 重载运算符是特殊的函数,它们的名字由operator和其后要重载的运算符号共同组成. 因为重载运算符时函数, 因此它包含返回值.参数列表和函数体. 对于重载运算符是成员函数时, 它的第一个 ...
- C#基础知识系列一(goto、i++、三元运算符、ref和out、String和string、重载运算符)
前言 这两天在网上看到的总结很多,尤其是博客园中的,很多很多,也给了我很多的启发,当然自己也总结过,而且有很多人也给与我一些意见和看法.不管怎样,自己还是先把所谓的基础知识加强巩固下吧. 2014年的 ...
- c++重载运算符注意
c++重载运算符的时候加&或不加: 如果加了&表示引用,说明用的都是同一块内存.如果不加,那么用的就是一份拷贝,即不同的内存. 一般连续操作的时候要加&. 可以重新定义一个对象 ...
随机推荐
- Python学习3——Python的简单推导
列表推导是一种从其他列表创建列表的方式,类似于数学中的集合推导,列表推导的工作原理非常简单,类似于for循环.(以下代码均在IDLE实现) 最简单的列表推导: >>>[x*x for ...
- centos7 安装NVM 管理node
[转载] 转载自https://blog.csdn.net/shuizhaoshui/article/details/79325931 NVM git地址: https://github.com/cr ...
- Hadoop学习-hdfs安装及其一些操作
hdfs:分布式文件系统 有目录结构,顶层目录是: /,存的是文件,把文件存入hdfs后,会把这个文件进行切块并且进行备份,切块大小和备份的数量有客户决定. 存文件的叫datanode,记录文件的切 ...
- Python在office开发中的应用
Python with Excel 有几个很好的Python模块能够方便地操作Excel的数据,包括读与写,不要求本地安装Excel.例如pandas, openpyxl, xlrd, xlutils ...
- 知识图谱学习与实践(4)——通过例句介绍Sparql的使用
通过例句介绍Sparql的使用 1 简介 SPARQL的定义,是一个递归的定义,为SPARQL Protocal and RDF Query Language,是W3C制定的RDF知识图谱标准查询语言 ...
- mySQL相关函数的使用
获取执行SQL指令被影响的记录数或字段数 ·mysqlo_num_rows()函数:适用于执行SELECT语句,可以返回被筛选出来的记录数. 其语法如下,参数result为资源标识符 mysqlo_n ...
- java练习---1
//程序员:罗元昊 2017.9.6public class Ap{ public static void main(String[] args){ System.out.println(" ...
- STL 大法好
#include <vector> 1.支持随机访问,但不支持在任意位置O(1)插入: 2.定义: ```cpp vector<int> a; ``` ...
- Python机器学习·微教程
Python目前是机器学习领域增长最快速的编程语言之一. 该教程共分为11小节.在这个教程里,你将学会: 如何处理数据集,并构建精确的预测模型 使用Python完成真实的机器学习项目 这是一个非常简洁 ...
- python基础之变量与数据类型
变量在python中变量可以理解为在计算机内存中命名的一个存储空间,可以存储任意类型的数据.变量命名变量名可以使用英文.数字和_命名,且不能用数字开头使用赋值运算符等号“=”用来给变量赋值.变量赋值等 ...