操作符函数的名字:
操作符函数的参数的个数:
成员函数 vs. 非成员函数
为什么使用操作符重载?
如何声明一个重载的操作符?
重载的操作符在类体中被声明,声明方式如同普通成员函数一样,只不过他的名字包含关键字operator,以及紧跟其后的一个c++预定义的操作符。
可以用如下的方式来声明一个预定义的==操作符:
class person{ private: int age; public: person(int a){ this->age=a; } inline bool operator ==(const person &ps) const; };
实现方式如下: inline bool person::operator==(const person &ps) const { if (this->age==ps.age) return true; return false; } 调用方式如下: #include<iostream> using namespace std; int main() { person p1(10); person p2(20); if(p1==p2) cout<<”the age is equal!”< return 0; }
这里,因为operator ==是class person的一个成员函数,所以对象p1,p2都可以调用该函数,上面的if语句中,相当于p1调用函数==,把p2作为该函数的一个参数传递给该函数,从而实现了两个对象的比较。
考虑如下的if语句:
if(10==p1) cout<<”the age is equal!”<<endl;
是否回正确执行呢?
答案是不会的,因为只有左操作数是该类类型的对象的时,才会考虑作为类成员重载操作符。因为10不是person类型的对象,所以,不能调用classperson的操作符==。
考虑如下if语句:
if(person(10)==person(11))
cout<<"ok"<<endl;
是否能够正确执行呢?答案是可以,因为操作符两边均是无名对象。
/*实现下标操作符*/ #include #include using namespace std; class person { private: int tel; public: int & operator[](string const & nm) { return tel; } int GetTel() { return tel; } }; int main() { person p1; p1["suo"]=110; person p2; p2["rose"]=120; cout< cout< return 0; }
对于重载为成员函数方式的操作符,隐式的this指针被作为该函数的第一个参数,来代表左操作数。
B:操作符重载实现为非类成员函数(全局函数)
对于全局重载操作符,代表左操作数的参数必须被显式指定。例如:
#include #include using namespace std; class person { public: int age; public: };
/*在类的外部,不能访问该类私有数据,所以,要把
age设置为public*/
bool operator==(person const &p1 ,person const & p2) { if(p1.age==p2.age) return true; return false; } int main() { person rose; person jack; rose.age=18; jack.age=23; if(rose==jack)/*两个对象分别代表左右操作数*/ cout<<"ok"< return 0; }
C:如何决定把一个操作符重载为类成员函数还是全局名字空间的成员呢?
①如果一个重载操作符是类成员,那么只有当与他一起使用的左操作数是该类的对象时,该操作符才会被调用。如果该操作符的左操作数必须是其他的类型,则操作符必须被重载为全局名字空间的成员。
②C++要求赋值=,下标[],调用(), 和成员指向-> 操作符必须被定义为类成员操作符。任何把这些操作符定义为名字空间成员的定义都会被标记为编译时刻错误。
③如果有一个操作数是类类型如string类的情形那么对于对称操作符比如等于操作符最好定义为全局名字空间成员。
D:操作符重载为友元函数方式
如果把操作符重载为友元函数方式,则在该函数的内部,可以直接访问授权类的私有数据成员,这是友元函数方式和全局名字空间方式的主要区别。
E:怎样判断一个非类成员的操作符应该是类的友元还是应该使用成员访问函数呢?
一般来说,类的实现者应该尽量使得名字空间函数和访问类内部表示的操作符的数目最小化。如果已经提供了访问成员函数并且它们具有等同的效率,那么最好是使用这些成员函数。但是如果类的实现者决定不为该类的某些私有成员提供访问成员函数而且名字空间操作符需要引用这些私有成员才能完成,它们的操作那么就必须使用友元机制。
#include #include using namespace std; class person{ public: int age; public: };
bool operator==(person const &p1 ,person const & p2) { if(p1.age==p2.age) return true; return false; } ostream operator<<(ostream &os,person const &p) { os<<"the person age is:"< return os; } int main() { person rose; person jack; rose.age=18; jack.age=23; cout< /*call ostream operator<<(ostream &os,person const &p) */ cout< return 0; }
重载规则:
重要的几点:
#include #include using namespace std; class person { private: int age; public: person(int a) { age=a; } person const operator++()/*prefix ++ */ { this->age++; return *this; } person const operator++(int )/*postfix ++ */
{
person temp(1);
temp=*this;
this->age++;
return temp; //返回的是增加前的对象
} int GetAge() { return age; } }; int main() { person rose(10); person jack(20); person marry(22); person tom(30); jack=++rose; marry= tom++; cout< cout< return 0; }
转一篇文章:
在句法上,重载函数是通过它们的参数类型的差异区分的,但是不管是前缀形式还是后缀形式的自增和自减都没有参数,我们到底该怎么区分它们呢?在开始时,C++在语法上面确实是存在这个问题的,程序员对此颇有微词。后来C++中加了一些特性来解决这个问题。
C++规定后缀形式有一个int类型的参数,当函数被调用时,编译器传递一个0作为int参数的值给该函数。
且看下面的小程序:
#include <iostream> class MyInt{ public: MyInt(int a):i(a) { } MyInt& operator++(); // prefix ++ const MyInt operator++(int); // postfix ++ MyInt& operator--(); // prefix -- const MyInt operator--(int); // postfix -- friend std::ostream& operator<<(std::ostream&,const MyInt&); private: int i; }; MyInt& MyInt::operator++() { this->i++; return *this; } const MyInt MyInt::operator++(int) { const MyInt temp = *this; ++(*this); return temp; } std::ostream& operator<<(std::ostream& out,const MyInt& t) { out << t.i ; return out; } int main() { MyInt a(0); a++; std::cout << a << std::endl; // i = 1,print 1 ++a; std::cout << a << std::endl; // i = 2,print 2 std::cout << a++ << std::endl; // i = 3,print 2 std:: cout << ++a << std::endl; // i = 4,print 4 return 0; }
看上面的程序可以发现以下几点:
1. 后缀形式的参数并没有被用到,它只是语法上的要求,为了区分而已;
2. 后缀形式返回一个const对象;这样做是大有深意滴~~ 我们知道操作符重载本质上是一个函数而已,它应该和操作符原来意义、使用习惯相似,而对于int内置类型来说的话,i++++是有语法错误的,故为了保持一致,重载之后的后缀形式也应该是这样的。假如返回的不是const类型,那么对于
MyInt t(1);
t++++; // t.operator++(0).operator++(0)
这样的形式将是正确的,这显然不是我们期望的。另外,只要看上面的后缀形式的定义即可知道,这样写t只是增加了1而不是我们期望的2,为什么呢?因为第二次的调用是用第一次返回的对象来进行的,而不是用t,它违反了俺们程序员的直觉。因此,为了阻止这些行为,我们返回const对象。
3. 我们应该尽可能调用前缀形式;为什么呢?看看后缀形式的定义就可以知道,我们定义了一个临时对象,临时对象的创建、析构还是很费时间的。而前缀形式则不一样,它的效率应该相对好一些。
深入探究:
http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html
http://en.wikibooks.org/wiki/C++_Programming/Operators/Operator_Overloading
2015-03-02:
operator它有两种用法,一种是operator overloading(操作符重载),一种是operator casting(操作隐式转换)。
operator casting
C++可以通过operator 重载隐式转换,格式如下: operator 类型T (),如下所示
class A
{
public:
operator B* () { return this->b_;}
operator const B* () const {return this->b_;}
operator B& () { return *this->b_;}
operator const B& () const {return *this->b_;}
private:
B* b_;
};
A a;
当if(a),编译时,其中它转换成if(a.operator B*()),其实也就是判断 if(a.b_)
类型转换操作符(type conversion operator)是一种特殊的类成员函数,它定义将类类型值转变为其他类型值的转换。转换操作符在类定义体内声明,在保留字 operator 之后跟着转换的目标类型。boost::ref和boost::cref就使用到了类型转换操作符。
函数原型
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
1. 转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空;返回值是隐含的,返回值是与转换的类型相同的,即为上面原型中的T2;
2. T2表示内置类型名(built-in type)、类类型名(class type)或由类型别名(typedef)定义的名字;对任何可作为函数返回类型的类型(除了 void 之外)都可以定义转换函数,一般而言,不允许转换为数组或函数类型,转换为指针类型(数据和函数指针)以及引用类型是可以的;
3. 转换函数一般不应该改变被转换的对象,因此转换操作符通常应定义为 const 成员;
4. 支持继承,可以为虚函数;
5. 只要存在转换,编译器将在可以使用内置转换的地方自动调用它;
#include <iostream> using namespace std; class D{ public: D(double d): d_(d){} operator int()const{ cout<<"(int)d called!!"<<endl; return static_cast<int>(d_); } private: double d_; }; int add(int a,int b){ return a+b; } int main(){ D d1=1.1; D d2=2.2; cout<<"add(d1,d2)="<<add(d1,d2)<<endl; return 0; } (int)d called!! (int)d called!! add(d1,d2)=3 Press any key to continue
类型转换构造函数(conversion constructor)
先来说下类型转换构造函数:C++中的explicit用来修饰类的构造函数,表明该构造函数是显示的,既然有显示的,那么就有隐式的
若果一个类的构造函数时一个单自变量的构造函数,所谓的单自变量是可能声明一个单一参数,也可能声明一个拥有多个参数,并且除了第一参数外都其他参数都有默认值
这样的constructor称为单自变量constructor.
若果类中有这样一个constructor那么在编译的时候编译器将会产生一个省却的操作:将该constructor参数对应 的 数据类型 的 数据转换为该类的对象
class MyClass
{
public:
MyClass( int num );
}
....
MyClass obj = 10; //ok,convert int to MyClass
在上面的操作中编译器其实产生代码如下:
Myclass temp(10);
Myclass obj=temp;
若果要避免编译器产生上诉的隐式转换,那么此时explicit将产生作用。
explicit的作用:
explicit关键字将作用在类的构造函数,被修饰的构造函数,将再不能发生隐式转换了,只能以显示的进行类型转换
explicit 的注意:
只能作用在类的内部的构造函数上
只能作用在单自变量的构造函数上。