目录
在上篇中,为大家介绍了C++的历史,以及命名空间、缺省参数、函数重载的概念。接下来,将继续为大家介绍C++入门的基础知识点,然后就可以正式开始C++的学习之路了!
一、C++入门知识点:引用
我们先来看看引用是什么样子的,为什么很多人把引用和指针分不清。(别名的符号& 和 取地址符&是一个符号,所以不要理解了哦)
int a = 10;
int& b = a;
1、C++引用:概念
我们首先要搞明白,引用并不是定义了一个新的变量,而是现在存在一个变量,给这个已存在的变量起了一个别名。编译器不会为了这个别名重新开辟空间去存放数据,因为本质上这个别名和它所引用的变量,共用的是同一块内存空间。请看如下代码运行的结果:
int a = 10;
int& b = a;
printf("a的地址:%p\n", &a);
printf("b的地址:%p\n", &b);
此时的变量a和他的别名b的地址是相同的,这也就证实了我们之前所说的:别名并不重新开辟空间,而是对原有空间的另一种称呼。那么我们修改别名,原变量会跟着一起修改吗?
肯定会咯!因为他们是同一块内存地址嘛,所以你想想看,针对这块内存地址的操作,你无论用什么名字去访问这块内存空间,里面的内容都应该是一样的。
b = 20;
cout << a << endl;
cout << b << endl;
注意:引用类型必须和被引用的对象是同一类型。(当然,别名是不开辟新空间的,原空间是什么类型数据,别名也得是什么类型)
2、C++引用:特性
- 引用在定义时,就必须初始化
引用在定义时就必须初始化的原因其实很简单,因为起别名是针对已经存在变量的一种操作,引用在一开始定义的时候,就必须绑定要操作的变量对象。
- 一个变量,可以有多个引用(一个实体可以存在多个引用)
就如同你的名字一样,你可以有大名,有小名,有外号,这些代号都指向你一个人,别人不会认错。
- 引用一旦定义,就不能修改(一个引用对应一个实体)
引用一旦被定义(定义的时候会进行初始化,也可以说引用一旦被初始化),就无法修改。因为一个别名是不可以指向两个不同的内存空间的,这就相当于你的名字,同时指代两个人,这是不符合我们常理的。并且如果一个别名,被赋值多次的话,那么在使用的时候也可能会造成歧义。
3、C++引用:常饮用
引用类型必须和被引用的对象是同一类型。所以我们在使用引用的时候,要时刻注意我们引用类型以及操作权限是否匹配。假如你使用一个普通的引用类型去引用一个const修饰的类型,那么你的引用就失败了。(因为const修饰的变量具有常属性,你用一个可以修改的类型,去引用不可修改的类型,就会引发权限放大的问题)
int main()
{
const int a = 10;
//int& ra = a; //该语句编译时会出错,a为常量
const int& ra = a;//正确
//int& b = 10; //该语句编译时会出错,10为常量
const int& b = 10;//正确
return 0;
}
权限放大的问题,我们在学习C\C++的时候会经常遇到。总的来说,就是我们严格限制的变量,在传递的过程中,可以对他进行一些额外的操作时,就会触发权限放大的问题。严格的讲,变量操作的权限只能平移和缩小(最好是平移)。
4、C++引用:引用的使用场景
a、引用做参数
在前文,我们已经知道了,引用其实本质上也还是针对的同一块地址进行操作。所以传参时传递引用变量,即相当于传递原变量本身过去(针对引用变量的操作,也会在原变量上起作用)。在底层实现里,引用也是类似指针实现的,所以相比于传值传参,引用传参是更节省空间,效率更高的。
void swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
//此时其实相当于a空间里的数据和b空间进行了交换
b、引用做返回值
既然引用能传参,那么引用理应也可以当作返回值来传递。但是在引用返回的时候要特别注意,我们返回的数据不能是函数内部创建的局部变量。因为在函数内部定义的局部变量,在该函数结束调用之后,函数空间会销毁。此时继续访问该变量,会发生内存泄漏。因此,当我们要使用引用返回时,必须要注意变量的生效区间。一定得是static修饰的静态变量,或者是动态开辟在堆上,或者是全局变量的情况,才可以使用引用做返回值。(因为以上情况,函数调用结束后,数据不会被销毁,可以继续访问)
int& Add(int a, int b)
{
static int c = a + b;
return c;
}
//总结一句话:
//如果函数返回值时,除了作用域,而返回对象的数据还未归还给系统时,则可以使用引用返回
//如果已经还给系统了,那么必须使用传值返回(传值返回的本质是:对当前数据进行了拷贝,返回的是这个拷贝,所以函数结束也不会影响)
5、C++引用:引用和指针的区别
在语法概念上,引用就是已存在变量的一个别名,他没有自己的独立空间,和引用的对象公用的是一块空间。而指针,则是专门开辟了一个独立的内存,来存放这要操作的内存空间的地址。
int a = 10;
int& b = a;
printf("a的地址:%p\n", &a);
cout << sizeof(a) << endl;
printf("b的地址:%p\n", &b);
cout << sizeof(b) << endl;
int c = 10;
int* d = &c;
printf("c的地址:%p\n", &c);
cout << sizeof(c) << endl;
printf("d的地址:%p\n", &d);
cout << sizeof(d) << endl;
但是我们可以看到,为什么这里别名b实际上还是有大小的呢?其实这就和引用的底层实现有关系了。
实际上,引用的底层实现其实也是类似指针存地址的方式来处理的。
引用和指针的区别
1、引用在定义时必须初始化,指针没有要求。
2、引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
3、没有NULL引用,但有NULL指针。
4、在sizeof中的含义不同:引用的结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)。
5、引用进行自增操作就相当于实体增加1,而指针进行自增操作是指针向后偏移一个类型的大小。
6、有多级指针,但是没有多级引用。
7、访问实体的方式不同,指针需要显示解引用,而引用是编译器自己处理。
8、引用比指针使用起来相对更安全。
二、C++入门知识点:内联函数
我们在运行一个程序的过程中,势必会进行调用函数这个操作。而一旦调用函数,就会在内存中开辟栈帧(call),多次调用之后,则可能会造成过大的内存消耗。C++为了避免这种情况,允许将函数定义在内部(inline),但是使用inline关键字,并不是百分百就可以把函数定义为内联,这也与编译器的处理有关。一般来说,转成汇编10行以内的函数都可以被定义为内联函数。
1、C++内联函数:概念
inline函数,是一种用空间来换时间的做法。如果我们频繁地调用函数,会造成过大的内存消耗;将函数定义为内联函数,则可以避免调用这步操作,函数会在inline位置直接展开。汇编如下:
//设定sum函数为内联函数
inline int sum(int a, int b)
//sum函数为普通函数
int sum(int a, int b)
{
return a + b;
}
int main()
{
int c = sum(10, 20);
printf("%d\n", c);
return 0;
}
![](https://img-blog.csdnimg.cn/757fbddf3cda4df8b1ee08ac8451ce7c.png)
![](https://img-blog.csdnimg.cn/f619f730d77a4dfc97d709017689cb16.png)
2、C++内联函数:特性
前文我们已经说过了,内联函数是一种用空间来换时间的做法,但是这种做法也有弊端。
一是内联函数不适用于递归函数和一些较长的函数,因为这样会造成函数体的臃肿。内联只适用于一些短小的,调用平凡的函数。
二是inline关键词,对于编译器来说只是一个建议,并不能强制编译器将函数定义为内联函数。函数最终如何使用,取决于编译器的处理。
三是内联函数的展开特性,决定了内联函数最好是定义和生命不要分离编译。在程序链接阶段,分离的情况会造成链接失败。因为当内联函数在调用处被展开之后,就找不到函数地址了,链接就会失败。
三、C++入门知识点:auto关键字
要说C++对于C来说,最大的进步是什么?必不可少的就是对于类型的操作,譬如printf函数在打印时,必须强调打印数据的类型;而C++的cout,则比printf要机灵许多,我们只需要将变量放进去,他就会自动识别。而我们今天学习的auto关键字,也具有这样的逻辑。
1、C++auto关键字:概念
在早期的C/C++中auto的含义是:使用auto修饰的变量是具有自动存储器的局部变量,但遗憾的是一直没有人去使用它。
在C++11中,标准委员会赋予了auto全新的含义:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
int main()
{
int a = 10;
auto b = a;
char c = '0';
auto d = c;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
return 0;
}
根据打印的结果来说,我们在没有给定b和d的情况下,编译器自动为我们识别出来,变量b和变量a是同一类型,变量d和变量c是同一类型。这一切都取决于编译器的处理。因此,在使用auto关键字的时候,我们要注意不要产生歧义。譬如:auto在同一行定义多个变量时,需要统一变量类型。
int main()
{
int a = 10;
auto b = &a; //自动推导出b的类型为int*
auto* c = &a; //自动推导出c的类型为int*
auto& d = a; //自动推导出d的类型为int
//在使用auto声明引用变量时,必须添加&
cout << typeid(b).name() << endl;//打印结果为int*
cout << typeid(c).name() << endl;//打印结果为int*
cout << typeid(d).name() << endl;//打印结果为int
auto m = 10, n = 20;//正确
auto x = 1, y = 1.1;//错误,前后数据类型不匹配。
return 0;
}
2、C++auto关键字:注意事项
- auto类型的变量不可以作为函数的形参,因为没有实际类型可以给auto变量来推导。
- auto不能来声明数组,因为auto其实只是一个占位符,并不是实际的声明。编译器会在编译的过程中将auto替换为对应的数据类型。