目录
函数重载:
函数重载概念:
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
参数类型不同:
cpp
#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;
}
参数个数不同:
cpp
#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;
}
参数类型顺序不同:
cpp
#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++是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
引用:
引用概念:
用一种新的方式给一个已经定义的变量重新起一个别名,表示方式和指针相似,不同的是用&代替了*,对引用的操作与对变量直接操作完全一样。
(表示方式:类型 &引用变量名 = 已定义过的变量名)
引用特性:
引用在定义时必须初始化
一个变量可以有多个引用
引用一旦引用一个实体,再不能引用其他实体
cpp
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);
}
常引用:
引用权限不能放大:
cpp
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;
}
使用场景:
做参数:
cpp
void swap(int &a,int &b)
{
int tmp = a;
a = b;
b = tmp;
}
做返回值:
传值、传引用效率比较:
cpp
#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,指针自加即指针向后偏移一个类型的大小。
底层:
cpp
int main()
{
int a = 10;
int& ra = a;
int* pa = &a;
return 0;
}
通过反汇编代码可知,二者在底层是没区别的
内联函数:
- C和Cpp对于频繁调用的小函数的处理方式:
cpp
//C->宏定义
#define ADD(a, b) ((a) + (b))
cpp
//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被展开,就没有函数地址了,链接就会找不到。