第十五章 运算符重载
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
注意转换运算符没有指定返回值,但实际上返回了一个转换后的值。