文章目录
缺省参数
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
void TestFunc(int a = 0)
{
cout<<a<<endl;
}
int main() {
TestFunc(); // 没有传参时,使用参数的默认值
TestFunc(10); // 传参时,使用指定的实参
}
缺省参数分类
- 全缺省参数
void TestFunc(int a = 10, int b = 20, int c = 30)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
- 半缺省参数
void TestFunc(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
总结:
- 半缺省参数必须从右往左依次来给出,不能间隔着给
- 缺省参数不能在函数声明和定义中同时出现,只能出现一次
// a.h
void TestFunc(int a = 10);
// a.c
void TestFunc(int a = 20){}
// 如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。
- 缺省值必须是常量或者全局变量
- C语言不支持(编译器不支持)
内联函数
C++ 语言支持函数内联,其目的是为了提高函数的执行效率(速度)。
以 inline 修饰的函数叫做内联函数,编译时 C++编译器会 在调用内联函数的地方展开,没有函数压栈的开销, 内联函数提升程序运行的效率。
- 在调用一个内联函数时,编译器首先检查调用是否正确(进行类型安全检查,或者进行自动类型转换,当然对所有的函数都一样)。如果正确,内联函数的代码就会直接替换函数调用,于是省去了函数调用的开销。这个过程与预处理有显著的不同,因为预处理器不能进行类型安全检查,或者进行自动类型转换。假如内联函数是成员函数,对象的地址(this)会被放在合适的地方,这也是预处理器办不到的。
- C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码。“断言assert”恐怕是唯一的例外。assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。如果assert是函数,由于函数调用会引起内存、代码的变动,那么将导致Debug版本与Release版本存在差异。所以assert不是函数,而是宏。
内联函数的编程风格:
关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。如下风格的函数Foo不能成为内联函数:
inline void Foo(int x, int y); // inline仅与函数声明放在一起
void Foo(int x, int y)
{
…
}
而如下风格的函数Foo则成为内联函数:
void Foo(int x, int y);
inline void Foo(int x, int y) // inline与函数定义体放在一起
{
…
}
所以说,inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的声明、定义体前面都加了inline关键字,但我认为inline不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。
定义在类声明之中的成员函数将自动地成为内联函数,例如
class A
{
public:
void Foo(int x, int y) { … } // 自动地成为内联函数
}
将成员函数的定义体放在类声明之中虽然能带来书写上的方便,但不是一种良好的编程风格,上例应该改成:
// 头文件
class A
{
public:
void Foo(int x, int y);
}
// 定义文件
inline void A::Foo(int x, int y)
{
…
}
如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联:
- 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
- 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。
总结:
- inline是一种以空间换时间的做法,省去调用函数的开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。(当内联函数被调用的时候不会创建新的栈,而是在调用处展开)
- inline对于编译器而言只是一个建议,编译器会自动优化。如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
- inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。(内敛函数具有文件作用域)
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include “F.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp
#include “F.h"
int main()
{
f(10);
return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (? f@@YAXH@Z),该符号在函数 _main 中被引用
宏的优缺点?
宏常量 ------ #define MAX_SIZE 100
-
优点:
- 一改全改(不需要针对代码中的所有相同常量重新修改)
- 常量名字具有一定的含义
-
缺点:
- 宏常量没有类型(不会参与到类型检测中,导致代码安全性降低),编译器一旦报错,报错的位置不明确
宏函数 ------ #define MAX(a, b) (a > b ? a : b)
-
优点:
- 宏函数不是一个真正的函数,在预处理阶段,预处理器已经将宏函数采用宏体进行替换了,少了函数调用参数压栈、开辟栈帧、返回等的开销了,代码的运行效率提高了
-
缺点:
- 在实现时,可能会比较麻烦(需要到处加括号)
- 宏函数也没有参数类型(不会有参数类型检测,安全性不高)
- 宏函数在预处理阶段会展开(不能调试、代码膨胀)
- 宏函数具有副作用 —
MAX(++a, b)(调用了两次++,本该返回一次++的值)
总结优点:
- 增强代码的复用性。(宏常量)
- 提高性能。(宏函数)
总结缺点:
- 不方便调试宏。(因为预编译阶段进行了替换)
- 导致代码可读性差,可维护性差,容易误用。
- 没有类型安全的检查 。
C++有哪些技术替代宏?
- 常量定义 换用 const常量:可以达到宏替换的效果(而且有类型检测,更加安全)
在C语言中,被const修饰的变量仍然是变量,只是具有常属性(该变量的值不能修改),const 常量的替换发生在编译时。
在C++中,被const修饰的变量已经是常量,而且还具有宏替换的属性(const常量的替换发生在编译时)
#include <iostream>
using namespace std;
int main(){
const int a; // 在C++中错误,在C语言中可以
return 0;
}
- 函数定义 换用 内联函数:除了宏函数所带来代码膨胀的问题,其余问题都能解决~~
知识点习题:
1.关于c++的inline关键字,以下说法正确的是()
A. 使用inline关键字的函数会被编译器在调用处展开
B. 头文件中可以包含inline函数的声明
C.可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数
D. 定义在Class声明内的成员函数默认是inline函数
E. 优先使用Class声明内定义的inline函数
F. 优先使用Class实现的内inline函数的实现
正确答案:
D
答案解析:
A 项错误,因为使用 inline 关键字的函数只是用户希望它成为内联函数,但编译器有权忽略这个请求,比如:若此函数体太大,则不会把它作为内联函数展开的。
B 项错误,头文件中不仅要包含 inline 函数的声明,而且必须包含定义,且在定义时必须加上 inline 。【关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用】
C 项错误, inline 函数可以定义在源文件中,但多个源文件中的同名 inline 函数的实现必须相同。一般把 inline 函数的定义放在头文件中更加合适。
D 项正确,类内的成员函数,默认都是 inline 的。【定义在类声明之中的成员函数将自动地成为内联函数】
EF 项无意思,不管是 class 声明中定义的 inline 函数,还是 class 实现中定义的 inline 函数,不存在优先不优先的问题,因为 class 的成员函数都是 inline 的,加了关键字 inline 也没什么特殊的。
- 以下程序输出结果是__。
class A
{
public:
virtual void func(int val = 1)
{ std::cout<<"A->"<<val <<std::endl;}
virtual void test()
{ func();}
};
class B : public A
{
public:
void func(int val=0)
{std::cout<<"B->"<<val <<std::endl;}
};
int main(int argc ,char* argv[])
{
B* p = new B;
p->test();
return 0;
}
A. A->0
B. B->1
C. A->1
D. B->0
E. 编译出错
F. 以上都不对
正确答案:
B
答案解析
virtual 函数是动态绑定,而缺省参数值却是静态绑定。 意思是你可能会在“调用一个定义于派生类内的virtual函数”的同时,却使用基类为它所指定的缺省参数值。
结论:绝不重新定义继承而来的缺省参数值! (可参考《Effective C++》条款37)
如有不同见解,欢迎留言讨论