引用
1.什么是引用
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);//打印的地址相同
}
2 .引用特性
- 引用必须初始化,不能是空
int& ra; // 该条语句编译时会出错
- 引用类型要与引用对象类型一致
- 一个变量可以有多个引用(即别名)
如:
int a = 10;
int& ra = a;
int& rra = a;//变量a可有多个别名
- 引用一旦引用一个实体,再不能引用其他实体
即引用初始化后不能再来引用其它变量,不同的变量不能起相同的别名
3.常引用
引用对象是定量,引用就不能为变量
const int a=10;
const int &b=a;
int a=10;
const int &b=a;//引用对象是变量,引用可以是定量
const int a=10;
int &b=a;//这里编译会出错
int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
int& rd = d; // 该语句编译时会出错,类型不同
4.使用场景
- 做参数`
void Swap(int& a, int& b) {
int temp = a;
a = b;
b = temp; }
- 做返回值
int& Count()
{
static int n = 0;
n++;
return n;}
int main()
{
int& ret = Count();
cout << "Count() is :"<< ret <<endl;
return 0; }
注意:
如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
5.传值、传引用效率比较
因为传值是系统要开辟临时空间会消耗一定的时间,而引用不需要另外开辟临时空间,当传递的值较大时,引用传值效率会很高
6.引用和指针的区别
引用在语法概念上就是一个别名,没有独立空间,和其引用实体共用同一块空间。但在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
引用和指针的不同
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
内联函数
1.概念
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
2.使用方法:
#include <iostream>
using namespace std;
inline int Add(int i,int j) {
int c=i+j;
return c;}
int main(){
int ret=0;
ret=Add(1,2);
return 0;
}
3.特性
- inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
- inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
- inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
4.与宏比较
宏的优缺点
优点:
1.增强代码的复用性。
2.提高性能。
缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。
C++有哪些技术替代宏 - 常量定义 换用const
- 函数定义 换用内联函数
auto关键字(C++11)
1.auto简介
C++11中,标准委员会赋予了auto全新的含义即:auto作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
int TestAuto(){
return 10; }
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
auto e;// 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
2.auto的使用注意:
2.1.使用auto定义变量时必须对其进行初始化。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
2.2. auto与指针和引用结合起来使用用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;
2.3. 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错。
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同}
3.auto不能推导的场景
3.1. auto不能作为函数的参数
3. 2. auto不能直接用来声明数组auto b[]={1,2,3,...};//这是错误的用法
基于范围的for循环(C++11)
1.范围for的语法
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,可以减少在范围上错误。
int a[]={1,2,3,4,5,6,7,8,9}
//C++基于范围的for用法
for(const int &e:a)
cout<<e;
cout<<endl;
//C用法
int n=sizeof(a)/sizeof(a[0]);
for(int i=0;i<n;i++)
printf("%d",a[i]);
printf("\n");
2.范围for的使用条件
for循环迭代的范围必须是确定的
指针空值nullptr(C++11)
2.C++98中的指针空值
未初始化的指针,我们一般会赋空,否则可能会出现不可预料的错误。
但在使用空值的指针时,都不可避免的会遇到一些麻烦。如:
以函数重载,调用时为例:
(NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量)
void f(int) {
cout<<"f(int)"<<endl; }
void f(int*) {
cout<<"f(int*)"<<endl; }
int main()
{
f(0);
f(NULL);//我们会认为NULL是给指针赋空的,但由于NULL一般被定义为0,只会调用第一个函数,不会调用第二个函数
f((int*)NULL);//在这里强转换了NULL,但与我们认为NULL是给指针赋空的却要强转换为指针含义会有重复
return 0;}
注意:
1. 在使用nullptr表示指针空值时,不需要包含头文件
2. 在C++11中,sizeof(nullptr) 与 sizeof((void)0)所占的字节数相同。
3. 为了提高代码的健壮性,在表示指针空值时建议最好使用nullptr。*