C++ 第7章 运算符重载

2024-09-07 04:48
文章标签 c++ 重载 运算符

本文主要是介绍C++ 第7章 运算符重载,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

7.1 运算符重载规则

7.1.1 重载运算符的限制
可重载运算符:

+ - * / % ^ & | ~
! = < > += -+ *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* , ->
[] () new delete

不可重载运算符:

. .* :: ?: sizeof

重载运算符函数可以对运算符做出新的解释,即定义用户所需要的各种操作。但运算符重载后,原有的基本语义不变,包括:
(1)不改变运算符的优先级
(2)不改变运算符的结合性
(3)不改变运算符所需要的操作数

7.1.2 重载运算符的语法形式

运算符函数是一种特殊的成员函数或友元函数。成员函数的语句格式为:
类型 类名::operator op(参数表)

//相对于该类定义的操作

其中,“类型”是函数的返回类型。“类名”是要重载该运算符的类。“op”表示要重载的运算符。函数名是“operator op”,由关键字operator和被重载的运算符op组成。“参数表”列出该运算符所需要的操作数。

设计一个安全计算器

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>using namespace std;class Calculator
{public:Calculator(){value = 0;}void operator++();                  //重载自增运算符void operator--();                  //重载自减运算符unsigned int operator()()const;     //重载括号运算符private:unsigned int value;
};void Calculator::operator++()
{if (value < 65535)++value;    //使用语言预定义版本else    //溢出处理{cout<<"\nData overflow!"<<endl;abort();}
}void Calculator::operator--()
{if(value>0) --value;else{cout<<"\nData overflow!"<<endl;abort();}
}unsigned int Calculator::operator()()const
{return value;
}int main()
{Calculator Counter;int i;for(i=0; i<5;i++){++Counter;cout<<"\nCounter = "<< Counter();}for(i=0; i<=5; i++){--Counter;cout<<"\nCounter = "<< Counter();}
}//程序运行结果:
Counter = 1
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 4
Counter = 3
Counter = 2
Counter = 1
Counter = 0
Data overflow!
Aborted (core dumped)

调用重载函数的方式:
++Counter, –Counter, Counter()

函数名的方式:
Counter.operator++(), Counter.operator–(), Counter.operator()();

重载函数可以对运算符定义新的操作,甚至编写与原来版本意思完全不同的代码。
用于类运算的运算符通常都要重载,但有两个运算符系统提供默认重载版本:
1.赋值运算符”=”,系统默认重载为对象数据成员的复制;
2.地址运算符“&”,系统默认重载为返回任何类对象的地址;

7.2 用成员或友元函数重载运算符

运算符函数即可以重载为成员函数,也可以重载为友元函数或普通函数。使用非成员、非友元的普通函数重载访问private和protected数据成员时,必须通过public接口提供的函数实现,增加程序开销。所以通常重载运算符用成员函数或友元函数。它们的关键区别在于,成员函数具有this指针,而友元函数没有this指针。
1.一元运算符
一元运算符不论前置或后置,都要求有一个操作数:
Object op 或 op Object
当重载为成员函数时,编译器解释为:
Object.operator op()
函数operator op所需的操作数由对象Object通过this指针隐含传递,所以参数为空。
当重载为成员函数时,编译解释为:
operator op(Object)
函数operator op所需的操作数由参数表的参数Object提供。

2.二元运算符
任何二元运算符都要求有左,右操作数:
ObjectL op ObjectR
当重载为成员函数时,编译器解释为:
ObjectL.operator op(ObjectR)
左操作数由对象ObjectL通过this指针传递,右操作数由参数ObjectR传递。
重载友元函数时,编译解释为:
operator op(ObjectL, ObjectR)
左,右操作数都由参数传递。

7.2.1 用成员函数重载运算符

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>using namespace std;class TriCoor
{
    public:TriCoor(int mx = 0, int my = 0, int mz = 0);TriCoor operator+( TriCoor t);TriCoor & operator++();TriCoor & operator=(TriCoor t);void show();void assign(int mx, int my, int mz);
    private:int x,y,z;
};TriCoor::TriCoor(int mx, int my, int mz)
{x = mx;y = my;z = mz;
}TriCoor TriCoor::operator+(TriCoor t)
{TriCoor temp;temp.x = x + t.x;temp.y = y + t.y;temp.z = z + t.z;return temp;
}TriCoor & TriCoor::operator++()
{x++;y++;z++;return *this;
}TriCoor & TriCoor::operator=(TriCoor t)
{x = t.x;y = t.y;z = t.z;return *this;
}void TriCoor::show()
{cout<<x<<","<<y<<","<<","<<z<<endl;
}void TriCoor::assign(int mx, int my, int mz)
{x = mx;y = my;z = mz;
}int main()
{TriCoor a(1, 2, 3), b, c;a.show();b.show();c.show();for(int i = 0; i<5; i++)++b;b.show();c.assign(4,5,6);c = a+b+c;c.show();c = b = a;c.show();
}//运行结果
1,2,,3
0,0,,0
0,0,,0
5,5,,5
10,12,,14
1,2,,3

语句:
temp.x = x + t.x;
相当于:
temp.x = this->x + t.x;

*this是引起调用函数的对象,它是运算符的左操作数。如:
a + b
激活函数的是对象a,运算符右边的对象被作为参数传递给函数。因此,该表达式解释为:a.operator+(b)

重载运算符函数像其他函数一样,可以返回其他C++合法类型。函数返回类引用即符合运算符原来的语义,又减少了函数返回时对匿名对象数据复制的开销。
重载运算符函数中的语句:
return *this;

7.2.2 用友元函数重载运算符

有时,运算符的左,右操作数类型不同,用成员函数重载运算符会碰到麻烦:

class Complex
{public:Complex(int a){ Real = a; Image = 0;}Complex(int a, int b){ Real = a; Image = b;}Complex operator+(Complex);private:int Real;int Image;//...
};
int f()
{Complex z(1,2), k(3,4);z = z + 25; //正确//z = 25 + z;  //错误//...
}

z + 25被解释为:z.operator(25) //合法
25 +z被解释为:25.operator(z) //不合法

整型常量通过参数调用构造函数实现类型转换:

#include <iostream>
#include <cstring>
#include <cmath>using namespace std;class Complex
{public:Complex(double r = 0, double i = 0);Complex(int a){Real = a;Image = 0;  }void print() const;friend Complex operator+( const Complex &c1, const Complex &c2 );friend Complex operator-( const Complex &c1, const Complex &c2 );friend Complex operator-( const Complex &c );private:double Real, Image;
};Complex::Complex(double r, double i)
{Real = r;Image = i;
}Complex operator+( const Complex &c1, const Complex &c2 )
{double r = c1.Real + c2.Real;double i = c1.Image + c2.Image;return Complex(r, i);
}Complex operator-( const Complex &c1, const Complex &c2)
{double r = c1.Real - c2.Real;double i = c1.Image - c2.Image;return Complex(r, i);
}Complex operator-( const Complex &c )
{return Complex(-c.Real, -c.Image);
}void Complex::print()const
{cout<<"("<<Real<<","<<Image<<")"<<endl;
}int main()
{Complex c1(2.5, 3.7), c2(4.2, 6.5);Complex c;c = c1 - c2;c.print();c = 25 + c2;c.print();c = c2 + 25;c.print();c = -c1;c.print();
}//程序运行结果
(-1.7,-2.8)
(29.2,6.5)
(29.2,6.5)
(-2.5,-3.7)

7.3 几个典型运算符的重载

7.3.1 重载++ 与 –
自增自减运算符有前置和后置两种形式。C++规定,前置形式重载为一元运算符函数,后置形式重载为二元运算符函数。
(1)前置自增表达式
++Aobject
若用成员函数重载,则编译器解释为:
Aobject.operator++()
对应的函数原型为:
A & A::operator++();
若用友元函数重载,则编译器解释为:
operator++(Aobject)
对应的函数原型为:
friend A & operator++(A &);

(2)后置自增表达式
Aobject++
成员函数重载解释为:
Aobject.operator++(0)
对应的函数原型为:
A & A::operator++(int)
友元函数重载的解释为:
operator++(Aobject, 0)
对应的函数原型为:
friend A & operator++(A &, int);
在此,参数0是一个伪值,用于与前置形式重载相区别。另外,友元重载函数返回类类型的引用是为了减少函数返回时对象复制的开销,可以根据需要选择是否返回类类型的引用。

用友元函数重载++运算符。

class Increase
{public:Increase();//...friend Increase operator++(Increase &);friend Increase operator++(Increase &, int);private:unsigned value;
};
则前置重载的实现为:
Increase operator++(Increase & a)
{a.value++;return a;
}
后置重载的实现为:
Increase operator++(Increase &a, int)
{Increase temp(a);a.value++;return temp;
}

它们的实现区别是,后置操作中使用了临时变量temp,保存对象a的原值作为函数的返回值,然后对a进行自增运算。
函数体中不应该使用伪参数,否则会引起调用的二义性。
可以把置++重载函数改写为:

Increase Increase::operator++(int x)
{Increase temp;temp.value = value;value += x;return temp;
}

重载运算符函数可以用两种方式调用:
Aobject++ //隐式调用
Aobject.operator++(0) //显式调用

7.3.2 重载赋值运算符

赋值运算符重载用于对象数据的复制,只能用成员函数重载。重载函数原型为:
类名 & 类名::operator=(类名);

定义Name类的重载赋值函数:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>using namespace std;class Name
{public :Name( char *pN='\0');Name( const Name & );Name & Name::operator=( Name );~Name();protected:char *pName;int size;
};Name::Name( char *pN )
{cout<<"Construction "<<pN<<endl;size = strlen( pN );pName = new char[size+1];if(pName != 0) strcpy_s(pName, size+1, pN);
}Name::Name(const Name & Obj)  //定义复制构造函数
{cout<<"Copying"<<Obj.pName<<" into its own block!\n";size = Obj.size;pName = new char[size+1];if(pName != 0) strcpy_s(pName, size+1, Obj.pName);
}Name & Name::operator=(Name Obj) //重载赋值运算符
{delete[] pName;size = Obj.size;pName = new char[size+1];if(pName != 0) strcpy_s(pName, size+1, Obj.pName);return *this;
}Name::~Name()
{cout<<"Destructing "<<pName<<endl;pName[0] = '\0';delete[] pName;pName = NULL;size = 0;
}int main()
{Name Obj1("song yu long");Name Obj2 = Obj1;   //调用复制构造函数Name Obj3(Noname);Obj3 = Obj2 = Obj1; //调用重载赋值构造函数
}

7.3.3 重载运算符[]和()

运算符“[]”和”()”只能用成员函数重载,不能用友元函数重载。
1.重载下标运算符[]
下标运算符[]是二元运算符,用于访问数据对象的元素。其重载函数调用的一般形式为:
对象 [表达式]
如,类X有重载函数:

int & X::operator[](int);

其中,x是X类的对象,则调用函数的表达式:

x[k]
被解释为:x.operator[]()

2.重载函数调用运算符()
函数调用运算符()可以看成一个元元运算符。其重载函数调用的一般形式为:
对象 (表达式表)
其中,”表达式表”可以为空。
如,类A有重载函数:
int A::operator()(int, int);
若a是A类对象,则调用函数的表达式;
a(x, y)
被解释为:a.operator()(x, y)

定义一个向量类,用重载[]运算符函数访问向量元素,用重载()运算符函数返回向量长度。

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>using namespace std;
class Vector
{public :Vector(int size = 1);~Vector();int & operator[](int i)const;int operator()()const;private :int *v;int len;
};Vector::Vector(int size)
{if(size<=0 || size >100){cout<<"The size of"<<size<<"is null!\n";exit(0);}v = new int[size];len = size;
}Vector::~Vector()
{delete[] v;v = NULL;len = 0;
}int & Vector::operator[](int i)const  //重载运算符[],返回元素引用
{if(i>=0 && i<len) return v[i];cout<<"The subscript "<<i<<" is outsize!\n";exit(0);
}int Vector::operator()()const //重载运算符(),返回向量长度
{return len;
}int main()
{int k, i;cin >> k;Vector A(k);for(i=0; i < k; i++)A[i] = i + 1;for(i=0; i<k; i++)cout<<A[i]<<" ";cout<<endl;cout<<"The sizeof Vector a is"<<A()<<endl;
}

7.3.4 重载流插入和流提取运算符

运算符“<<”和“>>”在C++的流类库中重载为插入和提取操作,用于输出和输入标准类型的数据和字符串。程序员也可以重载这两个运算符,通常用于传输用户自定义类型的类据。

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstring>using namespace std;
class Vector
{public:Vector(int = 1);Vector(const int*, int);Vector(const Vector&);~Vector();int & operator[](int i)const;int operator()()const;Vector & operator=(const Vector &);bool operator==(const Vector &)const;bool operator!=(const Vector &)const;friend Vector operator+(const Vector&, const Vector &);friend ostream & operator<<(ostream &output, const Vector &);friend istream & operator>>(istream &input, Vector &);private :int *v;int len;
};Vector::Vector(int size)
{if(size <= 0 || size >100){cout<<"The size of "<<size<<" is fail!\n";exit(0);}v = new int[size];  for(int i=0; i<size; i++)v[i]=0;len = size;
}Vector::Vector(const int *B, int size)
{if(size<=0 || size>100){cout<<"The size of "<<size<<" is fail!\n";exit(0);}v = new int[size];len = size;for(int i=0; i<size; i++)v[i] = B[i];
}Vector::Vector(const Vector& A)
{len = A();v = new int[len];for(int i=0; i<len; i++)v[i] = A[i];
}Vector::~Vector()
{delete[] v;len = 0;
}int & Vector::operator[](int i)const
{if(i>=0 && i<len) return v[i];cout<<"The subscript "<<i<<" is outsize !\n";exit(0);
}int Vector::operator()()const
{return len;
}Vector & Vector::operator=(const Vector &B)
{if(len == B() ){for(int i=0; i<len; i++)v[i] = B.v[i];return *this;}else{cout<<"Operator=fail!\n";exit(0);}
}bool Vector::operator==(const Vector &B)const
{if(len == B.len){for(int i=0; i<len; i++){if(v[i]!=B.v[i])return false;}}else{return false;}return true;
}bool Vector::operator!=(const Vector &B)const
{return !(*this == B);
}Vector operator+(const Vector &A, const Vector &B)
{int size = A();int *T = new int[size];if(size ==B() ){for(int i=0; i<size; i++)T[i] = A.v[i] + B.v[i];return Vector(T, size);}else{cout<<"Operator + fail!\n";exit(0);}
}ostream & operator<<(ostream &output, const Vector &A)
{for(int i=0; i<A.len; i++)output<<A.v[i]<<" ";return output;
}istream & operator>>(istream & input, Vector & A)
{for(int i=0; i<A(); i++)input>>A.v[i];return input;
}int main()
{int k;cout<<"Input the lenght of Vector:\n";cin >>k;Vector A(k),B(k),C(k);cout<<"Input the elements of Vector A:\n";cin>>A;cout<<"Input the elements of Vector B:\n";cin>>B;if(A==B){for(int i=0; i<A(); i++)C[i] = A[i]*2;}else{C = A+B;}cout<<" ["<<A<<"]\n+["<<B<<"]\n=["<<C<<"]"<<'\n';
}

这篇关于C++ 第7章 运算符重载的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1144104

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(