《c++入门经典》笔记15

第十五章 运算符重载

15.1重载运算符

对于c++内置类型,对其使用相应运算符,编译器能准确知道其意思,比如:

int x = 17,y = 12,z;
z = x * (y + 5);

通过使用成员函数multiply()和add(),类也能提供这样的功能,但语法复杂得多。假如有个表示整数的Number类,下述代码与上例的相同:

Number x(17);
Number y(12);
Number z,temp;
temp = y.add(5);
z = x.multiply(temp);

这些代码将5与y相加,再将结果与x相乘,最终结果同样是289

为了简化代码,可重载运算符,这样便可使用运算符来操作对象。

运算符重载定义了将运算符用于对象时执行的操作,几乎所有的c++运算符都可重载。

程序清单15.1 Counter.cpp

#include<iostream>

class Counter
{
private:
    int value;
public:
    Counter();
    ~Counter(){}
    int getValue() const{return value;}
    void setValue(int x){value = x;}
};

Counter::Counter():value(0)
{}

int main()
{
    Counter c;
    std::cout<<"The value of c is "<<c.getValue()<<std::endl;
    return 0;
}

运行结果就不贴了。这个程序并未进行运算符重载,也没有进行改动,Counter对象也不能进行递增、递减、相加和赋值,不能用其他运算符操作它,显示其值也不容易(需要调用成员函数进行显示)。

编写递增方法

通过重载运算符,可给类提供原本不能进行的运算符操作。

要在类中重载运算符,最常见的方式是使用成员函数。

函数名由operator和要定义的运算符(如+或++)组成

程序清单15.2 Counter2.cpp

#include <iostream>

class Counter
{
private:
    int value;

public:
    Counter();
    ~Counter() {}
    int getValue() const { return value; }
    void setValue(int x) { value = x; }
    void increment() { ++value; }
    const Counter &operator++(); //重载函数声明
};

Counter::Counter() : value(0) {}

const Counter &Counter::operator++()
{
    ++value;
    return *this;//对this指针解引用以返回当前对象
}

int main()
{
    Counter c;
    std::cout << "The value of c is " << c.getValue() << std::endl;
    c.increment();
    std::cout << "The value of c is " << c.getValue() << std::endl;
    ++c;//++重载
    std::cout << "The value of c is " << c.getValue() << std::endl;
    Counter a = ++c;
    std::cout << "The value of a: " << a.getValue() << std::endl;
    std::cout << " and c: " << c.getValue() << std::endl;
}

重载后缀运算符

给成员函数operator++()添加一个int参数。在函数体内,不会使用这个参数,它只用于表明改函数定义的是后缀运算符。

为此,在重载的成员函数中,必须创建一个临时对象,用于存储原始值,以便对原对象进行递增。返回的将是原始对象,因为后缀运算符要求使用原始值,而不是递增后的值。

必须按值(而不是按引用)返回该临时对象,否则函数返回时它将不再在作用域内。

程序清单15.3 Counter3.cpp

#include <iostream>

class Counter
{
private:
    int value;

public:
    Counter();
    ~Counter() {}
    int getValue() const { return value; }
    void setValue(int x) { value = x; }
    const Counter &operator++();
    const Counter operator++(int);
};

Counter::Counter() : value(0) {}

const Counter &Counter::operator++()
{
    ++value;
    return *this;
}

const Counter Counter::operator++(int)
{
    Counter temp(*this);
    ++value;
    return temp;
}

int main()
{
    Counter c;
    std::cout << "The value of c is " << c.getValue() << std::endl;
    c++;
    std::cout << "The value of c is " << c.getValue() << std::endl;
    ++c;
    std::cout << "The value of c is " << c.getValue() << std::endl;
    Counter a = ++c;
    std::cout << "The value of a : " << a.getValue() << std::endl;
    std::cout << "and c : " << c.getValue() << std::endl;
    a = c++;
    std::cout << "The value of a : " << a.getValue() << std::endl;
    std::cout << "and c : " << c.getValue() << std::endl;
    return 0;
}

重载加法运算符

和上面的意识是大同小异

程序清单15.4 Counter4.cpp

#include <iostream>

class Counter
{
private:
    int value;

public:
    Counter();
    Counter(int intialValue);
    ~Counter() {}
    int getValue() const { return value; }
    void setValue(int x) { value = x; }
    Counter operator+(const Counter &);
};

Counter::Counter() : value(0) {}
Counter::Counter(int intialValue) : value(intialValue) {}
Counter Counter::operator+(const Counter &rhs)//重载+运算符
{
    return Counter(value + rhs.getValue());
}

int main()
{
    Counter alpha(4), beta(13), gamma;
    gamma = alpha + beta;
    std::cout << "alpha: " << alpha.getValue() << std::endl;
    std::cout << "beta: " << beta.getValue() << std::endl;
    std::cout << "gamma: " << gamma.getValue() << std::endl;
    return 0;
}

对运算符重载的限制

不能重载用于内置类型的运算符:不能改变运算符的优先级和目数(单目、双目或三目);另外,不能创建新运算符,因此不能将**声明为指数(乘方)运算符。

运算符重载是c++新手过度使用和滥用的c++功能之一,他们经常禁不住诱惑,给一些晦涩的运算符提供有趣的新用途,但这常常会导致代码令人迷惑,难以理解。

赋值运算符

赋值运算符的重载函数为operator=(),重载后每当给对象赋值都将调用它。

重载赋值运算符时,有如下问题需要考虑:

  • 如果将一个对象赋值给另一个对象,比如dallas = wichita;这时对象dallas之前的内存如果是在堆中分配的,那么就应该考虑重载=运算符时释放该对象所指向的堆内存后再进行赋值。
  • 如果将一个对象赋值给自己,比如dallas = dallas;,这种情况可能意外发生。再结合上面的释放内存问题,这个时候就可能导致dallas将堆中分配给自己的内存释放,这样,就会出现无法预料的意外。为了避免这种问题,可以使用this指针检查右操作数是否为当前对象。

程序清单15.5 Assignment.cpp

#include <iostream>

class Tricycle
{
private:
    int *speed;

public:
    Tricycle();
    ~Tricycle();
    int getSpeed() const { return *speed; }
    void setSpeed(int newSpeed) { *speed = newSpeed; }
    Tricycle operator=(const Tricycle &);
};

Tricycle::Tricycle()
{
    speed = new int;
    *speed = 5;
}

Tricycle::~Tricycle()
{
    delete speed;
}

Tricycle Tricycle::operator=(const Tricycle &rhs)
{
    if (this == &rhs)
        return *this;
    delete speed;
    speed = new int;
    *speed = rhs.getSpeed();
    return *this;
}

int main()
{
    Tricycle wichita;
    std::cout << "Wichita's speed: " << wichita.getSpeed() << std::endl;
    std::cout << "Setting Wichita's speed to 6 ..." << std::endl;
    wichita.setSpeed(6);
    Tricycle dallas;
    std::cout << "Dallas's speed: " << dallas.getSpeed() << std::endl;
    std::cout << "Copying Wichita to Dallas ..." << std::endl;
    wichita = dallas;
    std::cout << "Dallas's speed: " << dallas.getSpeed() << std::endl;
    return 0;
}

剩下的那些运算符也都大同小异,只要重载运算符时考虑那些可能会出现bug的细节。

15.2转换运算符

如果您视图将一个内置类型赋值给一个用户自定义的类型,如果不创建转换运算符而直接赋值会导致编译失败,比如:

int beta = 5;
Counter alpha = beta;//报错,无法将int转换为Counter对象

下面这个程序通过创建一个转换运算符(其实是一个构造函数,接受一个int参数并创建一个Counter对象)修复了这个问题。

程序清单15.7 Counter6.cpp

#include <iostream>

class Counter
{
private:
    int value;

public:
    Counter() : value(0) {}
    Counter(int newValue);
    ~Counter() {}
    int getValue() const { return value; }
    void setValue(int newValue) { value = newValue; }
};

Counter::Counter(int newValue) : value(newValue) {}

int main()
{
    int beta = 5;
    Counter alpha = beta;
    std::cout << "alpha: " << alpha.getValue() << std::endl;
    return 0;
}

运行输出是:alpha:5

上面是将内置类型变量赋值给对象,下面将演示将对象赋值给内置变量类型。

c++支持在类中添加转换运算符,以指定如何将对象隐式地转换为内置类型变量。

程序清单15,8 Counter7.cpp

#include <iostream>

class Counter
{
private:
    int value;

public:
    Counter() : value(0) {}
    Counter(int newValue);
    ~Counter() {}
    int getValue() const { return value; }
    void setValue(int newValue) { value = newValue; }
    operator unsigned int();
};

Counter::Counter(int newValue) : value(newValue) {}

Counter::operator unsigned int() //转换运算符
{
    return (value);
}

int main()
{
    Counter epsilon(19);
    int zeta = epsilon;
    std::cout << "zeta: " << zeta << std::endl;
    return 0;
}

运行输出是:zeta: 19

注意转换运算符没有指定返回值,但实际上返回了一个转换后的值。

posted @ 2020-07-20 15:34  小立哥  阅读(268)  评论(0编辑  收藏  举报