C++入门(二)

时间:2024-04-14 14:06:38

目录

函数重载:

函数重载概念:

参数类型不同:

参数个数不同:

参数类型顺序不同:

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被展开,就没有函数地址了,链接就会找不到。