C++命名空间、函数重载、缺省参数、内联函数、引用
一 .C++入门
1.C++关键字
2.命名空间
3.C++输入&输出
4.缺省参数
5.函数重载
6.引用
7.内联函数
8.auto关键字
9.基于范围的for循环
10.指针空值nullptr&nullptr_t
二. 正文
1.C++关键字(C++98)
C++98中的关键字总共用63个,如下表:
在这这做简单介绍,感兴趣的朋友可以参考相关资料作进一步了解。
2.命名空间
由于在编写程序的过程中,很容易出现变量、函数和类重名的现象,这些变量、函数和类都在全局作用域中,因此会导致很多冲突。使用命名空间的目的是:对标识符的名称进行本地化,以避免命名冲突或者名字污染,命名空间的关键字就是:namespace
2.1命名空间的定义
定义命名空间,需要用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中的即为命名空间的内容,命名空间的定义方法有三种,下面一一列出:
a.普通的命名空间:
namespace N1
{
//命名空间中的内容,既可以定义变量,也可以定义函数 int a;
int Add(int left, int right)
{
return left + right;
}
}
b.命名空间可以嵌套
namespace N2
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N3
{
int c;
int d;
int Sub(int left, int right)
{
return left - right; }
}
}
c.同一个工程中允许出现多个命名相同的命名空间,编译器最后会合成同一个命名空间中
namespace N1
{
int Mul(int left, int right)
{
return left * right; }
}
2.2命名空间的使用
命名空间中的成员的三种使用方法:
注意:"::"称为作用域限定符
1.加命名空间名称及作用域限定符
int main()
{
printf("%d ",N1:a);
return ;
}
2.使用using将命名空间中的成员引入
using N1::b;
int main()
{
printf("%d ",b);
return ;
}
3.使用using namespace 命名空间名称引入
using namespace N1
int main()
{
printf("%d ",b);
printf("%d ",a);
return ;
}
注意:第一种方法与第二种方法只能将作用域限定符后的变量从它所在的命名空间引入,而第三种方法将命名空间中的所有的成员都引入了。
3.C++输入&输出
先用一段程序来直观的说说C++中的输入输出的使用方法:
#include<iostream>
using namespace std;
int main()
{
int a=;
cin>>a>>endl;
cout<<"hello World!"<<endl;
return ;
}
说明:
1.使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含<iostream>头文件,以及std标准命名空间。
2.使用C++输入输出更方便,不需要增加数据格式控制,比如:整形--%d,字符--%c
#include<iostream>
using namespace std;
int main()
{
int a;
char c;
double d;
cin>>a;
cin>>c>>d;
cout<<a<<endl;
cout<<d<<" "<<c<<endl; //endl在C++中的意思是换行
return ;
}
4.缺省参数
4.1缺省参数的概念
缺省参数是申明或者定义函数的时候为函数的参数指定默认值的操作。在调用该函数时,如果没有指定参数就使用该默认值,否则使用指定的实参。
#include<iostream> void TestFun(int a=)
{
cout<<a<<endl;
} int main()
{
TestFun(); //没有传参时,使用参数的默认值
TestFun(); //指定了参数时,使用指定的实参的值
return ;
}
4.2缺省参数的分类
a.全缺省参数:函数的参数列表中的形参的值都指定了默认值
void TestFun(int a=, double b=0.0, char c=' ')
{
cout<<a<<endl;
cout<<b<<endl;
cout<<c<<endl;
}
b.半缺省参数:函数的参数列表中的形参的值只有一部分指定了默认值,而且默认值只能从右往左依次给出
void TestFun(int a, int b=, double c=0.0)
{
cout<<a<<endl;
cout<<b<<endl;
cout<<c<<endl;
}
注意:
1.半缺省参数必须从右往左依次给出
2.缺省参数不能在声明和定义中同时给出(建议在申明中给出)
3.缺省参数必须是常量或者是全局变量
4.C语言不支持
//a.h
void TestFun(int a=);
//a.c
void TestFun(int a=)
{}
//如果申明和定义中同时出现,恰巧两个位置的默认值如上边代码中的一样
//则编译器就无法确定到底该用哪个缺省值。
5.函数重载
5.1函数重载的概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数类型、参数个数、参数次序)必须不相同,常用来处理功能相似但类型不同的问题。
int Add(int left, int right)
{
return left + right;
}
int Add(double left, double right)
{
return left + right;
}
long Add(long left, long right)
{
return left + right;
} int main()
{
Add(,);
Add(12.12,13.13);
Add(10L,20L);
reutrn ;
}
注意:函数的返回值类型对于函数是否重载没有影响
5.2名字修饰
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
名字修饰是一种在编译阶段,将函数、变量的名称重新改编的机制,简单来说,就是编译器为了区分各个函数,将各个函数通过某种算法,重新修饰为一个全局唯一的名称。
C语言中的名字修饰规则非常简单,仅仅只是在函数或者变量的名字前面加了下划线。读者朋友可以通过以下代码查看编译后的函数名(即通过只申明不定义,编译时编译器报错时会显示未找到<某某函数>,某某函数就是编译后的新名字。
int Add(int left, int right); int main()
{
int a=;
int b=;
Add(a,b);
return ;
}
上代码运行时报错:error LNK2019:无法解析的外部符号_Add,该函数在_main函数中被引用。
上述Add函数只给了申明,没有给定义,因此在链接时就会报错,提示:在main函数中引用的Add函数找不到函数体。报错结果中可以看到,C语言知识简单的在函数明前添加下划线。因此当工程中存在相同的函数名的时候,就会产生冲突。读者有兴趣可以自己验证。
由于C++要支持函数重载,命名空间等,使得其的修饰规则比较复杂,不同的编译器的实现方式可能都有差异,同样,用上述代码在VS编译环境下运行,可以看到VS编译器对C++的修饰方式:
error LINK2019:无法解析的外部符号"double cdecl Add(int,int)"(?Add@YAHHH@Z)
如果定义的函数的参数类型是double类型的,则可以看到下面的报错信息:
error LINK2019:无法解析的外部符号"double cdecl Add(int,int)"(?Add@YANNN@Z)
通过上述的报错信息可以看出,编译器实际上在底层使用的不是Add名字,而是被重新修饰过的一个比较复杂的名字,被修饰后的名字中包含了:函数的名字以及参数类型。这就是为什么函数重载中几个同名的函数要求它的参数列表必须不同的原因。只要参数列表不同,编译器在编译时通过对函数名字进行重新修饰,将参数类型包含在最终的名字中,就可以保证名字在底层的全局唯一性。这就不会导致Link时的多重定义。
5.3 extern "C"
有时候在C++工程中可能需要将某些函数按照C的分格来编译,在函数前面加extern "C",意思是告诉编译器将该函数按照C语言的分格进行编译。
extern "C" int Add(int left, int right)
{
return left + right;
} int main()
{
Add(,);
return ;
}
6.引用
6.1引用概念
引用不是定义一个新变量,而是给已存在的变量取了一个别名,编译器不会为引用变量开辟新的内存空间,它和它引用的变量公用同一块内存空间。
类型&引用变量(对象名)=引用实体
void TestRef()
{
int a=;
int & ra=a;//定义引用变量
printf("%d ",a);
printf("%d ",ra);
}
注意:引用类型必须和引用实体是同种类型的。
6.2引用特性
1.引用在定义时必须初始化
2.一个变量可以有多个引用
3.引用一旦引用一个实体,再不能引用其他的实体
void TestFun()
{
int a=;
//int& ra; 此语句编译时会出错
int& ra=a;
int& rra=a;
printf("%p %p %p",&a,&ra,&rra);
}
6.3常引用
void TestFun()
{
int a=;
//int& ra=a;编译时出错,因为a为常量
const int & ra=a;
//int& rb= 10;编译时出错,因为10为常量
const int&rb =;
double d=12.34;
int& rd=d;//编译时出错,类型不同
const int& rd=d;
}
6.4使用场景
1.做参数
void reverse(int& a, int&b)
{
int tmp=a;
a=b;
b=tmp;
}
//交换两个实参的值
2.做返回值
int& TestFun(int left, int right)
{
int tmp=left;
left=right;
right=tmp;
}
注意:如果函数返回时,离开函数作用域后,其栈上空间已经返还给操作系统,因此不能用栈上的空间作为引用类型返回。如果用引用类型返回,返回值的使用周期必须不受函数的限制(即生命周期比函数长)。
6.5传值、传引用、传地址效率比较
以值作为参数或者函数的返回值类型,在传参和返回期间,函数不会直接传递实参或者返回值类型,而是传递实参或者返回值的一份临时拷贝,因此用值作为参数或返回值传递效率非常低,尤其是当参数或者返回值的类型非常大的时候。
传地址时不存在拷贝,开辟临时变量的时间开销,所以效率是比较高的。但是传地址的缺点是函数的副作用会改变实参的值,数据不安全。
引用通过代码测试效率和传地址差不多。(有兴趣的读者朋友可以用clock()函数记时,测量传地址和引用的时间,进而判断它们的效率)
6.6引用和指针的区别
在语法概念上引用就是一个别名,没有独立的空间,和其引用实体公用同一块地址空间。但在底层实现上是有空间的,因为引用是按照指针的方式来实现的,读者可以将引用和传地址的函数代码转到汇编了解其汇编代码的实现方式,其实是一样的。
引用和指针的区别:
1.引用在定义是必须初始化(实例化),但指针不用
2.引用被一个实体初始化后不能引用其他的实体,但指针却可以(int& ra=a;<===>int * const p=&a;)
3.没用NULL引用,但有NULL指针
4.在sizeof中的含义不同,引用结果为引用类型大小,但指针的始终是地址空间所占的字节个数(32位平台下占4个字节)
5.引用加加是引用实体加一,但指针加加会让指针跳过一个类型的大小
6.有多级指针,但没有多级引用
7.访问实体不同,引用编译器自己处理,指针需要显示解引用
8.引用比指针更安全。(对空指针解引用会出错)
7.内联函数
7.1概念
以inline修饰的函数叫做内联函数,编译时C++会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序的运行效率。
7.2特性
1.inline是一种以空间换时间的做法,省去调用函数的额外开销。所以代码很长,或者有循环/递归的函数不适宜用作内联函数。
2.inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会自动忽略掉内联。
3.宏的优缺点:优点:增强代码的复用性,提高性能。缺点:不方便调试(预处理期间替换)、没有类型的安全检测、代码的可读性差、可维护性差、容易误用。
4.C++中替代宏的技术:
常量定义换用const、函数定义换用内联函数
8.auto关键字
8.1auto简介:
早期的C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的是没有人去使用。C++11中给了全新的含义:auto不再是存储类型的指示符,而是一个全新的类型指示符来指示编译器,auto申明的变量必须右编译器时期推演而得。
int TestFun()
{
return ;
} int main()
{
int a=;
auto b=a;
auto c='a';
auto d=TestFun();
cout<<typeid(b).name()<<endl;//typeid().name()的作用是打印比变量的类型
cout<<typeid(c).name()<<endl;
cout<<typeid(d).name()<<endl;
//auto e;无法通过哦编译,使用auto定义变量必须对其进行初始化,这样编译器才能
//推演出变量的类型;
return ;
}
注意:使用auto关键字定义变量时必须对其初始化,在编译阶段编译器需要根据初始化的表达式来推导auto的实体类型。因此auto并不是一种类型声明,而是类型声明时的占位符,编译器在编译时会将auto替换为变量的实际的类型。
8.2auto的使用细则
1.auto与指针和引用结合起来使用:用auto声明指针类型时,用auto与auto*没有任何区别,但用auto声明引用时必须加上&
2.在同一行声明多个变量时,这些变量必须是相同类型的,否则编译器会报错,因为实际上编译器只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
8.3auto不能推导的场景
1.auto不能作为函数的参数
2.auto不能用来直接声明数组
3.为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法
4.auto常用与范围for循环中
5.auto不能定义类的非静态成员变量
6.实例化模版时不能使用auto作为模板参数
9.基于范围的for循环
void TestFun()
{
int array[]={,,,,};
for(auto& e:array)
e*=;
for(auto e:array)
cout<<e<<endl;
}
for循环后的括号由冒号":"分为两部分,前面的部分是范围内用于迭代的变量,后边的部分是迭代的范围。与普通循环类似,可以用continue跳出本次循环,break跳出循环。
范围for的使用条件:for循环的迭代范围必须是确定的,对于数组而言,就是数组中的第一个元素和最后一个元素,对于类而言,应该是提供begin和end的方法,begin和end就是for循环的迭代的范围。迭代的对象要实现++或者==的操作。
10.指针空值nullptr&nullptr_t
NULL其实是一个宏,通常被定义为0,因此在C++中有了函数重载之后,对于NULL而言就有了歧义性,为了解决这个问题,C++11中给出了全新的nullptr表示空指针,代表一个空值指针。nullptr是有类型的,其类型是nullptr_t,仅仅可以被隐式转化为指针类型。
注意:在使用nullptr时不需要包含头文件,因为nullptr是C++11作为新的关键字引入的。在C++11中sizeof(nullptr)与sizeof(void *)所占的字节数相同,为了提高代码的健壮性,在后续的表示空指针的地方建议使用nullptr。
C++命名空间、函数重载、缺省参数、内联函数、引用的更多相关文章
- 不可或缺 Windows Native (16) - C++: 函数重载, 缺省参数, 内联函数, 函数模板
[源码下载] 不可或缺 Windows Native (16) - C++: 函数重载, 缺省参数, 内联函数, 函数模板 作者:webabcd 介绍不可或缺 Windows Native 之 C++ ...
- C++初阶(命名空间+缺省参数+const总结+引用总结+内联函数+auto关键字)
命名空间 概述 在C/C++中,变量.函数和后面要学到的类都是大量存在的,这些变量.函数和类的名称将都存在于全局作用域中,可能会导致很多冲突.使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲 ...
- 【C++初学者自学笔记三】哑元函数、缺省参数、内联函数(模块二,PS:需要用到重载函数)
一,哑元函数:一个函数的参数只有类型没有名字的则这个参数称之为哑元.类似于void fun(int); 功能:1保持向前的兼容性,比方说我们需要做成一个成品,然后成品是会不断的更新第一代第二代,当我们 ...
- 3.C++内联函数,默认参数,占位参数
本章主要内容: 1)内联函数(替代宏代码段) 2)默认参数 3)占位参数 1.C++的内联函数分析 1.1讲解内联函数之前,首先回忆下之前讲的define宏定义: 之前讲过宏定义会经过预处理器进行文本 ...
- C++中对C的扩展学习新增语法——内联函数以及函数参数
内联函数以及函数参数 内联函数 使用 inline 关键字必须和函数体放在一起. 内联函数具有内部链接属性. 内联函数会被编译器在编译阶段替换到函数调用的地方. 可以把内联函数定义写到头文件中,多个c ...
- const引用和函数占位参数遇上默认参数以及内联函数
1.const引用: 但是加上const之后是可以的,const int &a=100;就不会报错了. 2.函数占位参数: 如果给最后的占位参数加上默认值: 3.内联函数 内联只是对编译器发起 ...
- C++内联函数与宏定义
用内联取代宏: 1.内联可调试: 2.可进行类型安全检查或自动类型转换: 3.可访问成员变量. 另外,定义在类声明中的成员函数自动转化为内联函数. 文章(一) 内联函数与宏定义 在C中,常用预处理语句 ...
- [ 随手记 1 ] C/C++宏,普通函数,内联函数
函数定义 C 语言中的函数定义的一般形式如下: return_type function_name( parameter list ) { body of the function } 在 C 语言中 ...
- C/C++之宏、内联函数和普通函数的区别
内联函数的执行过程与带参数宏定义很相似,但参数的处理不同.带参数的宏定义并不对参数进行运算,而是直接替换:内联函数首先是函数,这就意味着函数的很多性质都适用于内联函数,即内联函数先把参数表达式进行运算 ...
- __inline定义的内联函数和宏的区别
转自:http://blog.csdn.net/lw370481/article/details/7311668 函数与宏 #define TABLE_COMP(x) ((x)>0?(x):0) ...
随机推荐
- 1、GDB程序调试
GDB是GNU开源组织发布的一个强大的Linux下的程序调试工具.一般来说GDB主要完成下面四个部分的功能. 1)启动你的程序,可以按照你的自定义的要求运行程序. 2)可让被调试程序在你所指定的调试的 ...
- 关于document.write(来自网络)
对象属性: document.title //设置文档标题等价于HTML的<title>标签document.bgColor / ...
- 2018.09.23 atcoder Boxes and Candies(贪心)
传送门 一道挺有意思的贪心. 从1到n依次满足条件. 注意要特判第一个数已经大于x的情况. 但是如何贪心吃呢? 如果靠左的数没有越界,我们吃靠右的数. 原因是下一次靠右的数就会成为靠左的数,相当于多贡 ...
- time & datetime 模块
在平常的代码中,我们常常需要与时间打交道.在Python中,与时间处理有关的模块就包括:time,datetime,calendar(很少用,不讲),下面分别来介绍. 在开始之前,首先要说明几点: 一 ...
- linux将程序扔到后台并获取程序的进程号
我们经常需要写一些执行时间较长的程序,但是如果在程序执行过程中超时了,有许多原因,可能是程序已经挂起了,这时就需要杀死这样的进程,则可以通过如下的命令执行: java -jar TestProcess ...
- C/C++中如何获取数组的长度?
C/C++中如何获取数组的长度? 收藏 C.C++中没有提供 直接获取数组长度的函数,对于存放字符串的字符数组提供了一个strlen函数获取长度,那么对于其他类型的数组如何获取他们的长度呢?其中一种方 ...
- Flord算法传递闭包
POJ3660 对于flord算法得学习,这篇博客写的非常好http://blog.csdn.net/ljhandlwt/article/details/52096932 这个题问你给你n头牛得前后关 ...
- http://www.cnblogs.com/xalion/p/5111279.html
https://quality.embarcadero.com/secure/Dashboard.jspa 注册成 delphi qc
- Linux 安装PAE内核
客户软件是部署在32位的CentOS5服务器当中,CentOS5目前只能识别4G内存,需要安装PAE内核,让系统支持PAE物理地址扩展. 1.安装PAE内核 yum -y install kern ...
- VS2010+Oracle11+Entity Framework4.1环境搭建及常见问题
在微软的实体数据模型中存在四种查询方式: SQL字符串:Linq:Linq to SQL:Linq to Entity(ESQL) 对于Linq SQL目前微软虽然仍在支持,但微软已经声明不再推荐. ...