运算符重载(operator overload) 是c++的一个重要的新特性,它使得程序员把c++运算符定义扩展到操作数是对象的情况.运算符重载的目的是使c++程序更加直观,更易懂.
但这种方便有时候也带来一些潜在的危险!!
运算符重载和普通的重载一样:
作用域相同 , 函数名字相同 , 但参数不同(参数个数,参数的类型) , 注意如果函数名字相同,参数相同但函数的返回值类型不同这不是重载,这是错误!
格式:
返回值类型 operator被重载的运算符 (参数 ...) {函数的实现};
具体的例子网上有很多 , 我的重点是一些我们在写重载中易忽略的一些小问题.
有时候我们的程序在多数情况下是对的但有时也总有一些出乎我们意料的情况:
请看下面这个程序:
<span style="font-family:Microsoft YaHei;font-size:14px;">#include<iostream>
#include<stdlib.h>
using namespace std;
int explicit add(int a,int b) //1
{
cout<<"int int"<<endl;
return a+b;
}
float explicit add(float a,float b) //2
{
cout<<"float float"<<endl;
return a+b;
}
int main()
{
cout<<add(1,1)<<endl; //这里完全匹配没有问题
//cout<<add(1.1,2.0)<<endl; //这里编译器编译的时候会有歧义
cout<<add(1.1,2)<<endl; //问题出现这里编译器会有一个隐式类型转换
//编译器会把 1.1向下隐式转换为1 (double->int)
//这里编译器不会把2向上转换即(int->double) 这的强者类型转换
//呵呵恐怖吗 你以为你在调用第二个函数 结果调用的是第一个
system("pause");
}</span>
运算结果:
我本以为第二个cout会调用第二个函数结果, 但结果程序调用的是第一个
这里涉及到类型转换编译器默认会把
1 .1 -> 1 (double->int) 向下转换.
而不是把 2->2.0 (int -> double ) 这样是强制类型转换.
那么如何避免编译器的隐藏类型转换了
那就是 explicit (清晰的) 关键字
这个关键字的作用是告诉编译器禁止默认的类型转换, 从而导致一些非法的参数被默认转换成为合法的参数. 我在opencv的头文件中见到了很多这个关键字, 可见这个关键字还是很重要的.
这是加上explicit关键字后的结果:
该错误在编译阶段就暴露出来 如果是在运行阶段出现这么一个错误,呵呵 累死你兴许也找不到原因!
下面是一个运算符重载的例子:
#include<iostream>
#include<stdlib.h>
using namespace std;
//一个复数类
class Complex
{
public:
Complex(){real=0,image=0;};
Complex(double,double);
Complex operator++(int);
Complex& operator++();
Complex& operator--();
Complex operator+(const Complex &);
Complex operator-(const Complex &);
Complex operator*(const Complex &);
//Complex& operator+=(Complex &);
//Complex& operator-=(Complex &);
//iostream & operator<<(iostream &);
//iostream & operator>>(iostream &);
double getReal();
double getimage();
//private:
double real;
double image;
};
Complex::Complex(double r,double i)
{
real=r;
image=i;
};
Complex& Complex::operator++()
{
real++;
image++;
return *this;
}
Complex Complex::operator++(int a)
{
Complex temp=*this;
real++;
image++;
return temp;
}
Complex& Complex::operator--()
{
real--;
image--;
return *this;
};
Complex Complex::operator+(const Complex &a)
{
return Complex(real+a.real,image+a.image);
};
Complex Complex::operator-(const Complex &a)
{
return Complex(real-a.real,image-a.image);
};
Complex Complex::operator*(const Complex &a)
{
return Complex(real*a.real,image*a.image);
};
/*Complex& Complex::operator+=(Complex &a)
{
real+=a.real;
image+=a.image;
return *this;
};*/
/*Complex& Complex::operator-=(Complex &a)
{
real-=a.real;
image-=a.image;
return *this;
};*/
Complex& operator+=(Complex &a,const Complex &b)
{
a.real+=b.real;
a.image+=b.image;
return a;
};
Complex& operator-=(Complex &a,const Complex &b)
{
a.real-=b.real;
a.image-=b.image;
return a;
};
ostream &operator<<( ostream &out,const Complex &a)
{
//out<<"("<<a.real<","<<a.image<<")"<<endl;
out<<"("<<a.real<<","<<a.image<<")";
return out;
};
istream &operator>>(istream &in,Complex &a)
{
in>>a.real>>a.image;
return in;
};
int main()
{
Complex a;
cout<<"请输入一个复数:"<<endl;
cin>>a;
cout<<"你输入的数是:"<<a<<endl;
Complex b;
cout<<"a:"<<a<<" b:"<<b<<endl;
b=a++;
cout<<"b=a++ :"<<b<<endl;
cout<<"a:"<<a<<" b:"<<b<<endl;
b=++a;
cout<<"b=++a :"<<b<<endl;
b=Complex(10,10);
Complex c;
c=a+b;
cout<<"a:"<<a<<" b:"<<b<<endl;
cout<<"a+b:"<<c<<endl;
c=a-b;
cout<<"a:"<<a<<" b:"<<b<<endl;
cout<<"a-b: "<<c<<endl;
c=a*b;
cout<<"a:"<<a<<" b:"<<b<<endl;
cout<<"a*b: "<<c<<endl;
cout<<"a:"<<a<<" b:"<<b<<endl;
a+=b;
cout<<"a+=b: "<<a<<endl;
cout<<"a:"<<a<<" b:"<<b<<endl;
a-=b;
cout<<"a-=b: "<<a<<endl;
//#ifndef COMPLEX_H
//cout<<"error!!"<<endl;
//#endif
system("pause");
return 0;
}
可能有人会说这个程序有什么特别的呢?
你可能会注意到这个程序里有好多引用& , 但有的函数返回值前加上了引用 ,有的没有加.
我先说一下 ,可能有些人不知道为什么要加引用,有时候在函数传参时加不加结果都是那,的确你的程序并没有错.
如果不加引用 ,一个函数在传参数的时候和返回的时候都会调用该对象所属类的拷贝构造函数构造出一个临时对象出来 . 如果这个类不是很复杂的话这点开销也许不算什么,但如果这个类很复杂的话这点开销你就得注意了.所以所有能加引用的地方尽量就使用它吧.
但我写的上面这个程序为什么有的地方 没有加引用了?
你仔细看就会发现这些函数的返回值,都有一个共性就是他们都返回了一个在该函数中创建的一个局部的变量. 该对象(变量) 在函数返回时生命周期结束,它所占用的内存空间就会被释放掉, 你所返回的东西已经不存在了 , 所以这里千万不能加引用.
不加引用 函数在返回时会构造出另为一个该类的对象,新对象的作用域和调用它的地方所在的作用域不相同,所有你就可以使用这个新的对象了,但必须在调用它的地方的作用域中新建一个对象调用它的复制函数来接收返回的临时对象.
函数在返回的时候会构造出一个新的对象 该对象也是临时的 , 也就是说在返回一个函数中的局部变量的时候不能用引用或指针来指向它 ,因为这样做的后果是不确定的
上面的东西是我在学习实践的过程中不断总结发现的, 随着知道的更多 对原来东西的理解也在变化.以上纯属个人见解,如果有不合理的地方我会慢慢改正的.