C++运算符重载
C++运算符重载
一般运算符的重载
首先看一个复数类的例子
class complex {
double re, im;
public:
complex(double r, double i);
complex add(complex b);
complex multiply(complex b);
};
void f() {
complex a(1.0, 1.0), b(-1.0, -2.0), c(0, 0);
a = b.add(c);
b = b.add(c.multiply(a));
c = a.multiply(b).add(complex(1,2));
// a = b+c;
// b = b + c * a;
// c = a * b + complex(1, 2);
}我们看到用成员函数的方式定义复数的加减乘除运算,调用起来非常不直观。我们希望复数类对象能够和int一样用+-*/运算符进行运算。这时就用到运算符重载,它为自定义类型添加运算符。
class complex {
double re, im;
public:
complex(double r, double i);
complex operator+ (complex b);
complex operator* (complex b);
};
int main() {
complex a(2, 3), b(-1, 2), c(3, 4);
a = b + c; // b.operator+(c)
b = b + c * a; // b.operator+(c.operator*(a))
c = a * b + complex(1, 2); //a.operator*(b).operator+(complex(1,2));
}
complex complex::operator+(complex b) {
return complex(re + b.re, im + b.im);
}能够重载的运算符
+ - * / % ^ & | ~ ! = < >
+= -= *= /= %= ^= &= |= << >>
>>= <<= == != <= >= && || ++
-- , -> [ ] () new new[ ] delete delete[ ]
不能重载的运算符
:: . .* ?: sizeof typeid
重载的运算符是一个名字和调用方法特殊的函数。可以用成员函数的方式重载,也可以用普通函数的方式重载,比如:
complex operator+( complex c, complex d);
重载的运算符的参数至少有一个是类,比如下面这些用法是错的:
int operator+(int a, int b); //error
bool operator==(char *, char *); // error
[ ], (), =, ->运算符必须以成员函数方式重载。运算符重载不会改变运算符原有的优先级、操作数数目、结合性。
int main () {
complex a, b, c;
complex d = a+b*c;
}不能够自己创造运算符,比如**。尽量不要改变运算符原来的语义,比如把加法操作定义成-,把减法操作定义成+。
二元运算符可以用带一个参数的成员函数的形式,也可以用带两个参数的普通函数的形式重载。区别:成员函数重载,第一个参数只能是类的对象。
class X {
public:
void operator+( int i);
X( int a );
};
void operator+( X a, X b);
void operator+( X a, double b);
void f( X a) {
a+1; // 相当于a.operator+(1);
1+a; //相当于 operator+(1, a);
a+1.0; //相当于 operator+(a, 1.0);
}一元运算符可以用不带参数的成员函数的形式,也可以用带一个参数的普通函数的形式重载
class X {
X* operator&(); // &a
X* operator&(X b); // 双目 a & b
X operator++(); // ++a
};
X operator-(X); // -a
X operator--(X); //--a
void f( X a) {
&a; // 相当于a.operator&();
-a; //相当于 operator-(a);
--a; //相当于 operator--(a);
++a; //相当于 a.operator++();
}完整的complex运算符
class complex {
double re, im;
public:
complex( double r = 0, double i = 0): re(r), im (i) { }
double& real() { return re; }
double& imag() { return im; }
complex operator+() { return *this; }
complex operator-() { return complex(-re, -im); }
complex operator+=( complex c) { re+= c.re; im+= c.im; return *this; }
complex operator-=( complex c);
complex operator*=( complex c);
complex operator/=( complex c);
};
complex operator+( complex a, complex b) {
a+=b; return a;
}
complex operator-( complex a, complex b);
complex operator*( complex a, complex b);
complex operator/( complex a, complex b);练习:创建分数类,定义尽可能多的运算符
operator 输入输出
<<和>>是移位运算符,经常重载作输入输出用
void f( complex c) {
cout << " hello world" << 100;
cout << c ; // operator<<(cout, c)
}
ostream &operator<<(ostream&os, complex c) {
return os << "(" << c.real() << "+" << c.imag() << "i)";
}
istream &operator>>(istream&is, complex &c) {
return is >> c.real() >> c.imag();
}练习:输出输出Date类型的值
operator= 赋值
赋值运算符,自定义对象赋值行为
void f( complex c);
int main () {
complex c1;
complex c2(c1); //拷贝构造
c2 = c1;
f( c1 ); //拷贝构造
}
如果没有自定义赋值运算符,默认的赋值行为:类中逐个成员赋值。
class string {
char *str;
int size;
public:
string( char *s ) {
size = strlen(s) +1;
str = new char[size];
strcpy(str, s);
}
~string( ) {
delete [ ] str;
}
string( const string& c){
size = c.size;
str = new char [size];
strcpy(str, c.str);
}
string& operator=(const string&c){
if(this != &c) {
delete [ ] str;
size = c.size;
str = new char[size];
strcpy(str, c.str);
}
return *this;
}
};
int main() {
string c ( "123" );
string d ( c );
c = d; // c.operator=(d)
}自定义赋值运算符加上自定义拷贝构造函数、析构函数,C++的内存管理就完整了。正确运用它们可以保证应用程序不产生内存泄漏。
练习:自己定义Stack类(栈),Array类(数组)
operator type 类型转换
可以将自定义类型转换为其他类型
class string {
char * str;
int size;
public:
operator char *() {
return str;
}
};class istream {
bool flag;
public:
operator bool() {
return flag;
}
istream& operator>>(double);
};
extern istream cin;
int main( ) {
int i;
while( cin >> i ) {
/*...*/
}
}练习:自定义分数类型自动转换成浮点型
如何把内置类型自动转换成自定义类型?通过一个参数的构造函数,比如
void f(complex c);
int main() {
complex c(5);
complex d = 7;
f(5);
}
class complex {
double re, im;
public:
complex(double r) {
re = r;
im = 0;
}
};如何禁止一个参数的构造函数自动转换类型?用explicit修饰构造函数
class complex {
double re, im;
public:
explicit complex(double r) {
re = r;
im = 0;
}
};operator[] 数组取成员
让对象像数组一样使用
class string {
char *str;
int size;
public:
char &operator[](int i) {
return str[i];
}
char operator[](int i) const {
return str[i];
}
};
void f( string s) {
cout << s[10]; //相当于s.operator[](10)
}operator[]运算符的参数可以是任何类型,但是只能是二元的
class Dictionary {
public:
string &operator[](string word) {
//...
}
};
void f( Dictionary s) {
cout << s[ "hello" ];
}练习:写一个Array类
operator() 函数调用
重载(),让对象可以像函数一样使用
class Add {
int base;
public:
Add(int b) : base(b) { }
int operator()(int a) { return base + a; }
};
void f(Add add) {
Add add2( 2 );
int c = add2( 8 ); // 相当于 add2.operator()(8);
c = add(5);
}operator()的参数个数可以任意。重载()的类又叫做仿函数,它是一种带状态的函数
operator++, operator– 自增自减
++和–运算符分为前置和后置两种。
class Integer {
int i;
public:
Integer& operator++() { //前置
++i;
return *this;
}
Integer operator++(int) { //后置
Integer temp(*this);
i++;
return temp;
}
};
void f( Integer i ) {
i++; // i.operator++(0);
++i; // i.operator++();
}练习: 写一个clock类
operator-> 指针取成员
重载->让一个对象可以像指针一样使用
class autoptr{
SchoolMember *person;
public:
autoptr( SchoolMember *p) : person(p) { }
~autoptr() { delete person; }
SchoolMember *operator->( ) { return person; }
SchoolMember &operator*( ) { return *person; }
};
int main() {
SchoolMember *s = new Teacher;
s->print();
delete s;
autoptr p( new Teacher);
p->print( );
}new, delete, new[ ], delete[ ] 内存分配
自定义对象new和delete的行为
class memory{
public:
void *operator new(size_t s) {
return malloc( s );
}
void operator delete(void *p) {
free(p);
}
};
int main() {
memory *p = new memory;
// p = memory::new (sizeof(memory)); call constructor on p;
delete p;
// p->~memory(); memory::operator delete(p);
}string例子
class String{
char *str;
int size;
public:
String(const char *s="");
String(const String&s);
String& operator=(const String&);
virtual ~String();
char &operator[ ](int i);
char operator[ ](int i) const;
bool operator==(const String &) const;
int length() const;
operator char*();
friend ostream& operator<<(ostream&os, const String &s);
friend istream& operator>>(istream&is, String &s);
};