C++函数的高级特征
重载(overloade)、内联(inline)、const和virtual是C++独有的而C不具备的四种机制。其中重载和内联机制既可以用于全局函数也可以用于类的成员函数,const与virtual机制仅用于类的成员函数。
1->函数重载:在C++程序中,可以将语义、功能相似的几个函数用同一个名字表示。
重载的实现方法:函数重载只能靠参数而不能靠返回值类型不同来区分重载函数。编译器根据参数为每个重载函数产生不同的内部标识。例如:int Func(int x,int y)在C++中被编译成Func_int_int,而C语言不提供函数的重载,编译后的名称为Func。
经典面试题:如果C++程序要调用已经被编译后的C函数,该如何解决?
C++程序不能直接调用已经编译后的C函数,这是因为名称的问题。举个例子:一个函数叫做void foo(int x,int y),该函数被C编译器编译后再库中的名字为_foo,而C++则产生_foo_int_int之类的名字用来支持函数重载和类型安全连接,名称就不一样,因此不能直接调用。
C++提供了一个C连接交换指定符号extern "C"来解决这个问题
例如:
extern “C”
{
void foo(int x,int y);
}
这就告诉C++编译器,函数foo是个C连接,应该到库中找名字_foo而不是找_foo_int _int.
另外还需要注意的是:并不是两个函数的名字相同就能够构成重载。全局函数和类的成员函数同名就不算重载,因为函数的作用域不同。
2->小心隐式转换导致重载函数产生二义性
例如:
#include <iostream.h>
void output(int x); void output(float x);
void output(int x){cout<<"output int"<<endl;} void output(flaot x){cout<<"output float"<<endl;}
void main(){
int x=1; float y=1.0; output(x); //output int 1
output(y); //output flaot 1 output(1); //output int 1
output(0.5); //error! ambiguous call 自动类型转换产生错误
output(int(0.5)); output(float(0.5));
}
3->成员函数的重载覆盖和隐藏
成员函数被重载的特征:1,相同的范围(在同一类中) 2,函数名字相同 3,参数不同 4,virtual关键字可有可无
覆盖是指派生类函数覆盖基类函数,特征:1,不同的范围(分别位于派生类与基类)2,函数名字相同 3,参数相同 4,基类函数必须有virtual关键字
下面例子,函数Base::f(int) 和Base::f(float)相互重载,而Base::g(void)被Derived::g(void)覆盖
Class Base{
public:
void f(int x){cout<<"Base::f(int)"<<x<<endl;}
void f(float x){cout<<"Base::f(float)"<<x<<endl;}
virtual void g(void){cout<<"Base::g(void)"<<endl;}
};
class Derived:pubic Base{
public:
virtual void g(void){cout<<"Derived::g(void)"<<endl;}
};
4->隐藏规则
隐藏是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
a):如果派生类的函数与基类的函数同名,但是参数不同,此时,不论有无virtual关键字。基类的函数都将被隐藏
b):如果派生类的函数与基类的函数同名,并且参数也相同。但基类函数没有virtual关键字,此时基类的函数同样被隐藏
例如下面给出的例子:
1):函数Derived::f(float) 覆盖了Base::f(float)
2):函数Derived::g(int) 隐藏了Base::g(float),而不是重载
3):函数Derived::h(float)隐藏了Base::h(float),而不是覆盖
Class Base{
public:
virtual void f(float x){cout<<"Base::f(float)"<<x<<endl;}
void g(float x){cout<<"Base::g(float)"<<x<<endl;}
void h(flaot x){cout<<"Base::h(flaot)"<<x<<endl;}
};
class Derived:public Base{
public:
virtual void f(float x){cout<<"Derived::f(float)"<<endl;}
void g(int x){cout<<"Derived::g(int)"<<x<<endl;}
void h(float x){cout<<"Derived::h(float)"<<endl;}
};
void main(void){
Derived d; Base *pb=&d; Derived *pd=&d;
pb->f(3.14f); //Derived::f(flaot) 3.14
pd->f(3.14f); //Derived::f(float) 3.14
pb->g(3.14f); //Base::g(flaot) 3.14
pd->g(3.14f); //Derived::g(int) 3
pb->h(3.14f); //Base::h(float) 3.14
pd->h(3.14f); //Derived::h(float) 3.14
}
摆脱隐藏:
Class Base{
public:
void f(int x);
};
class Derived:public Base{
public:
void f(char *str);
};
void Test(void){
Derived *pd=new Derived; pd->f(10); //ERROR
}
这个程序原本想调用Base::f(int x);但是被Derived中的f(char *)所隐藏。
所以将上述程序改为:
Class Derived:public Base{
public:
void f(char *str); void f(int x){Base::f(x);}
}
4->函数的缺省值
a):参数缺省值只能出现在函数的声明中,而不能出现在定义体中
void Foo(int x=0,int y=0); //正确
void Foo(int x=0,int y=0){} //错误
b):如果函数有多个参数,参数只能从后向前挨个缺省,否则将导致函数的调用出错
5->运算符的重载
在C++语言中,可以用operator加上运算符表示函数,就叫做运算符重载
如果运算符被重载为全局函数,那么只有一个参数的运算叫做一元运算符,有两个参数的运算符叫做二元运算符。如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为自己成了左侧参数。
运算符重载:
所有的一元运算符:建议重载为成员函数 =()[] -> 只能重载为成员函数 += -= /= *= &= |=等建议重载为成员函数 所有其他运算符 建议重载为全局函数
其中. * : ? 不能重载
6->内联函数
a):用内联取代宏代码(提高函数的执行效率)
b):inline必须与函数的定义体放在一块,如果与函数的声明放在一块将不能产生任何效果