- 注册时间
- 2005-4-8
- 最后登录
- 1970-1-1
|
好久没来了,最近异常忙乱,每天精神都比较疲惫~~这篇算是我那个教程的补充,等过了这一段我再继续连载那个。
一、函数重载:
所谓重载,就是一个名字能干多个事情。函数重载的意思就是,使用同一个函数名,可以定义不同的函数。
就像这样:- int add( int a, int b);
- double add( double a, double b);
复制代码 使用同一个函数名add,我们定义了两个函数,分别对应整数的加法和双精度浮点数的加法,这样,在调用的使用,编译器会根据调用参数的不同自动选择要调用哪一个- int c=add(10,20);
- double t=add(10.3,40.2);
复制代码
下面就有几个疑问- int c=add(10.4,20.3);
- int d=add(10.3,20);
复制代码 这里的两个add会怎样调用呢?
对于第一个,它会调用double版本的,然后把结果转化为一个int。因为c++在判断该调用哪个重载的时候只看他的参数,而不会去理会他的返回值,由此也导致一件事情,就是参数类型完全相同,而返回值类型不同的时候是不会编译通过的。
对于第二个,你说该调用哪个呢?猜猜?
结果是哪个也不调用,编译器会抱怨不知道该怎么办,所以不要把代码写成这样。
在游戏设计中,一个很常见的行为就是画点,那么这个函数就可以被重载
- void DrawPoint(int x,int y);
- void DrawPoint(Point p);
复制代码
这样无论你使用分开的两个坐标,还是一个Point类型的变量,它都可以很好地被执行。
在实现上,对于一组完成相同功能的重载函数,一般会设定一个为主要函数,其他的尽量使用这个函数来完成,例如还是上面那个画点:- void DrawPoint(int x, int y)
- {
- //Your method to draw a point
- //for example:
- ::SetPixel(HDC,x,y);
- }
- void DrawPoint(Point p)
- {
- //implement using the above function
- DrawPoint(p.x,p.y)
- }
复制代码
这样可以保证最小的代码复制,提高了程序的健壮性,要知道,代码复制往往是恐怖的事情的开始。
二、运算符重载:
其实这个也是函数重载的一部分,不过由于它的特殊性,我把它单独列出来。
所谓运算符重载,在c++中的含义就是改变一个已经存在的运算符的意义,使它可以完成新的功能。要注意,c++中只能重载一个已经存在的运算符,并且只能赋予它新的意义,而不能改变它旧的意义,这一点一定要记住。
几乎所有的运算符都可以重载,目前只有. .* :: :? 四个运算符不可以重载,那么可以重载的运算符就剩下:
算术运算符:+ - * / % ++ --
位操作运算符:& | ~ ^ << >>
逻辑运算符:! && ||
比较运算符:< > >= <= == !=
赋值运算符:= += -= *= /= %= &= |= ^= <<= >>=
其他运算符:[] () -> ,(逗号运算符) new delete new[] delete[] ->*
如果没有玩过c++的人一定会被吓一跳,这么多运算符?其实这都不是全部,有时候类型转换也会被作为运算符加到这里面来。
不过这里我先按下复杂的不说,只讲讲最简单的几个关于算术操作的,先来看看加法吧。
作为一个运算符,我们一般会这样使用一个+这样的话,其实我们可以把这个加法看作一个拥有两个参数的函数,它的返回值就是两个参数的和,就像上面提到的add,其实这个+的函数原型是这样的:- int operator+(int a,int b);
- double operator+(double a,double b);
- .....
复制代码 c++为我们已经作了大量的重载,对于每一种基本类型,都定义了相关的operator+,可以让我们对他们使用+这个运算。那么对于我们自己的类型,定义的方式其实是一样的,下面我们定义一个复数类,它有两个成员。- class Complex
- {
- public:
- Complex(double r,double i)
- :real(r),ime(i) //这个叫成员初始化列表,把要初始化的成员列在这里
- //跟一个小括号中放它的初始值
- {
-
- }
- public:
- double real,ime;
- };
- [code]
- 他的加法应该这样定义:
- [code]
- //对于参数,这里使用常引用,这样可以保证c1,c2的值不会被改变
- //同时保证传递的时候不会发生拷贝
- //关于c++中的引用,请参见我前面写过的文章
- Complex operator+(const Complex& c1,const Complex& c2)
- {
- return Complex(c1.real+c2.real, c1.ime+c2.ime);
- }
复制代码 这样,当你需要把两个复数相加的时候,就可以直接写了:- Complex c1(10,20);
- Complex c2(2,4);
- Complex c3;
- c3=c1+c2;
复制代码
对于上面的代码,有一点需要解释,就是最后那句c3=c1+c2
编译器怎样知道应该怎么执行这个复值呢?它其实是自动把这个调用转化成了赋值运算符的调用,而我们又没有定义赋值运算符,所以编译器将使用位传送。对于c1+c2的运算结果,编译器将生成一个临时变量,然后将这个临时变量的值按位拷贝给c3。(当然,如果你暂时看不懂我说什么,就先放着吧)
学会了加法,你可以自己试试乘法和除法之类的,如果你使用过<iostream>的话,你对:一定不会陌生,这里其实就是ostream这个类型重载了他的<<运算符。
对于++和--,这里要讲一下。他们是一元运算符,所以我们一般将他们声明成类的成员- class MyInt
- {
- int v;
- public:
- MyInt(int value) : value(v){}
- MyInt operator++();
- MyInt& operator++(int a);
- }
复制代码
有以下几点需要解释:
1、把运算符声明成成员,那么运算符的第一个操作数就是this,也就是对象本身- class Complex
- {
- public:
- double real,ime;
- //这里作为成员,+的左操作数将作为函数的调用者
- Complex operator+(const Complex& c2);
- };
- void main()
- {
- Complex c1;
- Complex c2;
- c1+c2;//这里将调用c1.operator+(c2);
- }
复制代码 2、对于++的两种形式,一种是前缀,一种是后缀,这里使用一个假的参数来表示,没有参数的那个operator++将表示前缀的++,有参数的那个表示后缀的++- Complex c1;
- c1++; //这里调用c1.operator++()
- ++c1;//这里调用c1.operator++(int)
复制代码 3、对于++两种是形式的不同返回值,是基于这样的考虑:
对于后缀的++,是先将对象的值返回,然后再把自己加一,那么返回的就必须是一个临时变量,而临时变量只可以使用传值操作。
对于前缀的++,则先++,再返回,这样就可以返回对象本身。
(这段看不懂就算了)
三、成员函数重载:
一个class中的成员函数也是可以重载的- class Drawer
- {
- void DrawPoint(int x,int y);
- void DrawPoint(Point p);
- };
复制代码 其方法与函数重载并无二致,这里不再赘述。
四、小结:
对于c++的运算符重载,已经可以写一个很长的文章,甚至小册子了。这里只简要介绍几个比较简单的运算符。其实c++中的运算符重载应用十分广泛,有兴趣的人可以自己找书看看(前面不是有人推荐c++的书么)
另外从别人那里转几个要点:【转自http://www.yesky.com/406/1629406.shtml】1、运算符重载后,优先级和结合性怎么办?
用户重载新定义运算符,不改变原运算符的优先级和结合性。这就是说,对运算符重载不改变运算符的优先级和结合性,并且运算符重载后,也不改变运算符的语法结构,即单目运算符只能重载为单目运算符,双目运算符只能重载双目运算符。
2、重载运算符有哪些限制?
(1) 不可臆造新的运算符。必须把重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中。
(2) 重载运算符坚持4个“不能改变”。
·不能改变运算符操作数的个数;
·不能改变运算符原有的优先级;
·不能改变运算符原有的结合性;
·不能改变运算符原有的语法结构。
3、运算符重载时必须遵循哪些原则?
运算符重载可以使程序更加简洁,使表达式更加直观,增加可读性。但是,运算符重载使用不宜过多,否则会带来一定的麻烦。
使用重载运算符时应遵循如下原则:
(1) 重载运算符含义必须清楚。
(2) 重载运算符不能有二义性。 |
|