c++ 第十天使用类

一 、运算符重载 运算符重载是一种形式的c++多态,将重载的概念扩展到运算符上. c++允许将运算符重载扩展到用户定义类型.在实际使用的过程中根据操作数的数目和类型来决定使用哪种运算符定义.
要重载运算符,需使用运算符函数: operator op(‘参数列表’); op必须是有效的c++运算符. ( [ ] 是有效的,数组索引运算符)
//使用情景/*重载 + 运算符 将两个Saleperson对象的销售额相加*/Salepersopn sid,sara;double num = sid + sara;// double num = si.operator+(sara) ; 隐式的调用sid (是sid 对象调用了方法)显式的调用sara(它作为参数被传递) 不要返回指向局部变量或者临时对象的引用.函数执行完毕局部变量或者临时对象将消失,引用指向不存在的数据
二、重载的限制

  • 重载后的运算符必须有一个操作数是用户自定义类型,防止用户为标准类型重载运算符
  • 使用运算符不能违反运算符原来的句法规则. 例如: 不能将 % 重载成只使用一个操作数.
  • 不能修改运算符的优先级
  • 不能创建新的运算符
  • 不能重载以下运算符
    • sizeof:
    • . 成员运算符
      • 成员指针运算符
    • :: 作用域解析运算符
    • ?: 三元运算符
    • typeid 一个RTTI运算符
    • 类型转换运算符 : const_cast dynamic_cast reinterpret_cast static_cast
  • 以下运算符只能通过成员函数进行重载
    • = 赋值运算符
    • () 函数调用运算符
    • 下标运算符
【c++ 第十天使用类】//自定义时间类 mytime2.h#ifndef MYTIME2_H_#define MYTIME2_H_class Time{private: int hours; int minutes;public: Time(); Time(int hours,int minutes = 0); void addMin(int m); void addHour(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 #include #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::addHour(int h){ hours += h;}void Time::reset(int h,int m){ hours = h; minutes = m;}Time Time::operatot+(const Time &t) const { Time sum; sum.miutes = minutes + t.minutes; sum.hours = hours + t.hours; sum.minutes %= 60; return sum;}Time Time::operatot-(const Time &t) const { Time diff; int total1,total2; total1 = t.minutes + 60 * t.hours; total2 = minutes + 60 * hours; diff.minutes = (total1 - total2) % 60; diff.hours= (total1 - total2) / 60; return diff;//返回diff的拷贝//reurn &diff 返回的是diff的引用,diff在函数结束后释放 ,引用指向一个无用的值}Time Time::operatot*(double n) const { Time res; long total = hours*n*60 + minutes * n; res.hours = total/60; res.minutes = total%60; return res;}void Time::show(){ std::out << hours <<"hours,"< 三、友元
  • 友元函数
  • 友元类
  • 友元成员函数
通过让函数称为类的友元,可以赋予该函数与类的成员函数相同的访问权限.
运用场景:
在上述代码中.重载的加法和减法都结合了两个Time值,而重载的乘法将一个Time和一个double值结合,这限制了该运算符的使用方式;
Time B (1,40);Time A = B *2.75;//将被转换成 : Time A = B.operator(2.75), //反之: Time A = 2.75 * B; 编译不通过 //这时候就需要非成员函数(大多数重载运算符都可以通过成员函数或者非成员函数来重载)Time operator*(double n, const Time &t);//这引出一个新的问题: 非成员函数不能访问类的私有数据. 这时引入友元函数. 友元函数的声明与定义 friend Time operator*(double n , const Time &t);
  • 虽然operator * ()函数是在类中声明的,但它不是成员函数,因此不能用成员运算符来调用
  • 虽然它不是成员函数,但它的访问权限和成员函数相同
  • 因为不是成员函数,所以不需要Time:: 限定符,定义中不要再使用关键字friend
定义:
Time operator(double n ,const Time &t){ Time res; long total = t.hours*n*60 + t.minutes * n; res.hours = total/60; res.minutes = total%60; return res;} 友元和oop的讨论 友元是否违反了OOP数据隐藏的原则.
  • 这个观点是片面的,应该将友元当作类的扩展接口的组成部分.
  • 类声明决定了那一个函数是友元函数,因此类声明仍然控制了哪些数据可以访问私有数据.
  • 类方法和友元只是表达接口的两种不同机制
friend Time operator*(double n , const Time &t);也可以改为非友元函数
Time operator(double n ,const Time &t){ return t * n;} 这个版本将Time t作为一个整体使用,让成员函数处理私有值,因此不必是友元. 但是使用 friend Time operator*(double n , const Time &t)的好处在于:
  • 它将作为正式类的接口组成部分
  • 如果以后发现需要函数直接访问类的私有值,只需修改函数定义即可,而不必修改原型
常用的友元 : 重载 << 运算符 一个很有用的类 特性,可以对 << 进行重载,使之能与cout 一起显示对象的内容. 像这样输出一个Time 类的小时和分钟
cout << A;
首先排除 成员函数的重载方式. cout 是一个ostream 对象, 所以cout<< 相当于 ostream.operator <<( … );
如果使用成员函数的重载方式,那么调用方式就应该为 A << cout; 相当于 A.operator << ( ostream &os); 这样会令人迷惑
所以应该使用非成员函数的重载运算符.
void operator << (ostream & os , const Time &t){ os << hours <<"hours,"< 为了可以连续输出, cout << “string” << A < ostream & operator << (ostream & os , const Time &t){ os << hours <<"hours,"< 类的自动类型转换和强制类型转换 可以 将 类 定义成与基本 类型或另一个 类相关,使得从一种类型转换为另一种 类型是有意义的 。在这种情况下,程序员可以指示 C++ 如何自动进行转换,或通过强制类型转换来完成 。
#ifndef STONEWT_H_#define STONEWT_H_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()s};#endif 在 C++ 中,接受一个参数的构造函数为将类型与该参数相同的值转换为类提供了蓝图 。上述Stonewt(double lbs); 构造函数用于将double类型的值转换为Stonewt类型
/*程序将使用构造函数Stonewt(double)来创建一个临时的Stonewt 对象,并将 19.6作为初始化值 。随后,采用逐成员赋值方式将该临时对象的内容复制到myCat 中 。这一过程称为隐式转换,因为它是自动进行的,而不需要显式强制类型转换 。*/Stonewt myCat;myCat = 19.6;//只有接受一个参数?构造函数才能作为转换函数,含有两个参数的构造函数,第二个参数赋了默认值,也可用于转换. explict 关键子用于关闭这种隐式转换. 但是仍然允许显式转换.
转换函数 可以通过构造函数实现值类型到类类型的转换,反之则需要转换函数.
operator typeName();
  • 转换函数必须是类方法
  • 转换函数不能指定返回类型
  • 转换函数不能有参数
//转换未double类型的函数的原型::operator double();//转换未int类型的函数的原型::operator int(); 为了避免不必要的隐式转换,在 C++ 11 中,可通过explict关键字,将转换运算符声明为显式的,在需要强制转换时将调用这些运算符. 或者用一个功能相同的非转换函数替换该转换函数.
在进行类型转换是要注意存不存在二义性.
Stonewt myCat(19.2);long gone = myCat;//int double 都可以转换成long型, Stonewt中定义了类转换为double 和int 的转换函数,所以这里存在二义性.编译不通过.//通过显式强制转换来指出使用哪一种转换函数. 过多的转换函数很容易造成二义性.