一、运算符重载。
运算符重载允许将标准C++运算符(=和+)用于类对象。运符重载是一种形式的C++多态。函数重载或函数多态旨在能够用同名的函数来完成相同的基本操作,即使这种操作被用于不同的数据类型。运算符重载将重载的概念扩展到运算符上,允许赋予C++运算符多种含义。将*运算符用于地址,将得到存储载这个地址中的值;但将它用于两个数字之间,得到的是它们的乘积。C++根据操作数的数目和类型来决定采用哪种操作。
二、计算时间:一个运算符重载示例
时间是小时与分钟,在相加的单位(小时与分钟的混合)与内置类型不匹配。
// 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);
const Time Sum(const Time & t) const;//Time operator+(const Time & t) const;
void Show() const;
};
#endif
Time类提供了用于调整和重新设置时间、显示时间、将两个时间相加的方法。
// mytime0.cpp -- implementing 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;
}
const Time Time::Sum(const Time & t) const //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";
}
Sum函数中,参数是引用,但返回值类型却不是引用。将蚕食声明为引用的目的是为了提高效率。
// usetime0.cpp -- using the first draft of the Time class
// compile usetime0.cpp and mytime0.cpp together
#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);
//total = coding + fixing;
cout << "coding.Sum(fixing) = ";
//cout << "coding + fixing = "; n
total.Show();
cout << endl;
// std::cin.get();
return 0;
}
1.添加加法运算符
将Time类转换为重载的加法运算符很容易,只要将Sum()名称改为operator+()即可,并将结果用作方法名即可。如果像Sum()那样来调用operator+():
total = coding.operator+(fixing)
但将该方法命令为operator+()后,也可以使用运算符表示法:
total = coding + fixing
这两种表示法都将调用operator+()方法。在运算符表示法中,运算符左侧的对象(coding)是调用对象,运算符右边的对象(fixing)是作为参数被传递的对象。
2.重载限制
- 重载的运算符不必是成员函数,但必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。
- 使用运算符是不能违反原来的语句规则。
- 不能创建新的运算符
- 不能重载这些运算符:sizeof、.(成员运算符)、.*(成员指针运算符)、::(作用域解析运算符)、?:(条件运算符)、typeid(一个RTTI运算符)、const_cast(强制类型转换运算符)、dynamic_cast(强制类型转换运算符)、reinterpret_cast(强制类型转换运算符)、static(强制类型转换运算符)。
- 大多数运算符都可以通过成员或者非成员函数进行重载,但以下运算符只能通过成员函数进行重载。=(赋值运算符)、()(函数调用运算符)、[] (下标运算符)、->(通过指针访问类成员的运算符)
3.其他重载运算符
可能要将两个时间相减或将时间乘以一个因子,这需要重载减法或者乘法运算符。即operator-()和operator*()。将原型添加到类声明中:
Time operator-(const Time & t)const;
Time operator*(double n)const;
// mytime2.h -- Time class after operator overloading
#ifndef MYTIME2_H_
#define MYTIME2_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 operator+(const Time & t) const;
Time operator-(const Time & t) const;
Time operator*(double n) const;
void Show() const;
};
#endif
// mytime2.cpp -- implementing Time methods
#include <iostream>
#include "mytime2.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 = hours * mult * 60 + minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
void Time::Show() const
{
std::cout << hours << " hours, " << minutes << " minutes";
}
// usetime2.cpp -- using the third draft of the Time class
// compile usetime2.cpp and mytime2.cpp together
#include <iostream>
#include "mytime2.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 work time = ";
total = weeding + waxing; // use operator+()
total.Show();
cout << endl;
diff = weeding - waxing; // use operator-()
cout << "weeding time - waxing time = ";
diff.Show();
cout << endl;
adjusted = total * 1.5; // use operator+()
cout << "adjusted work time = ";
adjusted.Show();
cout << endl;
// std::cin.get();
return 0;
}
三、有元
C++控制对类对象私有部分的访问。通常,公有类方法提供唯一的访问途径,但是有时候这种限制太严格,以致于不适合特定的编程问题。在这种情况下,C++提供了另外一种形式的访问权限:友元。
有元有3种:
-
友元函数
-
友元类
-
友元成员函数
为何需要友元?因为在为类重载二元运算符时(带两个参数的运算符)常常需要友元。
在前面的Time示例中,重载的乘法运算符与其他两种重载运算符的差别在于,它使用了两种不同的类型。也就是说加法和减法运算符都结合两个Time值,而乘法运算符将一个Time值与一个double值结合在一起。这限制了该运算符的使用方式。记住,左侧的操作数是调用对象。
A = B * 2.75 被转换成成员函数调用
A = B.operator * (2.75)
但是如果换成
A = 2.75B的话,因为左侧的操作数应该是调用对象,但2.75不是对象。因此编译器不能使用成员函数调用来替换该表达式。
解决这一问题,一就是只用A= B2.75而不能写成A= 2.75* B,这是一种对服务器友好-客户警惕的解决方案,与OOP无关。再者,可以使用非成员函数(大多数运算符都可以通过成员或非成员函数来重载)来解决。非成员函数不是由对象调用的,它使用的所有值包括对象都是显示参数,编译器将上述的表达式与非成员函数调用匹配A=operator*(2.75,B)。该函数的原型:Time operator * (double m , const Time & t)
对于非成员重载运算符函数来说,运算符表达式左边的操作数对应于运算符函数的第一个参数,运算符表达式右边的操作数对应于运算符函数的第二个参数,而原来的成员函数则按相反的顺序处理操作数,也就是说double的值乘以Time值。
有一类特殊的非成员函数可以访问类的私有成员,它们被称为友元函数。
1.创建友元
创建友元函数的第一步是将其原型放在声明中,并在原型声明前加上关键字friend:
friend Time operator * (double m ,const Time & t )
该原型意味着: -
虽然operator*()函数是载类声明中声明的,但他不是成员函数,因此不能使用成员运算符来调用;
-
虽然operator*()函数不是成员函数,但它与成员函数的访问权限相同。
第二步是编写函数定义。因为它不是成员函数,所以不要使用Time::限定符。另外,不要再定义中使用关键字friend。
Time operator*(double m,const Time & t)
{
Time result;
long totalminutes = hours * mult * 60 + minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
类的友元是非成员函数,其访问权限与成员函数相同。
四、重载运算符:作为成员函数还是非成员函数
一般来说,非成员函数应是友元函数,这样它才能直接访问类的私有数据。
加法运算符需要两个操作数。对于成员函数版本来说,一个操作数通过this指针隐式地传递,另一个操作数作为函数参数显示地传递;对于友元版本来说,两个操作数都作为参数来传递。
这两个原型都与表达式T2+T3匹配,其中T2和T3都是Time类型对象。编译器将下面的语句:
T1 = T2+T3
转换成下面两个得任何一个:
T1 = T2.operator+(T3)
T1 = operator+(T2,T3)
在定义运算符,必须选择其中的一种格式,而不能同时选择这两种格式。因为这两种格式都与同一个表达式匹配,同时定义这两种格式将被视为二义性错误,导致编译错误。
五、再谈重载:一个矢量类
矢量重载运算符,无法使用一个数来表示矢量,因此应该创建一个类来表示矢量。
描述二维矢量只需要两个数,但可以选择到底使用哪两个数:
- 可以用大小(长度)和方向(角度)描述矢量;
- 可以用分量x和y表示矢量。
#ifndef VECTOR_H_
#define VECTOR_H_
#include <iostream>
namespace VECTOR
{
class Vector
{
public:
enum Mode {RECT, POL};
// RECT for rectangular, POL for Polar modes
private:
double x; // horizontal value
double y; // vertical value
double mag; // length of vector
double ang; // direction of vector in degrees
Mode mode; // RECT or POL
// private methods for setting values
void set_mag();
void set_ang();
void set_x();
void set_y();
public:
Vector();
Vector(double n1, double n2, Mode form = RECT);
void reset(double n1, double n2, Mode form = RECT);
~Vector();
double xval() const {return x;} // report x value
double yval() const {return y;} // report y value
double magval() const {return mag;} // report magnitude
double angval() const {return ang;} // report angle
void polar_mode(); // set mode to POL
void rect_mode(); // set mode to RECT
// operator overloading
Vector operator+(const Vector & b) const;
Vector operator-(const Vector & b) const;
Vector operator-() const;
Vector operator*(double n) const;
// friends
friend Vector operator*(double n, const Vector & a);
friend std::ostream & operator<<(std::ostream & os, const Vector & v);
};
} // end namespace VECTOR
#endif
六、类的自动转换和强制类型转换
将一个标准型变量的值赋给另一种标准类型的变量时,如果这两种类型兼容,则C++自动将这个值转换为接收变量的类型。
long count = 8;//将int 8转换成long
C++语言不自动转换不兼容的类型。例如,左边是指针类型,而右边是数字。
int * p = 10。
强制类型转换的话,
int * p = (int *)10
可以将类定义成基本类型或另一类相关,使得从一种类型转换为另一种类型是有意义的。在这种情况下,可以指示C++自动转换或者通过强类型转换来完成。
// stonewt.h -- definition for the Stonewt class
#ifndef STONEWT_H_
#define STONEWT_H_
class Stonewt
{
private:
enum {Lbs_per_stn = 14}; // pounds per stone
int stone; // whole stones
double pds_left; // fractional pounds
double pounds; // entire weight in pounds
public:
Stonewt(double lbs); // constructor for double pounds
Stonewt(int stn, double lbs); // constructor for stone, lbs
Stonewt(); // default constructor
~Stonewt();
void show_lbs() const; // show weight in pounds format
void show_stn() const; // show weight in stone format
};
#endif
// stonewt.cpp -- Stonewt methods
#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 division
pds_left = int (lbs) % Lbs_per_stn + lbs - int(lbs);
pounds = lbs;
}
// construct Stonewt object 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, wt = 0
{
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";
}
// stone.cpp -- user-defined conversions
// compile with stonewt.cpp
#include <iostream>
using std::cout;
#include "stonewt.h"
void display(const Stonewt & st, int n);
int main()
{
Stonewt incognito = 275; // uses constructor to initialize
Stonewt wolfe(285.7); // same as Stonewt wolfe = 285.7;
Stonewt taft(21, 8);
cout << "The celebrity weighed ";
incognito.show_stn();
cout << "The detective weighed ";
wolfe.show_stn();
cout << "The President weighed ";
taft.show_lbs();
incognito = 276.8; // uses constructor for conversion
taft = 325; // same as taft = Stonewt(325);
cout << "After dinner, the celebrity weighed ";
incognito.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";
// std::cin.get();
return 0;
}
void display(const Stonewt & st, int n)
{
for (int i = 0; i < n; i++)
{
cout << "Wow! ";
st.show_stn();
}
}