目录
5.2 C++支持函数重载的原理——名字修饰(name Mangling)
1.C++关键字(C++98)
C++总计63个关键字,而C语言32个关键字
2.命名空间(namespace)
在C/C++当中,变量,函数,以及C++中的类都是大量存在的。这些变量,函数,类都将处于全局域当中,会导致许多冲突。而使用命名空间对这些变量,函数,类这些标识符名称进行本地化,来避免出现命名冲突和名字污染。这个时候就出现了关键字------namespace
在C语言中,我们可能会因为创建的变量跟库函数名字相同而导致链接错误
#include<stdlib.h>
#include<stdio.h>
int main()
{
int rand=0;
//C语言是没有办法解决这样的问题的,C++则使用namespace来解决
printf("%d\n",rand);
return 0;
}
//因为定义的变量rand和库函数相同,导致链接错误
2.1 命名空间的定义
定义命名空间,需要使用namespace,后面跟空间名,最后加上一对{},括号里面的就是命名空间里的成员
三个特点
1.正常定义命名空间
定义的命名空间可以定义 函数/变量/类型。
namespace xh { //在命名空间中可以定义 变量/类型/函数 int Add(int a,int b) { return a+b; } int a=0; struct Node { struct node* next; int val; } }
2.命名空间可以嵌套定义
namespace xh { //在命名空间中可以定义 变量/类型/函数 int Add(int a,int b) { return a+b; } int a=0; struct Node { struct node* next; int val; } //命名空间可以嵌套定义 namespace xh2 { int val; } }
3.命名空间相同名称时,编译器会将其合并为一个命名空间
namespace xh { //在命名空间中可以定义 变量/类型/函数 int Add(int a,int b) { return a+b; } int a=0; struct Node { struct node* next; int val; } } namespace xh { int val; } //在出现多个名称相同的命名空间时,编译器会将其合并
重点:每定义一个命名空间就是创建出一个新的作用域,域中的所有内容都局限与该命名空间中
2.2 命名空间的使用
三种方式
1. 命名空间名称加作用域限定符
namespace hj
{
int a=0;
}
int main()
{
printf("%d\n",hj::a);
return 0;
}
2.使用using 将命名空间成员引入
namespace hj
{
int a=0;
int b=1;
}
using hj::a;
int main()
{
printf("%d\n",hj::b);
printf("%d\n",a);
return 0;
}
3.使用using namespace 命名空间名称 展开命名空间
namespace hj
{
int a=0;
int b=1;
}
using namespace hj;
int main()
{
printf("%d\n",b);
printf("%d\n",a);
return 0;
}
3.C++输入or输出
使用C++打个招呼
#include<iostream>
using namespace std;
//std是C++标准库的命名空间名,C++将标准库的定义和实现都放到这个命名空间里
int main()
{
cout<<"hello C++"<<endl;
return 0;
}
说明:
1.使用cout标准输出对象(控制台)和cin标准输出对象(键盘),需要包含<iostream>头文件以及按命名空间使用方法正确使用std。
2.cout和cin是全局的流对象,endl表示换行输出,它们都包含在<iostream>头文件当中
3.使用C语言输出需要手动控制输入输出的类型,C++会自动识别变量类型输入输出
4.<<是流插入操作符,>>是流提取操作符
<iostream>
using namespace std;
int main()
{
int a = 10;
char b;
double c;
cin >> a;
cin >> b >> c;
cout << a << endl;
cout << b << " " << c << endl;
}
std命名空间使用惯例:
1.在日常练习中使用using namespace std即可,方便
2.但是在做项目时,使用上面的方法就不妥了,可能会和库中的命名/对象/函数重名。使用std::cout使用时指定命名空间和using std::cout展开常用命名空间的方式
4.缺省参数
4.1缺省参数概念
缺省参数是在声明和定义函数时为函数参数指定一个缺省值,在调用函数时,没有指定实参则使用参数的缺省值,否则使用指定的实参。
int Add(int a=0, int b=0)
{
return a + b;
}
int main()
{
Add();
//未指定实参,Add函数采用缺省值
Add(1, 2);
//指定实参,采用实参
return 0;
}
4.2缺省参数分类
1.全缺省参数
void Test(int a = 0, int b = 0, int c = 0)
{
cout << a << endl << b <<endl<< c << endl;
}
2.半缺省参数
void Test(int a, int b = 0, int c = 0)
{
cout << a << endl << b << endl << c << endl;
}
注意:
1.半缺省参数需要从右往左依次给,不能间隔着给
2.缺省参数不能在函数声明和定义中同时出现
void Test(int a=10);
void Test(int a=20)
{
}
//如果在两个地方定义,恰好它们的值又不相同,那么编译器就不确定该用那个值
3.缺省值必须是常量或者全局变量
4.C语言不支持
5.函数重载
5.1 函数重载概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域内定义几个功能类似的同名函数,这些同名函数的新参列表(参数类型/参数个数/参数类型顺序)不同。
#include<iostream> using namespace std; 参数类型不同 void Add(int a,int b) { cout << "Add(int a,int b)" << endl; } void Add(double a,double b) { cout << "Add(double a,double b)" << endl; } 参数个数不同 void Add(int a) { cout << "Add(int a)" << endl; } void Add() { cout << "Add()" << endl; } 参数类型顺序不同 void Add(char a, int b) { cout << "Add(char a, int b)" << endl; } void Add(int a, char b) { cout << "Add(int a, char b)" << endl; } int main() { Add(); Add(1); Add(1, 2); Add(1.1, 2.2); Add('a', 10); Add(10, 'a'); return 0; }
5.2 C++支持函数重载的原理——名字修饰(name Mangling)
为什么C语言不支持函数重载,而C++就支持了呢?
我们都知道一个C/C++程序要运行起来,都需要经过 预处理-编译-汇编-链接。
而C语言在经过一系列的预处理、编译、汇编操作来到链接前时,这时候会有一个符号表,在符号表当中,C语言的函数名没有改变就变成的符号表中的名字,而C++经过编译器修饰会形成一个特定的符号名。符号名会根据形参列表(参数个数/参数类型/参数类型顺序)来修饰。而返回类型编译器无法判断,所有返回类型不影响函数重载。
经过名字修饰,就知道了C语言为什么不支持同名函数,因为同名函数C语言没办法区分 。而C++通过函数修饰归类来进行区分。
6 引用
6.1引用概念
引用不是新创建一个变量,而是给已经存在的变量取一个别名,编译器不会为引用变量开辟空间,它跟它引用的变量共用一块内存空间
类型& 引用变量名(类名)= 引用实体
int main()
{
int a = 0;
int& ra = a;
printf("%p\n", &a);
printf("%p\n", &ra);
return 0;
}
引用变量的类型必须要和被引用的变量类型一致
6.2 引用特性
1.引用在定义时必须初始化
2.一个变量可以有多个引用
3.引用一旦引用一个实体不可再引用其他实体
int main()
{
int a = 0;
//int& ra; 不初始化就会报错
int& ra = a;
int& rra = a;
printf("%p\n", &a);
printf("%p\n", &ra);
printf("%p\n", &rra);
return 0;
}
6.3常引用
int main()
{
const int a = 0;
//int& ra=a;该条语句会报错
const int& ra = a;
//int& b=10;类型不同,会报错
const int& b = 10;
double d = 1.2;
//int& rd=d;类型不同,会报错
const int& rd = d;
return 0;
}
6.4使用场景
1.做参数
//引用做参数
void Swap(int& left, int& right)
{
int tmp = 0;
tmp = left;
left = right;
right = tmp;
}
2.做返回值
//引用做返回值
int& Count()
{
static int n = 0;
n++;
return n;
}
引用做返回值的必要条件:
当函数返回时,如果返回对象出了函数作用域还在,那么可以使用引用返回,如果返回对象不存在了,那么只能使用传值返回。
6.4.1传值、传引用效率分析
传值时在函数调用中,将实参的临时拷贝传给函数,空间消耗大,而传引用则是给对象取了一个别名,跟要传的实参共享一块内存空间,没有多余的空间浪费。
因此使用传值是效率非常低下的,特别是传参或者返回值类型非常大时。
6.5 引用和指针的区别
在语法层面,引用则是给被引用的变量取一个别名,它跟被它引用的对象共用同一块内存空间。
在底层实现面,引用在底层实现实际是有空间的,因为引用是用指针来实现的。
指针与引用的不同点:
1.引用定义时必须初始化,指针没有要求
2.引用在初始化引用一个实体后不可再引用其他实体,而指针可以在任何时候指向一个任何同类型的变量
3.引用是一个别名,而指针存储变量地址
4.没有NULL引用,但有NULL指针
5.引用自加1则引用的实体加1,而指针加1代表指针向后偏移一个指针类型的大小
6.在sizeof中不同,引用为引用实体的类型大小,而指针始终是地址空间所占字节个数
7.没有多级引用,只有多级指针
8.访问实体方式不同,指针需要显示解引用,而引用由编译器自己解决
9.相较于指针而言,引用更安全。
7 内联函数
7.1 概念
以inline修饰的函数被称为内联函数,编译时C++编译器会在调用内联函数处展开,没有函数建立栈帧的开销,内联函数提升函数的运行效率。
7.2 特性
1.内联函数是一种以空间换时间的做法,在编译时编译器如果将函数当成内联函数,用函数体替换函数调用,可能会使目标文件变大,但也提升了效率。
2.内联函数不支持函数声明和定义分离,因为在编译阶段就展开了函数,没有函数地址,在链接的时候就会出错。
3.inline对编译器来说只是一个建议,inline函数如果过大或者有递归操作就不会展开。一般建议:将函数规模小、不是递归定义、频繁调用的函数采用inline修饰。