文章目录
- 缺省参数
- 函数重载
- 引用
- 引用和指针
- 内联
- 宏的优缺点
- auto
- 范围for
- NULL和nullptr
缺省参数
- 半缺省参数必须从右往左依次来给出,不能间隔着给
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
- 缺省参数不能在函数声明和定义中同时出现
- 缺省值必须是常量或者全局变量
函数重载
C++函数重载至少要满足,函数的返回值、函数的参数个数、函数的参数类型、函数不同参数类型的顺序至少有一个不一样 ;
原理:
生成可执行程序需要经过:预处理、编译、汇编、链接 四个阶段 。
在C语言专用的编译器中在: C语言 ——> 汇编代码 这个过程将函数名直接转为汇编代码 ;
而C++专用的编译器中在:C/C++语言 ——> 汇编代码 这个过程将函数名经过修饰转为汇编代码,Linux g++ 编译器修饰的方法为(不同的平台、编译器有不同的修饰方法,但都能支持C++重载的条件):
_Z+函数长度+函数名+类型首字母
引用
- 引用在定义时必须初始化;
- 引用一旦引用一个实体,再不能引用其他实体;
- 引用只能引用变量 ;
- 传引用返回要保证引用对象没有被销毁 (函数在运行结束后,函数生命周期内定义的变量的空间就会归还给操作系统处理);
- 能用引用传参和传返回值尽量用,因为不用拷贝运行效率更高;
- 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的
引用和指针
引用和指针的不同点:
- 引用概念上定义一个变量的别名,指针存储一个变量地址。
(在汇编代码层面原理相同) - 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节) - 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
内联
- 内联是一种建议,编译器不一定采用
- 使用内联函数,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率
- 内联函数定义和声明不要分离,因为展开是指函数体的展开,分离会导致链接错误,inline被展开,就没有函数地址了,链接就会找不到;
宏的优缺点
优点:
提高代码的可读性和可维护性:宏可以将一系列重复的代码片段封装起来,使代码更加简洁和易于理解。
增加代码的灵活性:宏可以根据不同的参数生成不同的代码,使代码具有更强的适应性和灵活性。
提高代码的复用性:宏可以在不同的地方多次使用,避免了重复编写相同的代码。
提高代码的效率:宏在编译时会被展开,不会引入额外的函数调用开销,可以在一定程度上提高代码的执行效率。
缺点:
宏的使用容易出错:宏展开后的代码可能会与预期不符,容易引入难以发现的错误。
宏的调试困难:宏在编译时展开,调试时无法直接查看宏展开后的代码,增加了调试的难度。
宏的可读性较差:宏展开后的代码可能会变得冗长和难以理解,降低了代码的可读性。
宏的滥用可能导致代码的可维护性下降:过度使用宏可能会导致代码的可维护性下降,增加了代码的复杂性和难度。
C++有哪些技术替代宏?
- 常量定义 换用const enum
- 短小函数定义 换用内联函数
auto
- 编译器在编译期会将auto替换为变量实际的类型
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须
加& - 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错
- auto不能作为函数的参数
- auto不能直接用来声明数组
范围for
范围for的使用条件:
-
容器或数组必须是一个可迭代的对象,即它必须实现了begin()和end()方法,用于返回迭代器指向容器或数组的起始和结束位置。
-
迭代器类型必须支持解引用操作,以便可以访问容器或数组中的元素
-
对于数组,range-based for循环会自动推导出数组的大小,并使用索引来遍历数组的每个元素。
-
对于数组需要知道范围
void TestFor() // 正确
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
}
void TestFor(int array[]) // 错误
{
for(auto& e : array)
cout<< e <<endl;
}
for循环原理:
编译器会根据range-based for循环的语法,将其转换为一个普通的for循环。对于容器类型,编译器会调用容器的 begin() 和 end() 方法来获取容器的起始和结束迭代器。这些迭代器用于遍历容器中的元素。对于数组类型,编译器会自动推导出数组的起始和结束位置,并使用索引来遍历数组的每个元素。在每次迭代中,编译器会将当前元素赋值给循环变量,然后执行循环体内的代码。
NULL和nullptr
- NULL是宏,定义为 ((void*)0) , nullptr为关键字
- nullptr和NULL所占空间相同