C++ Primer Plus 学习笔记(八)

第11章 使用类

1. 运算符重载

class Time
{
private:
    int h;
    int m;
public:
    Time operator+(const Time & t) const;  // + 重载
};

Time Time::operator+(const Time & t) const  // 定义
{
    ...
}
int main()
{
    Time t1, t2;
    Time total;
    total = t1 + t2;  // 调用重载的 + 
    total = t1.operator+(t2);  // 与上面等价
    return 0;
}

成员函数的重载运算符的左操作数是调用运算符的对象,右操作数是参数。非成员函数的重载运算符,左操作数是第一个参数,右操作数是第二个参数。

运算符重载不必是类的成员函数,且重载的运算符的操作数至少一个是用户定义的类型(防止用户重载标准类型下的运算符),重载的运算符不能违反它原来的句法规则(即二元运算符不能重载成一元,优先级也不能改变),不能创建新的运算符。

不能重载的运算符:

运算符说明
sizeofsizeof运算符
.成员运算符
.*成员指针运算符
::作用域运算符
?:条件运算符
typeid一个RTTI运算符
const_cast强制类型转换运算符
dynamic_cast强制类型转换运算符
reinterpret_cast强制类型转换运算符
static_cast强制类型转换运算符

只能通过成员函数重载的运算符:

运算符说明  
=赋值运算符
()函数调用运算符
[ ]下标运算符
->通过指针访问类成员运算符

重载运算符最好不改变原运算符的意义,例如 * 运算符,不要重载成交换两个类对象的数据成员。

一元运算符重载:

class D
{
private:
    int a;
    int b;
public:
    D& operator-();  //  取负运算符,运算符在操作数前边,无参数
    D& operator++();  // 前置递增重载,运算符在操作数前边,无参数
    D operator++(int);  // 后置递增重载,运算符在操作数后面,重载函数的参数有个int
};

D& D::operator-()
{
    a = -a;
    b = -b;
    return *this;
}
D& D::operator++()
{
    ++a;
    ++b;
    return *this;
}

D D::operator(int)
{
    ++(*this);
    return *this;
}

2. 友元函数

友元函数在类中声明,友元函数中的该类的参数,能访问类的私有成员。友元函数不是类的成员函数,不能用成员运算符 “ . ” 调用,作用域也不在类内。

友元函数原型要在类中声明,声明前面加上关键字 friend:

friend Time operator*(double m, const Time & t);  // 要在类中声明

Time operator*(double mul, const Time & t)  // 定义时不加 Time::  因为不是类的成员函数  
{                                           // 不加关键字 friend
    Time result;
    long totalm = t.h * 60 * mul +  t.m * mul;  // 调用了参数t的私有数据m,h
    result.h = totalm / 60;
    result.m = totalm % 60;
    return result;
}

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

假定有一个类,构造函数为对象的三个私有数据赋值:

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

可以这样强制转换:

Stonewt maCat;  // 调用默认构造函数
naCat = 19.6;  // 使用构造函数Stonewt(double)转换19.6

程序会调用构造函数 Stonewt(double) 创建一个临时对象,将19.6作为初始化值,然后逐成员将临时对象的内容复制到 myCat 中。这个过程叫隐式转换。只有接受一个参数的构造函数才能作为转换函数。Stonewt 类中,接受两个参数的构造函数,若 double 形参是默认参数的,则也可以作为转换函数,来匹配 int 型转换。

可以通过在声明构造函数时,添加关键字 explicit 关闭隐式转换,但还是可以强制转换:

explicit Stonewt(double lbs);
Stonewt myCat;
myCat = 19.6;  // 非法
myCat = Stonewt(19.6);  // 合法
myCat = (Stonewt) 19.6;  // 合法

若没有explicit 关键字,则隐式转换还可以在以下情况自动执行:将 Stonewt 对象初始化 double 值;将 double 值赋给 Stonewt 对象;将 double 值传递给接受 Stonewt 类型参数的函数时;返回值被声明为 Stonewt 的函数试图返回 double 值时;在上述任意一种情况下,使用可转换为 double 类型的内置类型时。

最后一条的意思是:函数的返回值是 double 类型,但是函数 return 了 Stonewt 类型对象。

Stonewt Jumbo(7000);  // 将int转换为double后,使用Stonewt(double)
Jumbo = 7300;  // 将int转换为double后,使用Stonewt(double)

只有不存在二义性时才会进行这种而不转换,即,当还有一个构造函数 Stonewt(long) 时,int 可以转换成 double 或 long,这种情况下会报错。

也可以进行相反的转换,通过 c++ 特殊的运算符函数—转换函数:

operator typeName();

typeName 是要转换的类型。转换函数必须是类方法;转换函数不能指定返回类型;转换函数不能有参数。可以在 Stonewt 类声明中添加两个转换函数:

operator int() const;  // 声明
operator double() const;  // 声明

Stonewt::operator int() const  // 定义,转换函数是类方法
{
    return int (pounds + 0.5);
}

使用转换函数时:

Stonewt pop(9, 2.8);  // 调用构造函数Stonewt(int, double)
double p_wt = pop;  // 隐式调用转换函数
std::cout << int (pop) << std::endl;  // 显示调用转换函数

与上面提到的类似,当不存在二义性时,转换也会自动执行。

强制转换:

long go = (double) pop;
long go = int (pop);     // 这两种形式的强制转换,都会调用转换函数

转换函数也可以通过 explic 关键字关闭隐式转换。

可以不定义转换函数,而自定义一个类方法来实现类型转换,这样会省却一些隐式类型转换造成的麻烦。

类的类型转换总结:

只有一个参数的构造函数,可以用来将该参数类型的值转换成类对象,使用 explicit 关键字可以关闭隐式转换;

特殊的类的成员函数—转换函数,可以将类转换成其他类型,转换函数没有返回类型,没有参数,同样可以用 explicit 关闭隐式转换。

在类型转换中,函数参数存在转换条件时才会执行,调用对象不会被转换。有 + 运算符重载:

Stonewt operator+(const Stonewt & s) const;  // 成员函数重载

// 或者友元函数的形式,只能存一
friend Stonewt operator+(const Stonewt &s1, const Stonewt &s2)
Stonewt JennySt(9, 12);
double BennySt(12, 8);
Stonewt total;
total = JennySt + BennySt;  // total = JenntSt.operator+(BennySt)
                            // 或者 total = operator+(JennySt, BennySt)

这两种情况,由于BenntSt 是参数,可以通过构造函数将 BenntSt 隐式转换成类对象。如果类方法中,还有转换函数 operator double();将会造成二义性而报错。

total = BennySt + JennySt;  // 成员函数:total = BennySt.operator+(JennySt);
                            // 友元函数:total = operator+(BennySt, JennySt);

这种情况,友元函数时,BennySt 是参数,可以通过构造函数将其转换为类对象,而成员函数时,BennySt 不是参数,无法转换,会报错。

解决这种问题的方法,一是在重载加法时,采用友元函数;或者在成员函数重载运算符的基础上,再重载一个运算符函数,来符合加法操作数顺序问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值