【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对象(因为它被作为参数传递),来计算总和,并返回这个值。

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 <iostream>
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

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

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

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;      // * 右边操作数是类对象,调用非成员友元函数,重载运算符*

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; 	// << 右边是类对象,调用非成员友元函数,重载运算符<<

11.5 再谈重载:一个矢量类

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

vector.h

#ifndef PRIMERPLUS_VERTOR_H
#define PRIMERPLUS_VERTOR_H
#include <iostream>
#include <cmath>
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

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

usevector.cpp

// randwalk.cpp -- using the Vector class
#include <iostream>
#include <cstdlib>  // rand(), srand() prototypes
#include <ctime>    // 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;
}

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. 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

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

stonewt.cpp

// stonewt.cpp -- Stonewt methods
#include <iostream>
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;
}

usestonewt.cpp

// stone.cpp -- user-defined conversions
// compile with stonewt.cpp
#include <iostream>
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();
    }
}

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

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

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

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

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

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

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

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

operator double() {
    
    return mag;}

11.9 编程练习

p7.h

#ifndef __COMPLEX_0_H__
#define __COMPLEX_0_H__

#include <iostream>

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

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

mainp7.cpp

#include <iostream>
#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;
}

猜你喜欢

转载自blog.csdn.net/qq_39751352/article/details/126884382