app建站平台,深圳企业做网站公司,上海 响应式网站公司,行业解决方案操作符重载
操作符重载(operator overoading)是一种形式的 C多态。
第8章介绍了C是如何使用户能够定义多个名称相同但特征标(参数列表)不同的函数的。这被称为函数重载(function overloading)或函数多态(functional polymorphism)#xff0c;旨在让您能够用同名的函数来完成…操作符重载
操作符重载(operator overoading)是一种形式的 C多态。
第8章介绍了C是如何使用户能够定义多个名称相同但特征标(参数列表)不同的函数的。这被称为函数重载(function overloading)或函数多态(functional polymorphism)旨在让您能够用同名的函数来完成相同的基本操作即使这种操作被用于不同的数据类型
实际上很多 C(也包括C语言)操作符已经被重载。例如将*操作符用于地址将得到存储在这个地址中的值;但将它用于两个数字时得到的将是它们的乘积。C根据操作数的数目和类型来决定采用哪种操作。
数组加法 op是将要重载的操作符。例如operator ()重载操作符operator*()重载*操作符。op 必须是有效的 C操作符不能虚构一个新的符号。例如不能有 operator()这样的函数因为 C中没有操作符。
操作符重载范例
mytime0.h
// mytime0.h-- Time class before operator overloading
#ifndef MYTIMEO H
#define MYTIMEO H
class Time
{
private:int hours;int minutes;public:Time();Time(int h, int m 0);void AddMin(int m);void AddHr(int h);void Reset(int h 0, int m 0);Time Sum(const Time t) const;void Show() const;
};
#endif
Sum()函数的代码。注意参数是引用但返回类型却不是引用。将参数声明为引用的目的是为了提高效率。如果按值传递Time 对象代码的功能将相同但传递引用速度将更快使用的内存将更少。 mytime0.cpp
// mytime0.cpp--implement Time methods
#include iostream
#include mytime0.h
Time::Time()
{hours minutes 0;
}Time::Time(int h, int m)
{hours h;minutes m;
}void Time::AddMin(int m)
{minutes m;hours minutes / 60;minutes % 60;
}void Time::AddHr(int h)
{hours h;
}void Time::Reset(int h, int m)
{hours h;minutes m;
}
Time Time::Sum(const Time t) const
{Time sum;sum.minutes minutes t.minutes;sum.hours hours t.hours sum.minutes / 60;sum.minutes % 60;return sum;
}
void Time::Show() const
{std::cout hours hours. minutes minutes;
}
usetime0.cpp
#include iostream
#include mytime0.h
int main()
{using std::cout;using std::endl;Time planning;Time coding(2, 40);Time fixing(5, 55);Time total;cout planning time ;planning.Show();cout endl;cout coding time ;coding.Show();cout endl;cout fixing time ;fixing.Show();cout endl;total coding.Sum(fixing);cout coding.Sum(fixing);total.Show();return 0;
} 添加加法操作符
将 Time 类转换为重载的加法操作符很容易只要将 Sum()的名称改为 operator()即可。这样做是对的只要把操作符(这里为)放到operator的后面并将结果用作方法名即可。在这里可以在标识符中使用字母、数字或下划线之外的其他字符。
mytime1.h
// mytime0.h-- Time class before operator overloading
#ifndef MYTIMEO H
#define MYTIMEO H
class Time
{
private:int hours;int minutes;public:Time();Time(int h, int m 0);void AddMin(int m);void AddHr(int h);void Reset(int h 0, int m 0);// Time Sum(const Time t) const;Time operator(const Time t) const;void Show() const;
};
#endif
mytime1.cpp
// mytime0.cpp--implement Time methods
#include iostream
#include mytime0.h
Time::Time()
{hours minutes 0;
}Time::Time(int h, int m)
{hours h;minutes m;
}void Time::AddMin(int m)
{minutes m;hours minutes / 60;minutes % 60;
}void Time::AddHr(int h)
{hours h;
}void Time::Reset(int h, int m)
{hours h;minutes m;
}
Time Time::operator(const Time t) const
{Time sum;sum.minutes minutes t.minutes;sum.hours hours t.hours sum.minutes / 60;sum.minutes % 60;return sum;
}
void Time::Show() const
{std::cout hours hours. minutes minutes;
}
usetime1.cpp
#include iostream
#include mytime0.h
int main()
{using std::cout;using std::endl;Time planning;Time coding(2, 40);Time fixing(5, 55);Time total;cout planning time ;planning.Show();cout endl;cout coding time ;coding.Show();cout endl;cout fixing time ;fixing.Show();cout endl;total coding fixing;cout coding fixing;total.Show();cout endl;Time morefixing(3, 28);cout more fixing time ;morefixing.Show();cout endl;total morefixing.operator(total); // function notationcout morefixing.operator(total);total.Show();cout endl;return 0;
} 重载限制
重载的操作符(有些例外情况)不必是成员函数但必须至少有一个操作数是用户定义的类型 其他重载操作符
时间的减和乘 mytime2.h
// mytime0.h-- Time class before operator overloading
#ifndef MYTIMEO H
#define MYTIMEO H
class Time
{
private:int hours;int minutes;public:Time();Time(int h, int m 0);void AddMin(int m);void AddHr(int h);void Reset(int h 0, int m 0);// Time Sum(const Time t) const;Time operator(const Time t) const;Time operator-(const Time t) const;Time operator*(double n) const;void Show() const;
};
#endif
mytime2.cpp
// mytime0.cpp--implement Time methods
#include iostream
#include mytime0.h
Time::Time()
{hours minutes 0;
}Time::Time(int h, int m)
{hours h;minutes m;
}void Time::AddMin(int m)
{minutes m;hours minutes / 60;minutes % 60;
}void Time::AddHr(int h)
{hours h;
}void Time::Reset(int h, int m)
{hours h;minutes m;
}
Time Time::operator(const Time t) const
{Time sum;sum.minutes minutes t.minutes;sum.hours hours t.hours sum.minutes / 60;sum.minutes % 60;return sum;
}
Time Time::operator-(const Time t) const
{Time diff;int tot1, tot2;tot1 t.minutes 60 * t.hours;tot2 minutes 60 * hours;diff.minutes (tot2 - tot1) % 60;diff.hours (tot2 - tot1) / 60;return diff;
}
Time Time::operator*(double mult) const
{Time result;long totalminutes (minutes 60 * hours) * mult;result.minutes totalminutes % 60;result.hours totalminutes / 60;return result;
}
void Time::Show() const
{std::cout hours hours. minutes minutes;
}
usetime2.cpp
#include iostream
#include mytime0.h
int main()
{using std::cout;using std::endl;Time weeding(4, 35);Time waxing(2, 47);Time total;Time diff;Time adjusted;cout weeding time ;weeding.Show();cout endl;cout waxing time ;waxing.Show();cout endl;cout total time ;total weeding waxing;cout coding fixing;total.Show();cout endl;diff weeding - waxing;cout coding - fixing;diff.Show();cout endl;adjusted total * 1.5;cout Adjusted work time ;total.Show();cout endl;return 0;
} 友元简介
C控制对类对象私有部分的访问。通常公有类方法提供惟一的访问途径但是有时候这种限制太严格以致上不适合特定的编程问题。在这种情况下C提供了另外一种形式的访问权限: 友元。
友元有三种 介绍如何成为友元之前先介绍为何需要友元。在为类重载一元操作符时(带两个参数的操作符)常常需要友元。将Time对象乘以实数就属于这种情况 从概念上说2.75*B应与 B*2.75 相同但第一个表达式不对应于成员函数因为 2.75 不是 Time 类型的对象。记住左侧的操作数应是调用对象但2.75不是对象。因此编译器不能使用成员函数调用来替换该表达式。
决这个难题的一种方式是告知每个人(包括程序员自己)只能按B*2.75 这种格式编写不能写成 2.75*B。这是一种对服务器友好-客户警惕的(server-ffiendly,client-beware)解决方案,与OOP无关。 无法直接访问B中的内容
创建友元
创建友元函数的第是将其原型放在类声明中并在原型声明前加上关键字friend 第二步是编写函数定义。因为它不是成员函数所以不要使用Time:限定符。另外不要在定义中使用关键字 friend定义应该如下: 常用的友元重载操作符 之所以可以这样做是因为是可被重载的 C操作符之一
前面讲过cout 是一个 ostream 对象它是智能的能够识别所有的 C基本类型。这是因为对于每种基本类型ostream类声明中都包含了相应的重载的 operator()定义
因此要使 cout 能够识别 Time 对象一种方法是将一个新的函数操作符定义添加到 ostream 类声明中。但修改iostream 文件是个危险的主意这样做会在标准接口上浪费时间。更好的办法是通过Time类声明来让 Time 类知道如何使用 cout
第一种重载版本 司用 couttrip 应使用cout对象本身而不是它的拷贝因此该函数按引用(而不是按值)来传递该对象。这样表达式 couttrip 将导致 os 成为 cout 的一个别名;而表达式 cenr trip 将导致 os 成为 cen 的一个别名。Time对象可以按值或按引用来传递因为这两种形式都使函数能够使用对象的值。按引用传递使用的内存和时间都比按值传递少。
第二种重载版本
问题 例如 修改方法 调用os类的引用所以 特别的 头文件
// mytime0.h-- Time class before operator overloading
#ifndef MYTIME0_H_
#define MYTIME0_H_
class Time
{
private:int hours;int minutes;public:Time();Time(int h, int m 0);void AddMin(int m);void AddHr(int h);void Reset(int h 0, int m 0);// Time Sum(const Time t) const;Time operator(const Time t) const;Time operator-(const Time t) const;Time operator*(double n) const;friend Time operator*(double m, const Time t){return t * m;} // inline definitionfriend std::ostream operator(std::ostream os, const Time t);
};
#endif
其中包括 operator*()和 operator()这两个友元函数。它将第-个友元函数作为内联函数,因为其代码很短(当定义同时也是原型时,就像这个例子中那样,要使用 fiend前缀)。
源代码
// mytime0.cpp--implement Time methods
#include iostream
#include mytime0.h
Time::Time()
{hours minutes 0;
}Time::Time(int h, int m)
{hours h;minutes m;
}void Time::AddMin(int m)
{minutes m;hours minutes / 60;minutes % 60;
}void Time::AddHr(int h)
{hours h;
}void Time::Reset(int h, int m)
{hours h;minutes m;
}
Time Time::operator(const Time t) const
{Time sum;sum.minutes minutes t.minutes;sum.hours hours t.hours sum.minutes / 60;sum.minutes % 60;return sum;
}
Time Time::operator-(const Time t) const
{Time diff;int tot1, tot2;tot1 t.minutes 60 * t.hours;tot2 minutes 60 * hours;diff.minutes (tot2 - tot1) % 60;diff.hours (tot2 - tot1) / 60;return diff;
}
Time Time::operator*(double mult) const
{Time result;long totalminutes (minutes 60 * hours) * mult;result.minutes totalminutes % 60;result.hours totalminutes / 60;return result;
}
std::ostream operator(std::ostream os, const Time t)
{os t.hours hours, t.minutes minutes;return os;
}
源代码
#include iostream
#include mytime0.h
int main()
{using std::cout;using std::endl;Time aida(3, 35);Time tosca(2, 48);Time temp;cout Aida and Tosca:\n;cout aida ; tosca endl;temp aida tosca; // operator()cout Aida Tosca: temp endl;temp aida * 1.17; // member operator*()cout Aida*l.17: temp endl;cout 10*Tosca: 10 * tosca endl;return 0;
} 重载操作符作为成员函数还是非成员函数
两种加法操作符 对于两种加法 定义操作符时候只能使用其中一种格式不然会产生歧义。
重载矢量类 头文件
// vect.h--Vector class with,mode state
#ifndef VECTOR_H_
#define VECTOR_H_
#include iostream
namespace VECTOR
{class Vector{private:double x;double y;double mag;char mode;double ang;void set_mag();void set_ang();void set_x();void set_y();public:Vector();Vector(double nl, double n2, char form r);void set(double nl, double n2, char form r);~Vector();double xval() const { return x; } // report x valuedouble yval() const { return y; } // report y valuedouble magval() const { return mag; } // report magnitudedouble angval() const { return ang; } // report anglevoid polar_mode();void rect_mode(); // operator overloadingVector operator(const Vector b) const;Vector operator-(const Vector b) const;Vector operator-() const;Vector operator*(double n) const;friend Vector operator*(double n, const Vector a);friend std::ostream operator(std::ostream os, const Vector v);};} // end namespace VECTOR
#endif
源代码
// vector.cpp --methods for Vector class
#include cmath
#include vector.h
#include iostream
using std::atan2;
using std::cos;
using std::cout;
using std::sin;
namespace VECTOR
{const double Rad_to_deg 57.2957795130823;// private methods// calculates magnitude from x and yvoid Vector::set_mag(){mag sqrt(x * x y * y);}void Vector::set_ang(){if (x 0.0 y 0.0)ang 0.0;elseang atan2(y, x);}// set xfrom polar coordinatevoid Vector::set_x(){x mag * cos(ang);}// set y from polar coordinatevoid Vector::set_y(){y mag * sin(ang);}// public methodsVector::Vector() // default constructor{x y mag ang 0.0;mode r;}Vector::Vector(double n1, double n2, char form){mode form;if (form r){x n1;y n2;set_mag();set_ang();}else if (form p){mag n1;ang n2 / Rad_to_deg;set_x();set_y();}else{cout Incorrect 3rd argument to Vector()-- ;cout vector set to 0\n;x y mag ang 0.0;mode r;}}// set vector from rectangular coordinates if form is r(the// default)or else from polar coordinates if form is pvoid Vector::set(double n1, double n2, char form){mode form;if (form r){x n1;y n2;set_mag();set_ang();}else if (form p){mag n1;ang n2 / Rad_to_deg;set_x();set_y();}else{cout Incorrect 3rd argument to Vector()-- ;cout vector set to 0\n;x y mag ang 0.0;mode r;}}Vector::~Vector(){} // destructorvoid Vector::polar_mode() // set to polar mode{mode p;}void Vector::rect_mode() // set to rectangular mode{mode r;}// operator overloading// add two VectorsVector Vector::operator(const Vector b) const{return Vector(x b.x, y b.y);}// subtract Vector b from aVector Vector::operator-(const Vector b) const{return Vector(x - b.x, y - b.y);}// reverse sign of VectorVector Vector::operator-() const{return Vector(-x, -y);}// multiple vector by nVector Vector::operator*(double n) const{return Vector(n * x, n * y);}// friend methods// multiply n byVector aVector operator*(double n, const Vector a){return a * n;}// display rectangular coordinates if mode is r// else display polar coordinates if mode is pstd::ostream operator(std::ostream os, const Vector v){if (v.mode r){os (x,y)( v.x , v.y );}else if (v.mode p){os (m,a)( v.mag , v.ang * Rad_to_deg );}elseos Vector object mode is invalid;return os;}
} // end namespace VECTOR使用状态成员
状态成员 为Vector类重载操作符
加法
可以使用的加法无法使用极坐标 改进 采用的加法简单 乘法 友元内联函数来解决乘法交换顺序的问题 对已重载的操作符进行重载
减法二元操作符 取反一元操作符 模拟随机游走
// randwalk.cpp-…using the Vector class
// compile with the vect.cpp file
#include iostream
// rand(),srand()prototypes// time()prototype
#include cstdlib
#include ctime
#include vector.h
int main()
{using namespace std;using VECTOR::Vector;srand(time(0)); // seed random - number generatordouble direction;Vector step;Vector result(0.0, 0.0);unsigned long steps 0;double target;double dstep;cout Enter target distance(q to quit):;while (cin target){cout Enter step length:;if (!(cin dstep))break;while (result.magval() target){direction rand() % 360;step.set(dstep, direction, p);result result step;steps;}cout After steps steps, the subject has the following location : \n ;cout result endl;result.polar_mode();cout or\n result endl;cout Average outward distance per step result.magval() / steps endl;steps 0;result.set(0.0, 0.0);cout Enter target distance(qto quit):;}cout Bye!\n;return 0;
}类的自动转换和强制类型转换
内置类型转换
将一个标准类型变量的值赋给另一种标准类型的变量时如果这两种类型兼容则C自动将这个值转换为接收变量的类型。
例如 不兼容的类型不能自动转换 但是可以进行强制类型转换 类的转换
可以将类定义成与基本类型或另一个类相关使得从一种类型转换为另一种类型是有意义的。
stonewt.h
// stonewt.h --definition forStonewtCass
#ifndef STONEWT_H_
#define STONEWT_H_
class Stonewt
{
private:enum{Lbs_per_stn 14}; // pounds per stoneint stone; // whole stonesdouble pds_left; // fractional poundsdouble pounds; // entire weight in poundspublic:Stonewt(double lbs); // constructor for double poundsStonewt(int stn, double lbs); // constructor for stone,1bsStonewt(); // default constructor~Stonewt();void show_lbs() const;// show weight in pounds formatvoid show_stn() const;// show weight in stone format}
};#endif 源文件
#include iostream
using std::cout;
#include stonewt.h
// construct Stonewt object from double value
Stonewt::Stonewt(double lbs)
{stone int(lbs) / Lbs_per_stn; // integer divisionpds_left int(lbs) % Lbs_per_stn lbs - int(lbs);pounds lbs;
}// construct Stonewt obiect from stone,double values
Stonewt::Stonewt(int stn, double lbs)
{stone stn;pds_left lbs;pounds stn * Lbs_per_stn lbs;
}Stonewt::Stonewt() // default constructor,wt0
{stone pounds pds_left 0;
}Stonewt::~Stonewt()
// destructor
{
}
// show weight in stones
void Stonewt::show_stn() const
{cout stone stone, pds_left pounds\n;
}
// show weight in pounds
void Stonewt::show_lbs() const
{cout pounds pounds\n;
}对于函数 接受两个参数的不可以不能作为转换函数 避免意外的类型转换
最新的C实现新增了一个关键字(explicit)用来关闭这种自动特性。也就是说可以这样声明构造函数: 但是仍可以进行显示强制类型转换 隐式转换
不发生歧义下会进行二步转换
下面两条语句都首先将int转换为double然后使用 Stonewt(double)构造函数。 #include iostream
using std::cout;
#include stonewt.h
void display(const Stonewt st, int n);
int main()
{Stonewt pavarotti 260; // uses constructor to initializeStonewt wolfe(285.7); // same as Stonewt wolfe 285.7Stonewt taft(21.8);cout The tenor weighed ;pavarotti.show_stn();cout The detective weighed ;wolfe.show_stn();cout The President weighed ;taft.show_lbs();pavarotti 265.8;// uses constructor for conversiontaft 325; // same as taft Stonewt(325);cout After dinner,the tenor weighed ;pavarotti.show_stn();cout After dinner,the President weighed ;taft.show_lbs();display(taft, 2);cout The wrestler weighed even more,\n;display(422, 2);cout No stone left unearned\n;return 0;
}void display(const Stonewt st, int n)
{for (int i 0; i n; i){cout Wow!;st.show_stn();}
}二步转换 转换函数 如果定义了转换函数下列就是可行的 转换函数声明 更改上述代码
// stonewt.h --definition forStonewtCass
#ifndef STONEWT_H_
#define STONEWT_H_
class Stonewt
{
private:enum{Lbs_per_stn 14}; // pounds per stoneint stone; // whole stonesdouble pds_left; // fractional poundsdouble pounds; // entire weight in poundspublic:Stonewt(double lbs); // constructor for double poundsStonewt(int stn, double lbs); // constructor for stone,1bsStonewt(); // default constructor~Stonewt();void show_lbs() const;// show weight in pounds formatvoid show_stn() const;// show weight in stone format}// conversion functionsoperator int() const;operator double() const;
};#endif
源文件
#include iostream
using std::cout;
#include stonewt.h
// construct Stonewt object from double value
Stonewt::Stonewt(double lbs)
{stone int(lbs) / Lbs_per_stn; // integer divisionpds_left int(lbs) % Lbs_per_stn lbs - int(lbs);pounds lbs;
}// construct Stonewt obiect from stone,double values
Stonewt::Stonewt(int stn, double lbs)
{stone stn;pds_left lbs;pounds stn * Lbs_per_stn lbs;
}Stonewt::Stonewt() // default constructor,wt0
{stone pounds pds_left 0;
}Stonewt::~Stonewt()
// destructor
{
}
// show weight in stones
void Stonewt::show_stn() const
{cout stone stone, pds_left pounds\n;
}
// show weight in pounds
void Stonewt::show_lbs() const
{cout pounds pounds\n;
}Stonewt::operator double() const
{return pounds;
}
Stonewt::operator int() const
{return int(pounds 0.5);
}
源文件
#include iostream
#include stonewt.h
int main()
{using std::cout;Stonewt poppins(9, 2.8); // 9 stone,2.8 poundsdouble p_wt poppins; // implicit conversioncout Convert to double ;cout Poppins: p_wt pounds.\n;cout Convert to int ;cout Poppins: int(poppins) pounds.\n;return 0;
}最好使用显式转换而避免隐式转换。关键字 explicit 不能用于转换函数但只需用一个功能相同的非转换函数替换该转换数即可但仅在被显式地调用时该函数才会执行。也就是说可以将: 转换函数和友元函数
重载加法的两种方式
成员函数 友元函数 下列合理 进行状态转换 只有友元函数允许 原因