目录
函数重载:
函数重载概念:
参数类型不同:
参数个数不同:
参数类型顺序不同:
C++支持函数重载的原理--名字修饰:
引用:
引用概念:
引用特性:
常引用:
使用场景:
做参数:
做返回值:
传值、传引用效率比较:
编辑
引用和指针的区别:
语法:
底层:
内联函数:
特性:
函数重载:
函数重载概念:
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
参数类型不同:
#include <iostream>
using namespace std;
void Node(int a ,char b)
{
cout << "Node(a, b)" << endl;
}
void Node(int a ,int b )
{
cout << "Node(a,b)" << endl;
}
参数个数不同:
#include <iostream>
using namespace std;
void Node(int a ,int b)
{
cout << "Node(a, b)" << endl;
}
void Node(int a ,int b ,int c)
{
cout << "Node(a,b)" << endl;
}
参数类型顺序不同:
#include <iostream>
using namespace std;
void Node(int a ,char b)
{
cout << "Node(a, b)" << endl;
}
void Node(char a ,int b )
{
cout << "Node(a,b)" << endl;
}
C++支持函数重载的原理--名字修饰:
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接
预处理:展开头文件、宏替换、条件编译......生成.i文件编译:检查语法、生成汇编代码生成.s文件汇编:把汇编代码转成二进制机器码生成.o .obj文件链接:生成可执行程序生成.exe文件
实际项目通常是由多个头文件和多个源文件构成,当前a.cpp中调用了b.cpp中定义的Add函数时,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。而链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
引用:
引用概念:
用一种新的方式给一个已经定义的变量重新起一个别名,表示方式和指针相似,不同的是用&代替了*,对引用的操作与对变量直接操作完全一样。
(表示方式:类型 &引用变量名 = 已定义过的变量名)
引用特性:
引用在定义时必须初始化一个变量可以有多个引用引用一旦引用一个实体,再不能引用其他实体
void Node()
{
int a = 10;
int& a1 = a;
int& a11 = a;
int& a111 = a1;
printf("%p\n%p\n%p\n%p\n", &a, &a1,&a11,&a111);
}
常引用:
引用权限不能放大:
void TestConstRef()
{
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d;
}
使用场景:
做参数:
void swap(int &a,int &b)
{
int tmp = a;
a = b;
b = tmp;
}
做返回值:
传值、传引用效率比较:
#include <iostream>
#include <time.h>
using namespace std;
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc1(A&)-time:" << end2 - begin2 << endl;
}
int main()
{
TestRefAndValue();
return 0;
}
二者在运行效率上相差很大
引用和指针的区别:
语法:
- 引用概念上定义一个变量的别名,指针存储一个变量地址。
- 引用在定义时必须初始化,指针没有要求。
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
- 没有NULL引用,但有NULL指针。
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)。
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
底层:
int main()
{
int a = 10;
int& ra = a;
int* pa = &a;
return 0;
}
通过反汇编代码可知,二者在底层是没区别的
内联函数:
- C和Cpp对于频繁调用的小函数的处理方式:
//C->宏定义
#define ADD(a, b) ((a) + (b))
//Cpp->内联函数
#include <iostream>
using namespace std;
inline int Add(int x,int y)
{
return x+y;
}
int main()
{
int ret = 0;
ret = Add(1,2);
cout << ret << endl;
return 0;
}
特性:
- inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
- inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不 是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
- inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。