C++类特性

运算符重载

C++根据操作数的数目和类型决定使用哪种定义。

运算符的定义和方法的定义类似。

类的定义:

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;//声明和方法类似
    void show() 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;
}

使用:

total=coding.oprator+(fixing);
total=coding+fixing;//alse ok
t4=t1+t2+t3;//also ok

重载限制:

1.重载后的运算符必须至少有一个操作数是用户定义的类型。

2.使用运算符时不能违反运算符原来的句法规则。例如,不能将%重载成使用一个操作数。同样,不能修改运算符的优先级,重载的+将和原来的+拥有相同的优先级。

3.不能创建新运算符。

4.不能重载下列运算符:

sizeof
.
.*
::
?:
typeid
const_cast
dynamic_cast
reinterpret_cast
static_cast

5.下列运算符只能通过成员函数进行重载:

=
()
[]
->

除此以外,大多数运算符都可以通过成员函数和非成员函数进行重载。

友元

友元有三种:友元函数、友元类、友元成员函数。

通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。

如果要定义非成员函数重载运算符,会导致非成员函数无法直接访问类的私有数据这个问题。因此我们使用友元函数,使得这种函数可以访问类的私有成员。

创建友元函数需要将原型放在类声明中,并加上关键字friend:

friend Time operator*(double m,const Time & t);

虽然operator*函数是在类声明中声明的,但它不是成员函数,不能使用成员运算符来调用。它与成员函数的访问权限相同。

编写函数定义时,不用加friend关键字和::限定符:

Time operator*(double m,const Time &t){
...
}

因此,如果要为类重载运算符,并将非类的项作为其第一个操作数,可以用友元函数来反转操作数的顺序。

如果要让Time类知道使用cout,即使用如下的语句:

cout<<trip;

但是如果使用Time成员函数来重载<<,Time必须是第一个操作数,即:

trip<<cout;

这会令人迷惑。但是使用友元函数,可以这么做:

void operator<<(ostream & os,const Time &t){
  os<<t.hour<<"hours,"<<t.minutes<<"minites";
}

这样就可以使用一开始的那句语句。但是这还不够。

cout<<"Trip time: "<<trip<<" (Tuesday)\n";
((cout<<"Trip time: ")<<trip)<<" (Tuesday)\n";//两句话等价,但是之前重载的<<运算符返回的是void,因此最后一个<<无法实现。

因此只要做如下修改:

ostream & operator<<(ostream & os,const Time &t){
  os<<t.hour<<"hours,"<<t.minutes<<"minites";
  return os;
}

有趣的是,这个版本的重载还可用于文件输出,因为ofstream继承ostream。

类的自动转换和强制类型转换

将一个标准类型的值赋给另一种标准类型的变量时,如果这两种类型兼容,则C++自动将这个值转换为接收变量的类型,这可能导致降低精度。

long count=8;
double time=11;
int side=3.33;//ok,丢失精度
int *p=10;//no
int *p=(int *)10;//ok

可以将类定义成与基本类型或者另一个类相关,使得类型转换有意义。

倘若有这样的构造函数:

Stonewt(double lbs);

那么,它实质上是通过一个double值获得一个Stonewt类型的对象。

于是,我们可以写这样的代码:

Stonewt mycat;
myCat=19.6;//隐式转换

只有接受一个参数的构造函数才能这么做。

但是,如果提供了默认值:

Stonewt(int stn,double lbs=0);

这便可以用来转换int,但是这不好。使用explicit可以关闭隐式转换。

explicit Stone(double lbs);

但是仍然允许显式转换。

Stonewt mycat;
mycat=19.6;//no
mycat=Stonewt(19.6);//ok
mycat=(Stonewt) 19.6;//ok
mycat=7000;//ok,7000->7000.0

最后一句,int将被转换成double来执行。但是如果同时有double和long的构造函数,这将引起矛盾,编译器将拒绝编译。

如果没有使用explicit,还可以允许下面的隐式转换:
(1)将Stonewt对象初始化为double时
(2)将double赋给Stonewt时
(3)将double值传递给接受Stonewt参数的函数时
(4)返回值声明为Stonewt的函数试图返回double值时
(5)上述任意一种情况中,使用可转换为double类型的内置类型时

我们可以利用转换函数,就可以实现逆向的转换:

Stonewt wolf(285.7);
double host=wolf;//隐式,右边也可以是double(wolf)或者(double)wolf
Stonewt dog(20,3);
double star=dog;//只要写了正确的转换函数,这也可以完成

转换函数有如下要求:
(1)必须是类方法
(2)不能指定返回类型
(3)不能有参数
只要将下面的原型添加到类声明中:

oprator int();
oprator double() const;

如果在程序中那么用:

cout<<poppins;//no,不知道poppins应该用int还是double
long gone=poppins;//no,int和double都可以赋给long
//如果程序只定义了一个转换函数,则可行
long gone=(double) poppins;
long gone=int (poppins);

在重载加法之后:

Stonewt jenny(9,12);
double penny=146.0;
Stone total=penny+jenny;
//如果使用了Stone opetaror+(const Stone & st1,const Stone &st2),可行
//如果使用了Stone Stonewt::operator+(const Stone&st)const,不可行
//程序不会将penny转化成一个对象,程序只会转化成员函数参数

猜你喜欢

转载自blog.csdn.net/dxy18861848756/article/details/113747788