类(二):运算符重载、友元函数、类的自动转换和类的强制类型转换、转换函数、

1.运算符重载

格式:

operator(argument-list)

如:

Time operator+(const Time &)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;
}

重载的运算符不必是类的成员函数,但必须至少有一个操作数是用户定义的类型,防止用户为标准类型重载运算符。p315

运算符左侧的对象时调用类的成员重载函数的对象,若是使用类成员函数来编写重载函数,则调用该重载函数的类作为该函数的隐式参数;运算符右边的对象是作为参数被传递的对象。p314

 

2.友元函数:让函数称为类的友元,可以赋予该函数与类的成员函数相同的访问权限。p319

在为类重载二元运算符时,第一个操作数不是该类,但重载时要访问该类的私有函数成员。

对于非成员重载运算符函数来说,运算符表达式左边的操作数对应于重载函数的第一个参数(不能作为隐式参数,因为重载函数不是类的成员函数),运算符表达式右边的操作数对应于运算符函数的第二个操作数。

创建友元时

1)需将友元函数原型放入类的声明中,并在原型声明前加上关键字friend:

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

注意,该函数不是类的成员函数,所以不能用成员运算符来调用;但因为其是友元函数,所以它可以直接访问其原型所在类的私有成员;

2)编写函数定义:其不是类的成员函数,因此不需要使用Time::限定符;此外,不要在定义中使用friend关键字。

Time operator* (double m, const Time & t)
{
    Time result;
    long totalminutes = t.hours * m * 60 + t.minutes * m;
    result.hours = totalminutes / 60;
    result.minutes = totalminutes % 60;
    return result;
}

 

常用的友元函数:重载<<运算符 p320

如果想使用

cout<<trip; //Time trip;

来输出trip的时间,而不适用Time的成员函数show(),则需要重载<<运算符;

如果将<<重载为类的成员函数,则运算符左侧的操作数必须为Time类的对象(即为<<运算符的重载函数的调用对象),即:

trip<<cout; //cout是一个ostream对象

因此我们使用友元函数重载<<运算符:

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

注意,调用cout<<trip;应使用cout对象本身,而不是它的拷贝,因此该函数按引用(而不是值)来传递该对象。

但注意,上述函数不能:

cout<<trip1<<trip2;

其等同于

(cout<<trip1)<<trip2;

但是我们上述定义的<<重载函数并没有返回值,因此括号内并不会由一个值,因此需要<<的第二种重载版本:

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

 

给一个复数类Plural,重载>>运算符时,第一个参数为istream&,第二个参数一定要为 Plural& 类型,而不能是Plural类型:

std::istream & operator>>(std::istream & is, Plural & p)
{
    double a,b;
    is>>a>>b;
    p.real = a;
    p.image = b;
    return is;
}

如上图所示,若第二个参数为Plural类型,则会将a,b赋给临时变量p,而调用该函数的与形参Plural p对应的实参不会有任何改变;

 

3.类的自动转换和类的强制类型转换 p334

1)接收一个参数的构造函数允许使用赋值语法将对象初始化为一个值

类Stonewt

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
};

接收一个参数的构造函数允许使用赋值语法将对象初始化为一个值;

因此

Stonewt(double lbs);

上述构造函数可以将double类型的值转换为Stonewt类型。

所以,下列语句是可行的:

Stonewt myCat;
myCat = 19.6;

该程序使用构造函数Stonewt(double)来创建一个临时的Stonewt对象,并将19.6作为初始化值。随后,采用逐成员赋值的方式将该临时对象中的内容复制到myCat中。这一过程称为隐式转换,因为其是自动运行的,不需要显式强制类型转换。

使用explicit可以关闭这种隐式转换:

explicit Stonewt(double lbs); // no implicit conversions allowed

但仍然允许显式转换:

Stonewt myCat;
myCat = 19.6; // not allowed
myCat = Stonewt(19.6); //ok
myCat = (Stonewt)19.6; //ok

 

可以使用一个例子来辨析友元函数、类成员函数以及接收一个参数的构造函数允许使用赋值语句将类对象初始化一个值。

// 
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
class Time{
    int a;
public:
    Time();
    Time(int);//接收一个参数的构造函数允许使用赋值语法将对象初始化为一个值
    void set(int a){this->a = a;}
    int operator+(Time t);
    // friend int operator+(Time, int);
    // friend int operator+(int, Time);
    // friend int operator+(Time, Time);
};
int main()
{    
    Time t1;
    Time t2;
    t1.set(1);
    t2.set(2);
    int q = t1 + 2;//接收一个参数的构造函数允许使用赋值语法将对象初始化为一个值,这句话先调用了Time(int);的构造函数,再调用了+重载函数
    cout<<q<<endl;
}
Time::Time()
{
}
Time::Time(int d)
{
    a = d;
}

int Time::operator+(Time t){
    a = a + t.a;
    cout<<"calls member function."<<endl;
    return a;
}

// int operator+(Time t, int a)
// {
//     cout<<"calls friend function Time + int."<<endl;
//     return t.a + a;
// }

// int operator+(int a, Time t)
// {
//     cout<<"calls friend function int + Time."<<endl;
//     return a + t.a;
// }

// int operator+(Time a, Time b)
// {
//     cout<<"calls friend function Time + Time."<<endl;
//     return a.a + b.a;
// }

 

2)转换函数

使用上述方法,构造函数仅允许从某种类型到类类型的转换;

如果要进行相反的转换,则需要特殊的c++运算符函数:转换函数。转换函数是用户定义的强制类型转换。

转换函数的创建:

opeartor typeName();

其中:

a.转换函数必须是类方法

b.转换函数不能指定返回类型

c.转换函数不能有参数

d.转换函数必须返回转换后的值(虽然没有返回类型)

例子:

class Stonewt
{
private:
    enum {Lbs_per_stn = 14};      
    int stone;                    
    double pds_left;              
    double pounds;                
public:
    Stonewt(double lbs);          
    Stonewt(int stn, double lbs); 
    Stonewt();                    
    ~Stonewt();
    void show_lbs() const;        
    void show_stn() const;        
// conversion functions 转换函数
    operator int() const;
    operator double() const;
};

转换函数的定义:

Stonewt::operator int() const //类方法,,无参数,无返回值
{

    return (int)pounds;

}

Stonewt::operator double()const
{
    return pounds; 
}

转换函数的使用:

Stonewt wolfe(285.7);
double host = double (wolfe);
double thinker = (double) wolfe; //显式使用转换函数

Stonewt wells(20, 3);
double star = wells; //转换函数的隐式使用

同样的,c++11中,可以使用explicit关键字来避免隐式转换:

explicit operator int() const;
explicit operator double() const;

 

posted @ 2022-03-27 22:31  SanFranciscoo  阅读(63)  评论(0编辑  收藏  举报