C++运算符重载例程详解
- 格式:docx
- 大小:34.02 KB
- 文档页数:19
介绍C#中的运算符重载.介绍 C# 中的运算符重载周融,2007 年 5 ⽉(C) 2001-2007 保留所有权利。
重载是⾯向对象中的⼀个重要概念,它是对象多态性的⼀种不完全体现,⼈们通常所说的重载,往往指的是函数的重载。
本⽂向读者介绍⼀种新的重载模型——运算符重载。
在本⽂中的内容:1、为什么需要运算符重载2、C# 运算符重载决策⽰例3、C# 运算符重载⼀览表4、结论为什么需要运算符重载函数的重载为⼀个对象上的相同⾏为提供不同的参数⽅式,这样,开发⼈员便可以使⽤这些不同的参数实现类似的功能。
⼀组函数重载决策⼀般实现的功能是相同的,例如对 Object 对象上的 ToString() ⽅法就有⼏个重载版本,虽然它们接受的参数不同,但却都表达同⼀个⾏为的最终结果。
参数的不同导致函数重载版本的签名不同,这样编译器很容易知道需要调⽤那⼀个重载版本。
这种技术给开发⼈员带来了⽅便。
现在我们试图对重载的定义进⾏推⼴。
先让我们看看最简单的例⼦,我们通常需要像这样声明并初始化⼀个值类型的变量:int digit = 5;string sayHello = "Hello, World";这⾥的“=”运算符,就是将右边的值传递给左边变量的赋值运算符。
这⾥,5 的类型为 int,“Hello, World”的类型为 string,这与左边被赋值的变量类型完全⼀致。
但对于上述的解释,我们还可以这样认为:5 的类型为 uint 或 byte,"Hello, World"的类型为 char[],那么如此⼀来,赋值运算左边和右边的类型就不在等同,那么编译器如何处理呢?有⼈会说,这就是“隐式类型转换”,这个答案确实很好,但隐式类型转换的规则已经被编译器确定,如果赋值运算的两端不遵循隐式类型转换规则,则需要显式类型转换,例如:char c = '2';string s = (string)c;int i = (int)c;这些显式类型转换并不适⽤于任何场合,也许⼈们希望在其⾃定义的类中也能⽤赋值、加减乘除等语法操作它们。
C++基础系列——运算符重载1. 运算符重载简介所谓重载,就是赋予新的含义。
函数重载(Function Overloading)可以让⼀个函数名有多种功能,在不同情况下进⾏不同的操作。
同样运算符重载(Operator Overloading)可以让同⼀个运算符可以有不同的功能。
可以对 int、float、string 等不同类型数据进⾏操作<< 既是位移运算符,⼜可以配合 cout 向控制台输出数据也可以⾃定义运算符重载:class Complex{public:Complex();Complex(double real, double imag);Complex operator+(const Complex &a) const;void display() const;private:double m_real;double m_imag;};// ...// 实现运算符重载Complex Complex::operator+(const Complex &A) const{Complex B;B.m_real = this->m_real + A.m_real;B.m_imag = this -> m_imag + A.m_imag;return B;// return Complex(this->m_real + A.m_real, this->m_imag + A.m_imag);}int main(){Complex c1(4.3, 5.8);Complex c2(2.7, 3.7);Complex c3;c3 = c1 + c2; // 运算符重载c3.display();return 0;}运算结果7 + 9.5i运算符重载其实就是定义⼀个函数,在函数体内实现想要的功能,当⽤到该运算符时,编译器会⾃动调⽤这个函数,它本质上是函数重载。
C++基础知识之运算符重载详解⽬录运算符重载⽅式⼀, 使⽤成员函数重载运算符需求:把⽜⾁换猪⾁, ⽺⾁换猪⾁⽅式⼆, 使⽤⾮成员函数【友元函数】重载运算符两种⽅式的区别两种⽅式的选择:总结运算符重载为什么要使⽤运算符重载-C/C++的运算符,⽀持的数据类型,仅限于基本数据类型。
问题:⼀头⽜+⼀头马 = ?(⽜马神兽?)⼀个圆 +⼀个圆 = ?(想要变成⼀个更⼤的圆)⼀头⽜ – ⼀只⽺ = ? (想要变成4只⽺,原始的以物易物:1头⽜价值5只⽺)解决⽅案:使⽤运算符重载⽅式⼀, 使⽤成员函数重载运算符需求:把⽜⾁换猪⾁, ⽺⾁换猪⾁规则:⼀⽄⽜⾁:2⽄猪⾁⼀⽄⽺⾁:3⽄猪⾁实现:⽜ + ⽜ = ?猪⾁⽜ + ⽺ = ?猪⾁Cow类> Cow.h#pragma onceclass Pork;class Sheep;class Cow{ //⽜类public:Cow(int weight = 0);//使⽤运算符重载, 实现⽜⾁ + ⽜⾁ = 猪⾁Pork operator+(const Cow& cow);//使⽤运算符重载, 实现⽜⾁ + ⽺⾁ = 猪⾁Pork operator+(const Sheep& sheep);private:int weight; //重量};_________________________________________________________________________________________________________________________________ > Cow.cpp#include "Cow.h"#include "Pork.h"#include "Sheep.h"Cow::Cow(int weight){this->weight = weight;}//⼀⽄⽜⾁换两⽄猪⾁Pork Cow::operator+(const Cow& cow){return Pork((this->weight + cow.weight) * 2);}//⼀⽄⽜⾁换两⽄猪⾁, ⼀⽄⽺⾁换三⽄猪⾁Pork Cow::operator+(const Sheep& sheep){int tmp = (this->weight * 2) + (sheep.getWeight() * 3);return Pork(tmp);}Sheep类> Sheep.h#pragma once//⽺类class Sheep{public:Sheep(int weight = 0);int getWeight() const;private:int weight; //重量};_________________________________________________________________________________________________________________________________ > Sheep.cpp#include "Sheep.h"Sheep::Sheep(int weight){this->weight = weight;}int Sheep::getWeight() const{return weight;}Pork类> Pork.h#pragma once#include <string>using namespace std;class Pork{ //猪⾁类public:Pork(int weight = 0);string description() const;private:int weight;};_________________________________________________________________________________________________________________________________ > Pork.cpp#include <sstream>#include "Pork.h"Pork::Pork(int weight){this->weight = weight;}string Pork::description() const{stringstream ret;ret << this->weight << "⽄";return ret.str();}main.cpp#include <iostream>#include <Windows.h>#include "Cow.h"#include "Pork.h"#include "Sheep.h"using namespace std;int main(void) {Pork p1;Cow c1(100);Cow c2(200);Sheep s1(100);//调⽤运算符重载 Pork operator+(const Cow& cow);p1 = c1 + c2;cout << "⽜ + ⽜ = 猪⾁:" << p1.description() << endl;//调⽤运算符重载 Pork operator+(const Sheep& c1);p1 = c1 + s1;cout << "⽜ + ⽺ = 猪⾁:" << p1.description() << endl;//⽺+⽜会报错, 因为没有定义对应的⽺+⽜运算符重载//p1 = s1 + c1;system("pause");return 0;}⽅式⼆, 使⽤⾮成员函数【友元函数】重载运算符实现:⽜ + ⽜ = ?猪⾁⽜ + ⽺ = ?猪⾁Cow类> Cow.h#pragma onceclass Pork;class Sheep;class Cow{ //⽜类public:Cow(int weight = 0);//使⽤友元运算符重载, 实现⽜⾁ + ⽜⾁ = 猪⾁friend Pork operator+(const Cow& c1, const Cow& c2);//使⽤友元运算符重载, 实现⽜⾁ + ⽺⾁ = 猪⾁friend Pork operator+(const Cow& c1, const Sheep& s1);private:int weight; //重量};_________________________________________________________________________________________________________________________________ > Cow.cpp#include "Cow.h"Cow::Cow(int weight){this->weight = weight;}Sheep类> Sheep.h#pragma once//⽺类class Sheep{public:Sheep(int weight = 0);int getWeight() const;private:int weight; //重量};_________________________________________________________________________________________________________________________________ > Sheep.cpp#include "Sheep.h"Sheep::Sheep(int weight){this->weight = weight;}int Sheep::getWeight() const{return weight;}Pork类> Pork.h#pragma once#include <string>using namespace std;class Pork{ //猪⾁类public:Pork(int weight = 0);string description() const;private:int weight;};_________________________________________________________________________________________________________________________________ > Pork.cpp#include <sstream>#include "Pork.h"Pork::Pork(int weight){this->weight = weight;}string Pork::description() const{stringstream ret;ret << this->weight << "⽄";return ret.str();}main.cpp#include <iostream>#include <Windows.h>#include "Cow.h"#include "Pork.h"#include "Sheep.h"using namespace std;//要想访问类的私有数据成员, 就把这个函数定义为友元Pork operator+(const Cow& c1, const Cow& c2) {return ((c1.weight + c2.weight) * 2);}//要想访问类的私有数据成员, 就把这个函数定义为友元Pork operator+(const Cow& c1, const Sheep& s1) {return((c1.weight * 2) + (s1.getWeight() * 3));}int main(void) {Pork p1;Cow c1(100); //100⽄的⽜Cow c2(200); //200⽄的⽜Sheep s1(100); //100⽄的⽺//调⽤ friend Pork operator+(const Cow& c1, const Cow& c2);p1 = c1 + c2;cout << "使⽤友元⽜ + ⽜ = 猪⾁:" << p1.description() << endl;//调⽤ friend Pork operator+(const Cow& c1, const Sheep& s1);p1 = c1 + s1;cout << "使⽤友元⽜ + ⽺ = 猪⾁:" << p1.description() << endl;system("pause");return 0;}两种⽅式的区别区别:使⽤成员函数来实现运算符重载时,少写⼀个参数,因为第⼀个参数就是this指针。
c语言重载赋值运算符C语言作为一门高级编程语言,提供了丰富的功能以满足各种编程需求。
其中,运算符重载就是一项非常重要的特性。
运算符重载可以让程序员自定义已有运算符在新类型上的操作方式,使得语言更加灵活和强大。
本文将详细介绍C语言中赋值运算符的重载,包括重载的原理、步骤、方法以及注意事项。
1.C语言中的重载概念C语言中的运算符重载是指在已有的运算符上,根据运算对象的类型,赋予新的操作含义。
这种重载是基于类型的,不同类型之间的运算符重载有不同的处理方式。
运算符重载可以让原有运算符在特定类型上具有更符合语义的操作方式,例如对赋值运算符的重载可以让赋值操作更加直观。
2.赋值运算符的重载原理在C语言中,赋值运算符"="原本用于将右侧的值赋给左侧的变量。
当我们对赋值运算符进行重载时,实际上是将原有赋值操作转换为一个新的表达式,这个表达式中包含了重载后的赋值操作。
重载后的赋值运算符需要满足以下条件:- 重载后的赋值运算符仍为一个二元运算符。
- 重载后的赋值运算符的优先级和结合性与其他运算符保持一致。
- 重载后的赋值运算符需要考虑运算对象的类型,以实现正确的赋值操作。
3.重载赋值运算符的步骤与方法重载赋值运算符的步骤如下:- 定义一个函数,该函数的参数列表中包含一个或多个变量引用。
- 在函数体中,对传入的变量进行操作,以实现重载后的赋值操作。
- 使用函数返回值替换原赋值表达式中的右侧值。
以下是一个重载赋值运算符的示例:```c#include <iostream>class MyClass {public:void operator=(const MyClass& other) {// 实现重载后的赋值操作std::cout << "重载赋值运算符被调用" << std::endl;}};int main() {MyClass obj1;MyClass obj2;obj1 = obj2; // 调用重载后的赋值运算符return 0;}```4.重载赋值运算符的注意事项- 重载赋值运算符时,需要确保运算对象具有可赋值性。
C语⾔运算符的重载详解⽬录写⼀个Add函数为什么不⽤加号作为函数名运算符的重载上⾯问题解决总结写⼀个Add函数我们先讨论下⾯代码,并复习前⾯的内容class Complex{private:double Real, Image;public:Complex() :Real(0), Image(0) {}Complex(double r, double i) :Real(r), Image(i) {}~Complex() {}//Complex Add(const Complex* const this,const Complex &c)Complex Add(const Complex& x)const{Complex y;y.Real = Real + x.Real;y.Image = Image + x.Image;return y;//return Complex(this->Real + x.Real, this->Image + x.Image);}void Print(){cout << Real << "+" << Image << "i" << endl;}};int main(){Complex c1(12, 23);Complex c2(4.5, 5.6);Complex c3;c3 = c1.Add(c2);c3.Print();return 0;}直接return可以使⽤⽆名函数直接代替将亡值对象,相⽐可以省⼀次对象的构建我们再分析如果我们使⽤引⽤返回Add函数const Complex& Add(const Complex& x)const{Complex y;y.Real = Real + x.Real;y.Image = Image + x.Image;return y;//return Complex(this->Real + x.Real, this->Image + x.Image);}若我们以引⽤返回,将亡值对象会创建在Add函数的栈帧中,然后返回将亡值地址,函数return结束该空间会被释放、若没有引⽤,构建⽆名对象也就是将亡值对象会构建在主函数的空间中,这⾥使⽤将亡值对象值给到c3是没有问题的我们查看对象的构造与析构class Complex{private:double Real, Image;public:Complex() :Real(0), Image(0) {}Complex(double r, double i) :Real(r), Image(i){cout << "Create:" << this << endl;}Complex(const Complex& x):Real(x.Real),Image(x.Image){cout << "Copy Create:" << this << endl;}~Complex(){cout << "~Complex:" << this << endl;}//Complex Add(const Complex* const this,const Complex &c)Complex Add(const Complex& x)const{return Complex(this->Real + x.Real, this->Image + x.Image);}void Print(){cout << Real << "+" << Image << "i" << endl;}};int main(){Complex c1(12, 23);Complex c2(4.5, 5.6);Complex c3;c3 = c1.Add(c2);c3.Print();return 0;}⾸先我们使⽤引⽤返回需要加上const修饰,这是因为我们返回将亡值在临时空间具有常性,所以普通引⽤是不能进⾏返回的,需要使⽤常引⽤返回const Complex& Add(const Complex& x)const{return Complex(this->Real + x.Real, this->Image + x.Image);}我们发现对临时对象的构建后马上就进⾏析构,那么是怎么将数据拿出给到c3的?这个在最后我们进⾏分析为什么不⽤加号作为函数名//Complex operator+(const Complex* const this,const Complex &c)Complex operator+(const Complex &c) const{return Complex(this->Real + x.Real, this->Image + x.Image);}这⾥是不可以的,加号是⼀个操作符,不能使⽤操作放作为有效的函数名称;但是在C++中为了使这些操作符号能够当作函数名,那么我们需要在前⾯加上⼀个关键字operator//Complex operator+(const Complex* const this,const Complex &c)Complex operator+(const Complex &c) const{return Complex(this->Real + x.Real, this->Image + x.Image);}也就是告诉编译器,加号是⼀个有效的函数名,这就叫做运算符的重载;随后我们之间使⽤ c3 = c1 + c2 就是可以的int main(){Complex c1(12, 23);Complex c2(4.5, 5.6);Complex c3;c3 = c1 + c2;//编译器编译会是下⾯的情况//c3 = c1.operator+(c2);//c3 = operator+(&c1,c2); 加上this指针}运算符的重载⼀个对象,编译器会给它有6个缺省函数我们再来看下⾯这个问题//我们写⼀个赋值运算符重载void operator=(const Object& obj){this->value = obj.value;}//返回类型为void,这样不可以就不可以连等//obja = objb = objc;//obja = objb.operator=(objc);//obja = operator=(&objb,objc); 返回的⽆类型,不能给obja赋值且赋值函数不可以定义为const修饰Object& operator=(const Object& obj){this->value = obj.value;return *this;}obja = objb = objc;//改写obja = objb.operator=(objc);obja = operator=(&objb,objc);obja.operator=(operator=(&objb,objc));operator=(&obja,operator=(&objb,objc));通过返回对象,就可以实现连等;并且我们可以通过引⽤返回,因为此对象的⽣存期并不受函数的影响,不会产⽣⼀个临时对象作为⼀个过度防⽌⾃赋值若是我们将obja给obja赋值,也就是⾃赋值obja = objaoperator=(&obja,obja);我们就需要进⾏⼀步判断Object& operator=(const Object& obj){if(this != &obj)//防⽌⾃赋值{this->value = obj.value;}return *this;}上⾯问题解决我们通过这段代码来看,与上⾯问题相同Object& operator=(const Object& obj){if (this != &obj){this->value = obj.value;}return *this;}};Object& fun(const Object& obj){int val = obj.Value() + 10;Object obja(val);return obja;}int main(){Object objx(0);Object objy(0);objy = fun(objx);cout << objy.Value() << endl;return 0;}我们在这⾥希望通过引⽤返回,这⾥return的临时对象会构建在fun函数的栈帧中,并且在函数结束栈帧释放,随后调⽤赋值运算符重载,但是数值依旧是正确的我们跟踪这个被析构对象的地址,⾸先我们定位在fun函数的return obja;,随后进⼊析构函数将我们的obja进⾏析构接着运⾏到回到主函数进⾏赋值,接着进⼊赋值运算符重载,可以看到,这⾥的obj地址与已被析构的obja地址相同可以看到这个值依旧存在,依旧可以打印给出,这是因为vs2019的特殊性质造成的;我们每次运⾏程序会发现每次的对象地址都在变化,逻辑地址会随机改变,被析构对象的栈帧不会被接下来的赋值运算符重载扰乱地址空间,所以即使我们引⽤返回的对象已经死亡依旧可以将数值正确返回但是在vc中,我们得到的值会是随机值,这是因为vc中每次运⾏程序地址都不会改变,当我们从fun函数退出进⼊赋值语句中,就会将原本存储数据的地址扰乱,继⽽变成了随机值尽管我们引⽤返回能够将数据正确打印,但是该对象已经死亡,这是不安全的,所以我们⼀定不要以引⽤返回对象VS2019具有⼀个特点:当我们调⽤函数,若函数中没有定义局部变量或局部对象时,该函数基本不对栈帧进⾏清扫总结到此这篇关于C语⾔运算符的重载详解的⽂章就介绍到这了,更多相关C语⾔运算符重载内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
运算符重载就是对一个已有的运算符赋予新的含义,使之实现新功能。
下面将通过简单的几个例程阐叙其用法。
例10.1 通过函数来实现复数相加。
#include <iostream>using namespace std;class Complex //定义Complex类{public:Complex( ){real=0;imag=0;} //定义构造函数Complex(double r,double i){real=r;imag=i;} //构造函数重载Complex complex_add(Complex &c2); //声明复数相加函数void display( ); //声明输出函数private:double real; //实部double imag; //虚部};Complex Complex::complex_add(Complex &c2){Complex c;c.real=real+c2.real;c.imag=imag+c2.imag;return c;}void Complex::display( ) //定义输出函数{cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;}int main( ){Complex c1(3,4),c2(5,-10),c3; //定义3个复数对象c3=plex_add(c2); //调用复数相加函数cout<<″c1=″; c1.display( ); //输出c1的值cout<<″c2=″; c2.display( ); //输出c2的值cout<<″c1+c2=″; c3.display( ); //输出c3的值return 0;}例10.2 改写例10.1,重载运算符“+”,使之能用于两个复数相加。
#include <iostream>using namespace std;class Complex{public:Complex( ){real=0;imag=0;}Complex(double r,double i){real=r;imag=i;}Complex operator+(Complex &c2); //声明重载运算符的函数void display( );private:double real;double imag;};Complex Complex::operator+(Complex &c2) //定义重载运算符的函数{ Complex c;c.real=real+c2.real;c.imag=imag+c2.imag;return c;}void Complex∷display( ){ cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;}int main( ){ Complex c1(3,4),c2(5,-10),c3;c3=c1+c2; //运算符+用于复数运算cout<<″c1=″;c1.display( );cout<<″c2=″;c2.display( );cout<<″c1+c2=″;c3.display( );return 0;}例10.3 将运算符“+”重载为适用于复数加法,重载函数不作为成员函数,而放在类外,作为Complex类的友元函数。
#include <iostream>using namespace std;class Complex{public:Complex( ){real=0;imag=0;}Complex(double r,double i){real=r;imag=i;}friend Complex operator + (Complex &c1,Complex &c2);//重载函数作为友元函数void display( );private:double real;double imag;};Complex operator + (Complex &c1,Complex &c2)//定义作为友元函数的重载函数{return Complex(c1.real+c2.real, c1.imag+c2.imag);}//简洁的写法void Comple::display( ){cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;}int main( ){Complex c1(3,4),c2(5,-10),c3;c3=c1+c2;cout<<″c1=″; c1.display( );cout<<″c2=″; c2.display( );cout<<″c1+c2 =″; c3.display( );}例10.4 定义一个字符串类String,用来存放不定长的字符串,重载运算符“==”,“<”和“>”,用于两个字符串的等于、小于和大于的比较运算。
为了使读者便于理解程序,同时也使读者了解建立程序的步骤,下面分几步来介绍编程过程。
(1) 先建立一个String类:#include <iostream>using namespace std;class String{public:String( ){p=NULL;} //默认构造函数String(char *str); //构造函数void display( );private:char *p; //字符型指针,用于指向字符串};String∷String(char *str) //定义构造函数{p=str;} //使p指向实参字符串void String∷display( ) //输出p所指向的字符串{cout<<p;}int main( ){String string1(″Hello″),string2(″Book″);string1.display( );cout<<endl;string2.display( );return 0;}(2) 有了这个基础后,再增加其他必要的内容。
现在增加对运算符重载的部分。
为便于编写和调试,先重载一个运算符“>”。
程序如下:#include <iostream>#include <string>using namespace std;class String{public:String( ){p=NULL;}String(char *str);friend bool operator>(String &string1,String &string2);//声明运算符函数为友元函数void display( );private:char *p; //字符型指针,用于指向字符串};String∷String(char *str){p=str;}void String∷display( ) //输出p所指向的字符串{cout<<p;}bool operator>(String &string1,String &string2) //定义运算符重载函数{if(strcmp(string1.p,string2.p)>0)return true;else return false;}int main( ){String string1(″Hello″),string2(″Book″);cout<<(string1>string2)<<endl;}(3) 扩展到对3个运算符重载。
在String类体中声明3个成员函数:friend bool operator> (String &string1, String &string2);friend bool operator< (String &string1, String &string2);friend bool operator==(String &string1, String& string2);在类外分别定义3个运算符重载函数:bool operator>(String &string1,String &string2) //对运算符“>”重载{if(strcmp(string1.p,string2.p)>0)return true;elsereturn false;}bool operator<(String &string1,String &string2) //对运算符“<”重载{if(strcmp(string1.p,string2.p)<0)return true;elsereturn false;}bool operator==(String &string1,String &string2) //对运算符“==”重载{if(strcmp(string1.p,string2.p)==0)return true;elsereturn false;}再修改主函数:int main( ){String string1(″Hello″),string2(″Book″),string3(″Computer″); cout<<(string1>string2)<<endl; //比较结果应该为true cout<<(string1<string3)<<endl; //比较结果应该为falsecout<<(string1==string2)<<endl; //比较结果应该为false return 0;}(4) 再进一步修饰完善,使输出结果更直观。
下面给出最后的程序。
#include <iostream>using namespace std;class String{public:String( ){p=NULL;}String(char *str);friend bool operator>(String &string1,String &string2);friend bool operator<(String &string1,String &string2);friend bool operator==(String &string1,String &string2);void display( );private:char *p;};String∷String(char *str){p=str;}void String∷display( ) //输出p所指向的字符串{cout<<p;}bool operator>(String &string1,String &string2) {if(strcmp(string1.p,string2.p)>0)return true;elsereturn false;}bool operator<(String &string1,String &string2) {if(strcmp(string1.p,string2.p)<0)return true;elsereturn false;}bool operator==(String &string1,String &string2) {if(strcmp(string1.p,string2.p)==0)return true;elsereturn false;}void compare(String &string1,String &string2){if(operator>(string1,string2)==1){string1.display( );cout<<″>″;string2.display( );}elseif(operator<(string1,string2)==1){string1.display( );cout<<″<″;string2.display( );}elseif(operator==(string1,string2)==1){string1.display( );cout<<″=″;string2.display( );}cout<<endl;}int main( ){String string1(″Hello″),string2(″Book″),string3(″Computer″),string4(″Hello″);compare(string1,string2);compare(string2,string3);compare(string1,string4);return 0;}例10.5 有一个Time类,包含数据成员minute(分)和sec(秒),模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算。