C++入门(命名空间,缺省参数,函数重载,引用,内联函数,auto,范围fo

目录一.C++关键字二.命名空间三.缺省参数四.函数重载五.extern”C”六.引用七.内联函数八.auto关键字(C++11)九.范围for

一.C++关键字

C++总共有63个关键字,在入门阶段我们只是大致了解一下就可,在后续博客中会逐渐讲解

二.命名空间

相信学过C++的同学,一定都写过下面这个简单的程序

#include<iostream>using namespace std;int main(){cout<<"hello world"<<endl;return 0;}

我们先来看第二行代码,using namespace std , 这行代码是什么意思呢 ?

这里我们就要来引入命名空间的概念,命名空间是用来解决C语言命名冲突问题的,在我们的C语言阶段,如果我们写了下面的程序,是不能通过编译的,原因是因为scanf函数包含在 <stdio.h>这个库里,是一个全局的函数,而我们用scanf去命名全局变量,会报重定义的错误,这就导致了命名冲突,C语言是无法解决这个问题的,因此C++为了解决这个问题,引入了命名空间,来做名字的隔离

#include<stdio.h>int scanf = 10;int main(){printf("%x\n",scanf);}

命名空间 :在C/C++中,变量、函数和我们后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

上面的代码改正后如下

#include<stdio.h>namespace N{int scanf = 10;}int main(){printf("%x\n",scanf);  // 以十六进制打印出scanf函数的地址printf("%x\n",N::scanf); // 以十六进制打印出 N命名空间域里的 scanf变量}

其中 N::scanf 中的 :: 为域作用限定符,表明要打印的 scanf 是 N命名空间域里的

了解了命名空间后,回到我们最开始的问题 using namespace std 是什么意思呢?

C++库为了防止命名冲突,将自己库里的东西都定义在一个名为 std 的命名空间里,要使用标准库里的东西,有以下三种方式:

(1).指定命名空间

#include<iostream>int main(){std::cout<<"hello world"<<std::endl;}

(2).把std整个展开,即 using namespace std,虽然使用起来比较方便,但如果我们自己定义的东西跟库里冲突了,就没办法解决了,因此在规范的工程项目中不推荐此种方式

#include<iostream>using namespace std;int main(){cout<<"hello world"<<endl;}

(3).对部分常用的库里面的东西展开

#include<iostream>using std::cout;using std::endl;int main(){cout<<"hello world"<<endl;}

命名空间的几点注意事项 :

(1). 命名空间里既可以定义变量,也可以定义函数(2).命名空间可以嵌套定义

namespace A{int a;  // 定义变量int Add(int left,int right) // 定义函数{return left + right;}namespace B  // 嵌套定义{int b;int Sub(int left,int right){return left - right;}}}

(3).在同一个工程里可以存在多个相同名称的命名空间,在编译时最终会合成到一个命名空间里,因此注意不要定义同名的变量或函数,否则会报重定义的错误

三.缺省参数

(1).缺省参数的概念

缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参

void Testfunc(int a = 0) // 缺省参数{cout<<a<<endl;}int main(){Testfunc(10); // 使用给定的实参Testfunc();   // 使用默认值}

(2). 缺省参数的分类

全缺省参数 : 函数参数都指定了默认值

void TestFunc(int a = 10,int b = 20,int c = 30){cout<<a<<endl;cout<<b<<endl;cout<<c<<endl;}

半缺省参数 : 函数参数部分指定了默认值

void TestFunc(int a,int b = 20,int c = 30){cout<<a<<endl;cout<<b<<endl;cout<<c<<endl;}

注意 :

(1).半缺省参数必须从右往左依次给出,不能间隔给出

void TestFunc(int a = 10,int b,int c = 20) // 错误写法{cout<<a<<endl;cout<<b<<endl;cout<<c<<endl;}

(2).缺省参数不能在声明和定义中同时出现

a.hvoid TestFunc(int a = 10);a.cvoid TetsFunc(int a){cout<<a<<endl;}

(3).缺省参数的值必须为常量或全局变量

四.函数重载

(1).函数重载的概念

C语言并不支持同名函数的存在,若定义了同名函数会报重定义的错误,C++在C语言的基础上引入了函数重载的概念,即函数的名称可以相同,但函数的参数列表不能相同(参数的类型,参数的个数,参数的顺序),函数的返回值不能作为重载的标志,原因会在后面解释

// 函数重载int Add(int left,int right){return left + right;}double Add(double left,double right){return left + right;}

C++重载机制很好理解,但C++是怎么支持重载的呢?为什么C语言不支持重载呢?

在讲述这个问题之前,我们要先回顾一下我们之前学的编译链接过程

编译可分为以下三个阶段

1.预处理预处理阶段主要做的事情有以下几点(1).头文件的展开(2).进行宏替换(3).去掉注释(4).执行条件编译指令

经过预处理阶段后生成后缀名为.i的文件

2.编译编译阶段主要做的事情有以下几点(1).词法分析在词法分析过程中,我们的源代码程序会被输入到扫描器中,扫描器会将源代码的字符序列分割成不同的记号并分类,如关键字,标识符,字面量,同时扫描器将分好类的记号存储到对应的位置,为后面的操作做好铺垫(2).语法分析语法分析是通过建立一颗语法树来实现的,我们所写的语句是由多个表达式组成的,因此我们的语法树是一颗以表达式为结点的树,在语法分析的过程中,操作符的优先级和结合性也被确定下来了,如果在语法分析过程中,出现了语法错误,编译器就会报语法分析阶段的错误(3).语义分析语法分析仅仅对语法进行检测,但并不知道语义是否正确,这就需要语义分析器上场了,语义分析阶段主要做的是类型的匹配,转换,比如我们将一个浮点型表达式赋值给一个整型表达式,需要进行隐式类型转换,语义分析需要完成这个步骤,将一个浮点型赋值给一个指针,语义分析会发现类型不匹配,编译器会报错,经过语义分析阶段后,语法树的各个节点会被标记上类型,需要类型转换的,会插入相应的转换节点

经过编译阶段后,生成了后缀名为.s的汇编代码文件

3.汇编汇编阶段所做的事情比较简单,汇编阶段将编译产生的汇编代码文件转换成二进制机器指令

经过汇编阶段生成后缀名为.o的目标文件

生成的目标文件是按照ELF文件格式进行存储的,ELF文件由多个段组成,如.text(代码段) .data(数据段) .symtab(符号表)等,这里重点要说的是符号表,符号表是一个数组,数组的元素是结构体,结构体描述了文件中符号的各种信息(符号名,符号值,符号类型等)

而C++支持函数重载,C不支持函数重载的原因是它们生成符号名时机制不同

C语言在生成符号表时,符号名是变量或函数名C++在生成符号表时,符号名是函数名和形参列表的组合

如GCC编译器的修饰规则如下 :(1).所有的符号都以_Z开头(2).没有嵌套的名字后跟函数名,函数名前是函数名的字符串长度,后跟参数类型首字母(3).对于嵌套的名字(在命名空间或类里),后面紧跟’N’,然后是命名空间或类的名称,每个名字前是每个名字的字符串长度,后跟函数名,函数名前是函数名的字符串长度,后跟’E’,后跟参数类型首字母

由此我们就知道了C++为什么支持重载,而C语言不支持重载,因为C++生成目标文件以后,同名函数只要参数列表不同,符号名就不相同,而C语言生成目标文件以后,同名函数的符号名相同,就会引发命名冲突

五.extern”C”

C++为了与C兼容,在符号的管理上,C++有一个用来声明或定义C的符号的extern “C”关键字的用法

extern "C"{int func(int);int var;}

C++编译器会将大括号里面的代码当成C语言的代码来处理

六.引用

引用概念 : 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

使用: 类型& 引用变量名(对象名) = 引用实体

void TestRef(){ int a = 10; int& ra = a;//<====定义引用类型  printf("%p\n", &a); printf("%p\n", &ra); // 打印的地址一样}

注意 : 引用类型必须和引用实体是同种类型的

引用特性 :(1).引用必须初始化(2).引用一旦初始化,不能被更改(3).一个变量可以有多个引用

void TestRef(){ int a = 10; // int& ra; // 该条语句编译时会出错 int& ra = a; int& rra = a; printf("%p %p %p\n", &a, &ra, &rra); // 地址都一样 }

常引用 :

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;}
const int a = 10; //int& ra = a; // 该语句编译时会出错,a为常量

编译出错的原因 :原来a不能被修改,类型为 const int,但ra的类型为int,使权限提升了

double d = 12.34; //int& rd = d; // 该语句编译时会出错,类型不同 const int& rd = d;

编译出错的原因 :在进行类型转换时,会产生一个临时变量,rd是临时变量的别名,但因为临时变量具有常性,因此 int& rd = d;是错误的

引用做参数

void Swap(int& left,int& right){int tmp = left;left = right;right = tmp;}

引用做返回值

// 正确写法int& Count(){ static int n = 0; n++; // ... return n;}

下面代码的运行结果是什么?

// 错误示范int& Add(int a, int b){ int c = a + b; return c; }int main(){ int& ret = Add(1, 2); Add(3, 4); cout << "Add(1, 2) is :"<< ret <<endl; // Add(1,2) is : 7 return 0; }

错误在于返回了局部变量的引用,Add函数返回的是局部变量c的引用,c出了作用域以后,c的空间就被操作系统回收了

引用和指针的区别(1).引用必须初始化,指针可以不初始化(2).引用初始化一个实体之后,不能再引用另外一个实体,指针指向一个实体后,可以再指向另外一个实体(3).不存在空引用,存在空指针(4).在语法上,引用是给一个变量取别名,指针取的变量的地址(5).在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)(6).引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小(7).有多级指针,但是没有多级引用(8). 访问实体方式不同,指针需要显式解引用,引用编译器自己处理(9). 引用比指针使用起来相对更安全

七.内联函数

内联函数概念 : 以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。

C语言为了小函数避免栈帧的消耗,提供了宏函数的支持,那为什么C++还要引入内联函数呢?

(1).宏函数在预处理阶段会被替换掉,不能进入函数内部进行调试(2).宏函数不支持类型检查,语法复杂,容易出错

inline int Add(int x,int y){return x + y;}int main(){int ret = Add(1,2);cout<<ret<<endl;}

八.auto关键字(C++11)

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量。

C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

int main(){int a = 10;auto b = a;// 类型声明成auto,可以根据a的类型自动推导出b的类型}

(1). auto与指针和引用结合起来使用

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

int main(){ int x = 10; auto a = &x;  // 推导出 a 的类型为 int* auto* b = &x; // 推导出 b 的类型为 int* auto& c = x;  // 推导出 c 的类型为 int cout << typeid(a).name() << endl;  // int* cout << typeid(b).name() << endl;  // int* cout << typeid(c).name() << endl;  // int *a = 20; *b = 30;  c = 40;  return 0;}

(2). 在同一行定义多个变量当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

void TestAuto(){ auto a = 1, b = 2;  auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同}

(3). auto不能作为函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导void TestAuto(auto a){}

(4). auto不能直接用来声明数组

void TestAuto(){ int a[] = {1,2,3}; auto b[] = {4,5,6}; // 错误}

(5).为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

九.范围for

C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

void TestFor(){ int array[] = { 1, 2, 3, 4, 5 }; for(auto& e : array)  e *= 2;  for(auto e : array) cout << e << " ";// 2, 4, 6, 8, 10}

以上就是C++入门(命名空间,缺省参数,函数重载,引用,内联函数,auto,范围for)的详细内容,更多关于c++ 入门基础知识的资料请关注其它相关文章!

贪婪是最真实的贫穷,满足是最真实的财富

C++入门(命名空间,缺省参数,函数重载,引用,内联函数,auto,范围fo

相关文章:

你感兴趣的文章:

标签云: