• 【C++ Primer Plus】第11章 使用类


    除非经常使用它们,否则这些技能可能根本与日常工作无关.

    11.1 运算符重载

    1. C++根据操作数的数目和类型来决定采用哪种操作。
    2. 将*运算符用于地址,将得到存储在这个地址中的值;但将它用于两个数字时,得到的将是它们的乘积。
    3. C++允许将运算符重载扩展到用户定义的类型。例如,允许使用+将两个对象相加。
    4. 要重载运算符,需使用被称为运算符函数的特殊函数形式。operatorop(argument-list)
    5. operator +( )重载+运算符,operator ( )重载运算符。[ ]是数组索引运算符。
    district2 = sid + sara;
    // 编译器发现,这三个操作数有一个是用户自定义的类型,
    // 因此使用相应的运算符函数替换上述运算符:
    district2 = sid.operator+(sara);
    // 隐式地使用sid(因为它调用了方法)
    // 显式地使用 sara对象(因为它被作为参数传递),来计算总和,并返回这个值。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    11.2 计算时间:一个运算符重载示例

    1. 将参数声明为引用的目的是为了提高效率。 如果按值传递Time对象,代码的功能将相同,但传递引用,速度将更快,使用的内存将更少。
    2. 使用返回类型 Time意味着程序将在删除sum之前构造它的拷贝,调用函数将得到该拷贝。
    3. 不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失, 引用将指向不存在的数据。

    11.2.1 重载运算符的限制

    1. 重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。
    2. 使用运算符时不能违反运算符原来的句法规则。例如,不能将求模运算符(%)重载成使用一个操作数。
    3. 不能修改运算符的优先级。
    4. 不能创建新运算符。例如,不能定义operator **( )函数来表示求幂。
    5. 有些运算符只能通过成员函数进行重载。 =赋值运算符。 ( )函数调用运算符。 [ ]下标运算符。 ->通过指针访问类成员的运算符。
    6. 有些运算符不能重载。 sizeof运算符。 .成员运算符。 . *成员指针运算符。 ::作用域解析运算符。 ?:条件运算符。 typeid一个RTTI运算符。 const_cast强制类型转换运算符。 dynamic_cast强制类型转换运算符。 reinterpret_cast强制类型转换运算符。static_cast强制类型转换运算符。

    mytime0.h

    #ifndef PRIMERPLUS_MYTIME0_H
    #define PRIMERPLUS_MYTIME0_H
    #include 
    using namespace std;
    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 Sum(const Time &t) const;
    
        Time operator+(const Time &t) const;    // 重载运算符+
        Time operator-(const Time &t) const;    // 重载运算符-
        Time operator*(double n) const;         // 重载运算符*,*左边操作数必须是类对象
        void show() const;
    // 只有类声明可以决定哪一个函数是友元,以下是非成员友元函数
    // friend Time operator*(double m, const Time & t);    // 重载运算符*,*左边操作数左侧是double值
        friend Time operator*(double m, const Time & t) { return t * m;} // 内联函数,其中使用成员函数重载运算符*
        friend ostream & operator<<(ostream & os, const Time & t);      // 重载运算符<<
    };
    #endif //PRIMERPLUS_MYTIME0_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    mytime0.cpp

    #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;
    }
    
    Time Time::Sum(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 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;
        if (tot2 > tot1)
        {
            diff.minutes = (tot2 - tot1) % 60;
            diff.hours = (tot2 - tot1) / 60;
        }
        else
        {
            diff.minutes = (tot1 - tot2) % 60;
            diff.hours = (tot1 - tot2) / 60;
        }
        return diff;
    }
    
    Time Time::operator*(double n) const
    {
        Time result;
        long totalminutes = hours * n * 60 + minutes * n;
        result.hours = totalminutes / 60;
        result.minutes = totalminutes % 60;
        return result;
    }
    
    void Time::show() const
    {
        cout << hours << " hours, " << minutes << " minutes." << endl;
    }
    
    // 友元函数定义,不需要限定符,不需要关键字,可以使用私有成员
    //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;
    //}
    
    // operator<<( )直接访问Time对象的私有成员,所以它必须是Time类的友元。
    ostream & operator<<(ostream & os, const Time & t)
    {
        os << t.hours << " hours, " << t.minutes << " minutes." << endl;
        return os;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    usemytime0.cpp

    #include "mytime0.h"
    int main(void)
    {
        Time t1;
        Time t2(4, 20);
        Time t3;
        t3 = t2.Sum(t1);
        t3 = t2.operator+(t1);  // t3 = t1 + t2;
        Time t4;
        t4 = t3 + t2 + t1;  // + 加号两边都是类对象,调用成员函数,重载运算符+
        t4.show();
        Time t5;
        t5 = t4 - t3;       // - 减号两边都是类对象,调用成员函数,重载运算符-
        t5.show();
        Time t6;
        t6 = t5 * 3.0;      // * 左边操作数是类对象,调用成员函数,重载运算符*
        t6.show();
        t6 = 2.1 * t5;      // * 右边操作数是类对象,调用非成员友元函数,重载运算符*
        t6.show();
        cout << "t6:" << t6;// << 右边是类对象,调用非成员友元函数,重载运算符<<
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    11.3 友元函数

    1. 通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。可以访问类的私有成员。
    2. 如果要为类重载运算符,并将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序。
    3. 非成员函数不是由对象调用的,它使用的所有值(包括对象)都是显式参数。
    4. 对于成员函数版本来说,一个操作数通过this指针隐式地传递,另一个操作数作为函数参数显式地传递;
    5. 对于友元版本来说,两个操作数都作为参数来传递。

    11.3.1 创建友元函数

    1. 第一步,将其原型放在类声明中,并在原型声明前加上关键字friend:friend Time operator*(double m, const Time & t);
    2. 第二步,编写函数定义。不需要限定符和friend。
    3. 友元函数不是成员函数,因此不能使用成员运算符来调用,但它与成员函数的访问权限相同。
    4. 只有类声明可以决定哪一个函数是友元函数。
    // 在Time类中声明,只有类声明可以决定哪一个函数是友元
    friend Time operator*(double m, const Time & t);    // 非成员友元函数
    // friend Time operator*(double m, const Time & t) { return t * m;} // 内联函数,其中使用成员函数重载运算符*
    
    // 友元函数定义,不需要限定符,不需要关键字,可以使用私有成员
    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;
    }
    
    // 调用
    Time t5(12, 20);
    Time t6;
    t6 = 2.1 * t5;      // * 右边操作数是类对象,调用非成员友元函数,重载运算符*
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    11.3.2 常用的友元:重载<<运算符,以便用于输出。

    想要用cout << t6;来直接显示类对象t6,<< 右边是类对象,则使用非成员友元函数来重载运算符<<。

    // 声明
    friend ostream & operator<<(ostream & os, const Time & t); // 重载运算符<<
    
    // 定义
    // operator<<( )直接访问Time对象的私有成员,所以它必须是Time类的友元。
    // 注意返回是一个ostream的引用
    ostream & operator<<(ostream & os, const Time & t)
    {
        os << t.hours << " hours, " << t.minutes << " minutes." << endl;
        return os;
    }
    
    // 调用
    Time t6;
    cout << "t6:" << t6; 	// << 右边是类对象,调用非成员友元函数,重载运算符<<
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    11.5 再谈重载:一个矢量类

    1. 类非常适于在一个对象中表示实体的不同方面。首先在一个对象中存储多种表示方式;然后,编写这样的类函数,以便给一种表示方式赋值时,将自动给其他表示方式赋值。
    2. 如果方法通过计算得到一个新的类对象,则应考虑是否可以使用类构造函数来完成这种工作。这样做不仅可以避免麻烦,而且可以确保新的对象是按照正确的方式创建的。
    3. 因为运算符重载是通过函数来实现的,所以只要运算符函数的特征标不同,使用的运算符数量与相应的内置C++运算符相同,就可以多次重载同一个运算符。

    vector.h

    #ifndef PRIMERPLUS_VERTOR_H
    #define PRIMERPLUS_VERTOR_H
    #include 
    #include 
    using namespace std;
    namespace VECTOR
    {
        class Vector
        {
        public:
            enum Mode {RECT, POL};
            // 外部使用POL时要加上类名称和限定符,或者声明using VECTOR::Vector;
    
        private:
            double x;       // 直角坐标x
            double y;       // 直角坐标y
            double mag;     // 极坐标长度
            double ang;     // 极坐标角度
            Mode mode;      // 枚举变量
            void set_mag();
            void set_ang();
            void set_x() {x = mag * cos(ang);}
            void set_y() {y = mag * sin(ang);}
    
        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;}     // 在类声明中定义,自动成为内联函数
            double yval() const {return y;}
            double magval() const {return mag;}
            double angval() const {return ang;}
            void polar_mode() {mode = POL;}
            void rect_mode() {mode = RECT;}
    
            Vector operator+(const Vector & b) const;
            Vector operator-(const Vector & b) const;
            Vector operator-() const;           // 重载运算符-,进行取反操作
            Vector operator*(double n) const;   // 使用格式:类的对象 * 放大倍数
    
            friend Vector operator*(double n, const Vector & a) {return a * n;}
            friend ostream & operator<<(ostream & os, const Vector & v);
        };
    }
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    vector.cpp

    #include "vector.h"
    
    namespace VECTOR
    {
        const double Rad_to_deg = 45.0 / atan(1.0);
    
        void Vector::set_mag()
        {
            mag = sqrt(x*x + y*y);
        }
    
        void Vector::set_ang()
        {
            if (x == 0.0 && y == 0.0)
                ang = 0;
            else
                ang = atan2(y, x);  // 弧度制
        }
    
        Vector::Vector()
        {
            x = y = mag = ang = 0.0;
            mode = RECT;
        }
    
        Vector::Vector(double n1, double n2, Mode form)
        {
            mode = form;
            if (form == RECT)
            {
                x = n1;
                y = n2;
                set_mag();
                set_ang();
            }
            else if (form == POL)
            {
                mag = n1;
                ang = n2 / Rad_to_deg;  // 角度转换到弧度
                set_x();
                set_y();
            }
            else
            {
                cout << "ERROR." << endl;
                x = y = mag = ang = 0.0;
                mode = RECT;
            }
        }
    
        void Vector::reset(double n1, double n2, Mode form)
        {
            mode = form;
            if (form == RECT)
            {
                x = n1;
                y = n2;
                set_mag();
                set_ang();
            }
            else if (form == POL)
            {
                mag = n1;
                ang = n2 / Rad_to_deg;  // 角度转换到弧度
                set_x();
                set_y();
            }
            else
            {
                cout << "ERROR." << endl;
                x = y = mag = ang = 0.0;
                mode = RECT;
            }
        }
    
        Vector::~Vector()
        {
    
        }
    
        Vector Vector::operator+(const Vector & b) const
        {
            return Vector(x + b.x, y + b.y);
        }
    
        Vector Vector::operator-(const Vector & b) const
        {
            return Vector(x - b.x, y- b.y); // 注意!!!这里使用 x 和 y 为前项
            // 从隐式矢量参数减去以显式参数传递的矢量
        }
    
        Vector Vector::operator-() const
        {
            return Vector(-x, -y);
        }
    
        Vector Vector::operator*(double n) const
        {
            return Vector(x*n, y*n);
        }
    
        ostream & operator<<(ostream & os, const Vector & v)
        {
            if (v.mode == Vector::POL) //注意这里的POL要加上类限定符,友元函数不在类作用域
                os << "mag, nag : " << v.mag << ", " << v.ang << endl;
            else if (v.mode == Vector::RECT)
                os << "x, y : " << v.x << ", " << v.y << endl;
            else
                os << "ERROR." << endl;
            return os;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    usevector.cpp

    // randwalk.cpp -- using the Vector class
    #include 
    #include   // rand(), srand() prototypes
    #include     // time() prototype
    #include "vector.h"
    int main()
    {
        using namespace std;
        using VECTOR::Vector;
        srand(time(0));     // seed random-number generator
        double direction;
        Vector step;
        Vector result(0.0, 0.0);
        unsigned long steps = 0;
        double target;
        double dstep;
        cout << "Enter target distance (q to quit):";
        while (cin >> target)
        {
            cout << "Enter step length:";
            if (!(cin >> dstep))
                break;
            while (result.magval() < target)
            {
                direction = rand() % 360;
                step.reset(dstep, direction, Vector::POL);
                result = result + step;
                steps++;
            }
            cout << "After " << steps << " steps, the subject has the following location:\n";
            cout << result << endl;
            result.polar_mode();
            cout << " or\n" << result << endl;
            cout << "Average outward distance per step = " << result.magval()/steps << endl;
            steps = 0;
            result.reset(0.0, 0.0);
            cout << "Enter target distance (q to quit):";
        }
        cout << "Bye!\n";
        cin.clear();
        while (cin.get() != '\n')
            continue;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

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

    C++语言不自动转换不兼容的类型。

    从参数类型到类类型的转换:

    1. 只接受一个参数的构造函数:定义了从参数类型到类类型的转换。可以用于隐式和显示转换。
    2. 如果使用关键字explicit限定了这种构造函数,则它只能用于显示转换。
    Stonewt(double lbs);			// Stonewt类声明中的构造函数
    Stonewt incognito = 275;    	// 类的自动隐式转换,将int数字转换成类的对象,创建临时对象,传递给incognito
    Stonewt incognito_ = Stonewt(275); // 创建一个临时的无名的类对象,传递给incognito_
    
    // 加关键字就不会自动类型转换,需要显示转换
    explicit Stonewt(double lbs); 	
    Stonewt incognito_ = Stonewt(275); // 显示转换,创建一个临时的无名的类对象,传递给incognito_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    从类类型到其他类型的转换:

    1. C++运算符函数——转换函数:可以实现从类类型到其他类型的转换。
    2. 转换函数是用户定义的强制类型转换,可以像使用强制类型转换那样使用它们。
    3. 如果使用关键字explicit限定了转换函数,则它只能用于显示转换。

    如何创建转换函数呢?

    1. 要转换为typeName类型,需要使用这种形式的转换函数: operator typeName();
    2. 转换函数必须是类方法; 需要通过类对象来调用。
    3. 转换函数不能指定返回类型; typeName指出了要转换成的类型。
    4. 转换函数不能有参数。
    // 如果定义了从Stonewt类到double的转换函数
    operator double() const;
    explicit operator double() const;	// 如果加了关键字后,只能显示转换
    // 编译器发现,右侧是Stonewt类型,而左侧是double类型,因此它将查看程序员是否定义了与此匹配的转换函数。
    Stonewt wolfe(285.7);
    double hello = wolfe;				// 隐式
    double host = double (wolfe); 		// 显示,syntax #1
    double thinker = (double) wolfe; 	// 显示,syntax #2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    stonewt.h

    #ifndef PRIMERPLUS_STONEWT_H
    #define PRIMERPLUS_STONEWT_H
    class Stonewt
    {
    private:
        // 在类里定义const常量不会在内存中保留
        enum {Lbs_per_stn = 14};    // pounds per stone
        int stone;                  // 英石
        double pds_left;            // 磅
        double pounds;              // 总磅
    public:
        // explicit Stonewt(double lbs); // 加关键字explicit就不会自动隐式类型转换
        Stonewt(double lbs);        // 只有接受一个参数的构造函数才能作为转换函数。
        Stonewt(int stn, double lbs);
        Stonewt();
        ~Stonewt();
        void show_lbs() const;      // show weight in pounds format
        void show_stn() const;      // show weight in stone format
    
        operator int() const;             // 转换函数
        operator double() const;
    };
    #endif //PRIMERPLUS_STONEWT_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    stonewt.cpp

    // stonewt.cpp -- Stonewt methods
    #include 
    using std::cout;
    #include "stonewt.h"
    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";
    }
    // conversion functions
    Stonewt::operator int() const
    {
        return int (pounds + 0.5);
    }
    Stonewt::operator double() const
    {
        return pounds;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    usestonewt.cpp

    // stone.cpp -- user-defined conversions
    // compile with stonewt.cpp
    #include 
    using std::cout;
    using std::endl;
    #include "stonewt.h"
    void display(const Stonewt & st, int n);
    int main()
    {
        // 只有接受一个参数的构造函数才能作为转换函数。
        cout << "-------------------construct function--------------------" << endl;
        Stonewt incognito = 275;    // 类的自动转换,将数字转换成类的对象,创建临时对象,传递给incognito
        Stonewt incognito_ = Stonewt(275); // 创建一个临时的无名的类对象,传递给incognito_
        Stonewt wolfe(285.7);       // 构造函数用于将double类型的值转换为Stonewt类型
        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();
        cout << "---------------------------------------" << endl;
        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();
        cout << "---------------------------------------" << endl;
        display(taft, 2);
        cout << "The wrestler weighed even more.\n";
        display(422, 2);
        cout << "No stone left unearned\n";
        cout << "-------------------conversion functions--------------------" << endl;
        Stonewt poppins(9,2.8); // 9 stone, 2.8 pounds
        double p_wt = poppins; // implicit conversion
        cout << "Convert to double => ";
        cout << "Poppins: " << p_wt << " pounds.\n";
        cout << "Convert to int => ";
        cout << "Poppins: " << int (poppins) << " pounds.\n";
        return 0;
    }
    void display(const Stonewt & st, int n)
    {
        for (int i = 0; i < n; i++)
        {
            cout << "Wow! ";
            st.show_stn();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    11.7 总结

    1. 一般来说,访问私有类成员的唯一方法是使用类方法。C++使用友元函数来避开这种限制。要让函数成为友元,需要在类声明中声明该函数,并在声明前加上关键字friend
    2. C++扩展了对运算符的重载,允许自定义特殊的运算符函数,这种函数描述了特定的运算符与类之间的关系。运算符函数可以是类成员函数,也可以是友元函数(有一些运算符函数只能是类成员函数)。要调用运算符函数,可以直接调用该函数,也可以以通常的句法使用被重载的运算符。
    3. 对于运算符op,其运算符函数的格式如下:operatorop(argument-list),argument-list表示该运算符的操作数。如果运算符函数是类成员函数,则第一个操作数是调用对象,它不在argument-list中。例如,本章通过为Vector类定义operator +( )成员函数重载了加法。如果up、right和result都是Vector对象,则可以使用下面的任何一条语句来调用矢量加法:result = up.operator+(right);``result = up + right;在第二条语句中,由于操作数up和right的类型都是Vector,因此 C++将使用Vector的加法定义。
    4. 当运算符函数是成员函数时,则第一个操作数将是调用该函数的对象。例如,在前面的语句中,up对象是调用函数的对象。定义运算符函数时,如果要使其第一个操作数不是类对象,则必须使用友元函数。这样就可以将操作数按所需的顺序传递给函数了。
    5. 最常见的运算符重载任务之一是定义<<运算符,使之可与cout一起使用,来显示对象的内容。要让ostream对象成为第一个操作数,需要将运算符函数定义为友元;要使重新定义的运算符能与其自身拼接,需要将返回类型声明为ostream &。下面的通用格式能够满足这种要求:然而,如果类包含这样的方法,它返回需要显示的数据成员的值,则可以使用这些方法,无需在operator<<( )中直接访问这些成员。在这种情况下,函数不必(也不应当)是友元。
    ostream & operator<<(ostream & os, const c_name & obj)
    {
        os << ... ; // display object contents
        return os;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. C++允许指定在类和基本类型之间进行转换的方式。首先,任何接受唯一一个参数的构造函数都可被用作转换函数,将类型与该参数相同的值转换为类。如果将类型与该参数相同的值赋给对象,则C++将自动调用该构造函数。
      例如,假设有一个String类,它包含一个将char *值作为其唯一参数的构造函数,那么如果bean是String对象,则可以使用下面的语句:
      bean = "pinto"; // converts type char * to type String.
      然而,如果在该构造函数的声明前加上了关键字explicit,则该构造函数将只能用于显式转换:
      bean = String("pinto"); // converts type char * to type String explicitly
    2. 要将类对象转换为其他类型,必须定义转换函数,指出如何进行这种转换。转换函数必须是成员函数。将类对象转换为typeName类型的转换函数的原型如下: operator typeName();注意,转换函数没有返回类型、没有参数,但必须返回转换后的值(虽然没有声明返回类型)。例如,下面是将Vector转换为double类型的函数:
    Vector::operator double()
    {
        ...
        return a_double_value;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    11.8 复习题

    1.使用成员函数为Stonewt类重载乘法运算符,该运算符将数据成员与double类型的值相乘。注意,用英石和磅表示时,需要进位。也就是说,将10英石8磅乘以2等于21英石2磅。 (一英石等于14磅)

    // prototype
    Stonewt operator*(double mult);
    
    // definition - let constructor do the work
    Stonewt Stonewt::operator*(double mult)
    {
        return Stonewt(mult * pounds);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.使用友元函数为Stonewt类重载乘法运算符,该运算符将double值与Stone值相乘。

    // prototype
    friend Stonewt operator*(double mult, const Stonewt & s);
    
    // definetion - let constructor do the work
    Stonewt operator*(double mult, const Stonewt & s)
    {
        return Stonewt(mult * s.pounds);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.友元函数与成员函数之间的区别是什么?
    成员函数是类定义的一部分,通过特定的对象来调用。成员函数可以隐式访问调用对象的成员,而无需使用成员运算符。
    友元函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式访问类成员,而必须将成员运算符用于作为参数传递的对象。

    3.非成员函数必须是友元才能访问类成员吗?
    要访问私有成员,它必须是友元,但要访问公有成员,可以不是友元。

    5.哪些运算符不能重载?
    【sizeof】【.】【.*】【::】【?:】

    6.在重载运算符=、( )、[ ]和->时,有什么限制?
    这些运算符必须使用成员函数来定义。

    7.为Vector类定义一个转换函数,将Vector类转换为一个double类型的值,后者表示矢量的长度。

    operator double() {return mag;}
    
    • 1

    11.9 编程练习

    p7.h

    #ifndef __COMPLEX_0_H__
    #define __COMPLEX_0_H__
    
    #include 
    
    using namespace std;
    
    class complex
    {
    	private:
    		double real;
    		double imaginary;
    	public:
    		complex();
    		complex(double r, double i);
    		complex operator+(const complex &c) const;
    		complex operator-(const complex &c) const;
    		complex operator*(const complex &c) const;
    		complex operator~() const;
    
    		friend complex operator*(double x, const complex &c);
    		friend istream &operator>>(istream &is, complex &c);
    		friend ostream &operator<<(ostream &os, const complex &c);
    };
    
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    p7.cpp

    #include "complex0.h"
    
    complex::complex()
    {
    	real = imaginary = 0.0;
    }
    
    complex::complex(double r, double i)
    {
    	real = r;
    	imaginary = i;
    }
    
    complex complex::operator+(const complex &c) const
    {
    	return complex(real+c.real, imaginary+c.imaginary);
    }
    
    complex complex::operator-(const complex &c) const
    {
    	return complex(real-c.real, imaginary-c.imaginary);
    }
    
    complex complex::operator*(const complex &c) const
    {
    	return complex(real*c.real - imaginary*c.imaginary, real*c.imaginary + imaginary*c.real);
    }
    
    complex complex::operator~() const
    {
    	return complex(real, -imaginary);
    }
    
    complex operator*(double x, const complex &c)
    {
    	return complex(x*c.real, x*c.imaginary);
    }
    
    istream &operator>>(istream &is, complex &c)
    {
    	is >> c.real >> c.imaginary;
    	return is;
    }
    
    ostream &operator<<(ostream &os, const complex &c)
    {
    	os << "real = " << c.real << ", imaginary = " << c.imaginary << endl;
    	return os;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    mainp7.cpp

    #include 
    #include "complex0.h"
    
    using namespace std;
    
    int main(void)
    {
    	complex a(3.0, 4.0);
    	complex c;
    	cout << "Enter a complex number (q to quit): \n";
    
    	while(cin >> c)
    	{
    		cout << "c is " << c << endl;
    		cout << "complex conjugate is " << ~c << endl;
    		cout << "a is " << a << endl;
    		cout << "a + c is " << a + c << endl;
    		cout << "a - c is " << a - c << endl;
    		cout << "a * c is " << a * c << endl;
    		cout << "2 * c is " << 2 * c << endl;
    		cout << "Enter a complex number (q to quit): \n";
    	}
    	cout << "Done\n";
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 相关阅读:
    SpringAOP执行流程——从源码画流程图
    SpingMVC之拦截器使用详解
    [MAUI]集成富文本编辑器Editor.js至.NET MAUI Blazor项目
    使用自定义数据训练 YOLOv10
    【FPGA】Verilog:实现 RS 触发器 | Flip-Flop | 使用 NOR 的 RS 触发器 | 使用 NAND 的 RS 触发器
    安全防御——二、ENSP防火墙实验学习
    创建型模式-原型模式
    Spring:@Autowired @Resource @Value的区别
    .net 温故知新:【6】Linq是什么
    sklearn模型中预测值的R2_score为负数
  • 原文地址:https://blog.csdn.net/qq_39751352/article/details/126884382