一、运算符重载。

运算符重载允许将标准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= B
    2.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();
    }
}

在这里插入图片描述


版权声明:本文为weixin_45648902原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_45648902/article/details/106385286