Table of Contents
通过<C++Primer> 整理出的结构,附带有页码
本文将介绍 C++ 里面的
基本类型(引用 指针 结构体)
类型转换(static_cast、dynamic_cast、const_cast、reinterpret_cast),
类型处理(typedef auto decltype)
0.概述
1.基本类型
1.1 引用
1.1.1 引用的原理
引用的实现原理其实通过下面4句话,1副图,1段代码就可以说明白了
int a=1;
int &b=a
1.引用变量b和被引用变量a并没有共用一块内存,b是另外开辟了一块内存的
2.引用变量b开辟的内存中存放的是a的地址
3.任何对变量b的操作,都将转换为对(*b)的操作,比如b=b+1实际上是(*b)=(*b)+1 而(*b)代表的就是a
4.基于上面3点我们可以总结出 引用变量b可以理解为被引用变量a的别名
上面的两句代码 对应的内存分布图就如下
再看一个实际的例子
#include<iostream>
using namespace std;
int main()
{
int a = 1;
int& b = a;
cout << "a:address->" << &a << endl;
cout << "b:address->" << &b << endl;
getchar();
return 0;
}
运行结果:
a:address->0031FD54
b:address->0031FD54
1.1.2引用的注意点
1.引用必须在声明引用时将其初始化,而不能先声明,再赋值。也不能在使用过程中途对其赋值企图更改被引用的值,那样是无效的
比如:
int rats = 101;
int & rodents = rats;
int bunnies = 50;
rodents = bunnies;
在上面一通操作以后rodent引用的还是rats
2.在用引用作为函数形参的时候,如果实参与引用参数不匹配,C++将生成临时参数。使用临时参数的数据改变不会影响传入的数据。
比如:
void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
long a=3,b=5;
swap(a,b);
这里的a,b与传入函数参数的a,b类型不匹配,因此编译器将创建两个临时int变量,将它们初始为3和5,然后交换临时变量的内容,而a和b保持不变。
1.1.3 引用的分类
(以下纯属个人理解 ,知识储备不够,如有错误,恳请指正)
在介绍引用的分类之前,先介绍一下两个概念 :左值和右值
先从C++98时代说起, 左值和右值区别如下,左值和右值最重要的特性就是:左值的持久状态的,右值是转瞬即逝的
在C++98时代我们只能对左值做引用 不能有右值引用,也就是上面所讲的那些引用.
但是在后来,我们有了一些新的需求
1. 我们想通过引用去改变临时变量的值 就好比实现下面的效果
std::string s1 = "Hello ";
std::string s2 = "world";
std::string& s_rref = s1 + s2; // the result of s1 + s2 is an rvalue
s_rref += ", my friend"; // I can change the temporary string!
std::cout << s_rref << '\n'; // prints "Hello world, my friend"
所以引入了右值引用,用来绑定到一个转瞬即逝的右值上.
2,我们还有另外一个更大的想法,就是想用这种引入的右值引用来实现对象移动
我们知道在对象拷贝的时候 我们要先开辟一块内存然后把原来内存上的数据复制过去,然后释放掉原来的内存
这样效率有点低 ,我们用引用来实现对象的移动的想法是,用引用声明一个变量作为原来的对象的别名,然后销毁原来的对象(或者对象自己销毁自己),然后就可以通过引用来操作原来对象内存上的数据了,实现对象的移动.
在C++98时代 这种想法的实现是困难的,因为你用引用去绑定到一个对象以后,原来那个对象怎么自己销毁自己?
但是在C++11引入右值以后 我们就有新办法了,
就是把原来对象强行转换成右值,我们知道右值是转瞬即逝的,然后我们再定义一个右值引用绑定到该对象强行转换后的右值上,
就大功告成了.
在C++11中,可以通过一个std::move()函数将左值对象转成右值对象 方便右值引用对其绑定,从而实现对象移动
像这种和右值引用相关的表达式,通常是将一个对象变成将要销毁的对象的右值 叫将亡值
而在C++11中的和C++98重复的那部分右值叫纯右值
左值引用就是对一个左值进行引用的类型。右值引用就是对一个右值进行引用的类型,事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。
右值引用和左值引用都是属于引用类型。无论是声明一个左值引用还是右值引用,都必须立即进行初始化。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。
下表说明了左值引用右值引用对绑定量的限制
1.2指针
1.2.1指针的原理
变量的结构和原理
说道变量,很多人都觉得非常简单,每天都在定义变量,应用变量。可是有没有停下脚步细细的品味一下具体什么是变量呢?变量(variable)的定义在计算机科学中到底是如何定义的?然后variable到底是在内存中如何存储值的呢?那么跟着上面的问题,我们来一一的解答,首先最重要的,variable的定义,当你申明一个变量的时候,计算机会将指定的一块内存空间和变量名进行绑定;这个定义很简单,但其实很抽象,例如:int x = 5; 这是一句最简单的变量赋值语句了, 我们常说“x等于5”,其实这种说法是错误的,x仅仅是变量的一个名字而已,它本身不等于任何值的。这条statement的正确翻译应该是:“将5赋值于名字叫做x的内存空间”,其本质是将值5赋值到一块内存空间,而这个内存空间名叫做x。切记:x只是简单的一个别名而已,x不等于任何值。其图示如下:
变量在内存中的操作其实是需要经过2个步骤的:
1)找出与变量名相对应的内存地址。
2)根据找到的地址,取出该地址对应的内存空间里面的值进行操作。
指针的结构和原理
首先介绍到底什么是指针?指针变量和任何变量一样,也有变量名,和这个变量名对应的内存空间,只是指针的特殊之处在于:指针变量相对应的内存空间存储的值恰好是某个内存地址。这也是指针变量区别去其他变量的特征之一。例如某个指针的定义如下:
int x = 5;
int *ptr = &x;
ptr即是一个指正变量名。通过指针获取这个指针指向的内存中的值称为dereference,这个的中文翻译叫啥我也不知道。【惭愧】,哈哈。dereference其相对于内存空间的表示如下:
特别提醒:这里千万千万不要钻进变量名x, ptr的牛角尖里面,不要去思考这些变量名存储在哪里,变量名仅仅是一块内存空间的代号名字而已,我们应该关心的是这些变量名相对应的内存地址。根据上面的分析可以看出,指针变量和任何变量在内存中的形式是相同的,仅仅在于其存储的值比较特殊而已。
常量指针和指针常量区别?
常量指针是一个指针,读成常量的指针,指向一个只读变量。如int const *p或const int *p。
指针常量是一个不能给改变指向的指针。如int *const p。