个人之言,请持怀疑态度参考。
C++有重载、内联、const和virtual 四种新的机制。本篇博客主要探究重载与内联。const用发可以参考我的另外一篇文章 点击这里
重载和内联机制,既可以用于全局函数也可用于类的成员函数;const 、virtual机制仅仅用于类的成员函数
重载
重载如何实现
通过函数的接口 - 参数 来区分。为什么不用返回值?因为返回值不可以100%保证区分。例如:
void fun(int a); int fun(int a); /******************/ fun(10);
上述,调用fun 谁知道你调用的是哪个? 在C/C++C程序中是可以忽略返回值的
编译器根据内部标识符来区分重载函数。编译器会根据参数为每个重载函数生成不同的内部标识符。 (类似与 _fun_int )
C语言是没有重载的,如果用要在C++中调用已经被编译后的C库,应该使用
extern "C" { 函数或者其他C头文件 }
告诉C++编译器,函数是C链接,应该按照 C 的函数标识符查找,而不是C++
注意:C++编译器开发商已经对C标准库的头文件做了extern “C” 处理,所以我们可以直接引用。并不是函数名字相同就能构成重载,必须有函数作用域相同的前提条件
隐式转换导致重载函数产生二义性
int func(int a)
{
cout << "int a" << endl;
return 0;
}
int func(float a)
{
cout << "float a" << endl;
return 0;
}
int main()
{
func(0.5);
getchar();
return 0;
}
上面的代码看起来似乎很合情理,但是编译器会报错( error C2668: “func”: 对重载函数的调用不明确)。原因在于0.5 既可以匹配double 又可以通过隐式转换成 int 。
成员函数的重载、覆盖(重写)、隐藏
对照表
重载 覆盖(重写) 隐藏 范围 相同的范围,在同一个类中 不同的范围,位于派生类和基类中 不同的范围,位于派生类和基类中 函数名字 相同 相同 相同 参数 不同 相同 不同【相同】 基类virtual关键字 可有可无 必须有 可有可无【没有】 注意:【】代表这隐藏出现的两种情况
举个例子来说明:
#include <iostream>
using namespace std;
class A
{
public:
A(){};
~A(){};
/*重载*/
void dis(int a)
{
cout <<"_dis_int"<< endl;
}
void dis(char a)
{
cout << "_dis_char_" << endl;
}
/*覆盖(重写)*/
virtual void func(int a)
{
cout << "基类func" << endl;
}
/*隐藏*/
void fun()
{
cout << "我在基类中 fun" << endl;
}
virtual void fun_c()
{
cout << "我在基类中 fun_c" << endl;
}
private:
};
class B :public A
{
public:
B(){};
~B(){};
virtual void func(int a)
{
cout << "派生类func" << endl;
}
void fun()
{
cout << "我在派生类中 fun" << endl;
}
void fun_c(int a)
{
cout << "我在派生类中 fun_c" << endl;
}
private:
};
int main()
{
A a;
B b;
/*使用重载*/
a.dis('a');
a.dis(10);
/*使用(覆盖)重写*/
a.func(1);
b.func(2);
/*隐藏 试着解释一下运行结果 */
a.fun_c();
b.fun_c(10);
a.fun();
b.fun();
/*猜猜这个结果是什么?*/
B* bb = &b;
A* aa = &b;
aa->func(1);
bb->func(2);
aa->fun();
bb->fun();
getchar();
return 0;
}
如果你亲自运行过上面的代码,一定会发现 aa->fun(); 结果和你想的不一样,哈哈 ,这就是坑。被隐藏函数的行为取决于指针的类型!!
- 隐藏存在的意义(了解就好了,写代码还是要避免这么烦人的隐藏): 解决多继承的问题,比如一个类有多个基类,有时也搞不清楚那些基类定义了fun(),直接隐藏就好了,哈哈,自己都给写迷糊了的代码估计是在写BUG.
函数内联
用内联取代宏代码
- 宏代码的优点:
预处理器用复制宏代码的方式代替函数调用,省去了参数压栈,call调用、参数返回、执行return等步骤(ps:可以去了解一下C/C++翻译成汇编语言后的执行细节),提高了速度。- 缺点:
容易掉坑里!哈哈。预理器无脑复制宏代码常常出现一些边际效应如优先级错误等等;还有就是没法操作类的私有成员,这在C_++中是致命的。
内联函数如何工作
如果编译器检查没有发现内联函数有错误,那么他除了会把函数声明放在符号表中,还会把函数的代码也一并放入符号表。在调用内联函数时,编译器会像执行普通函数一样进行类型安全检查、自动类型转换等等。如果检查没有错误,内联代码就会直接替换函数的调用,省去的函数调用的开销,而且还进行了类型检查。
内联的特点
inline
是一个‘用于定义式的关键字’,而不是想其他关键字一样‘用在声明式’。如果你想把一个函数变成内联函数,在函数声明前加inline
是没什么卵用的!(如果你还弄不清神马是定义式,神马是声明式,那么你需要赶快充电了)inline
只是对编译器的一个申请,不是强制命令。也就是说你写的函数能不能成为内联函数不是你说了算,最后还的由编译器综合考虑才决定能不能内联- 申请可以隐式的提出。将函数定义在类的定义式内其实就是在暗示编译器,来给我把这个函数弄成内联吧。
- 内联的代价就是造成程序的体积膨胀,导致执行效率减低。所以
inline
只是申请,由编译器来优化决定。其次,申请成为内联的函数应该尽可能的小(没有循环,递归等等重量级的操作)。- 内联函数如果修改,将导致该函数的使用者重新编译!在编译一个项目是,其实并不是每次都要把代码全部编译,好的代码设计在大多数情况下仅仅重新链接就好了。如果你要设计一个库,那么你一定要慎重考虑。
- 大部分调试器对内联函数是没法调试的。因为内联函数直接将本体编进了程序,实际上这个函数并不存在,自然也没法跟踪调试了。(ps:内联函数是没法取到函数地址的)
有时侯编译器在进行内联的时候,还可能产生内联函数的本体。
inline void f(){} void ( * ps)() =f; f(); //会被内联 ps(); //不会被内联
前提是编译器同意对f() 采用内联策略
程序中要取f()的地址,编译器会生成一个内联的本体,毕竟不肯呢给你>随便编造一个地址出来。
SO 编译器通常不会对通过指针调用的函数实施内联大法。 正常调用的话还是会被内联的,比如上面代码中 f(); 直接调用。
内联函数的误区
- 类的构造析构函数使用内联更有效
这个想法是错误的!类的构造析构函数看起来没做什么事,其实是编译器把所有细节隐藏了!C++对‘对象的创建和销毁做了各种保证’,而且对象允许多种方式创建。这些行为其实都是在编译是,编译器自动把代码插在了构造器和析构器中,至少不会凭空发生。- 内联函数用的越多越好
错,从他的特性可以看出是要谨慎使用的
最后一段补充 80 - 20经验法则
程序往往把80%的时间化在20%的代码上。我们要做的是找出20%去优化,而不是瞎折腾其他没有什么影响的代码
软件开发根据我的个人经验,当效率不是问题的瓶颈时,更应考虑软件的整体逻辑,而不是抓着某一点死磕到底。所以内联只有在优化阶段需要优化效率时才考虑。
我的个人网站 http://www.breeziness.cn/
我的CSDN http://blog.csdn.net/qq_33775402
转载请注明出处 小风code www.breeziness.cn