C++ Primer 笔记之指针和引用

引用必须初始化

因为引用本身不是一个对象,所以不能定义引用的引用

引用的定义

允许在一条语句中定义多个引用,其中每个引用标识符都必须以符号&开头:

int i=1024, i2=2048; //i和i2都是int

int &r=i, r2=i2; //r是一个引用,与i绑定在一起r2是int

int i3=1024, &ri=i3; //i3是int,ri是一个引用,与i3绑定在一起

int &r3=i3, &r4=i2; //r3和r4都是引用

int &refVal4 = 10; //错误:引用类型的初始值必须是一个对象

double dVal = 3.14;

int &refVal = dval; //错误:此处引用类型的初始值必须是int型对象

空指针(null pointer)不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。

以下列出几个生成空指针的方法:

int *p1 = nullptr; //等价int *p1=0;

int *p2 = 0; //直接将p2初始化为字面常量0

//首先需要#include cstdlib

int *p3 = NULL; //等价于int *p3=0;

得到空指针最直接的办法就是用字面值nullptr来初始化指针,这也是C++11新标准刚刚引入的一种方法。

nullptr是一种特殊类型的字面值,它可以被转换成任意其他指针类型。

将int变量直接赋值给指针是错误的操作,即使int变量的值恰好等于0也不行。

int zero = 0;

pi = zero; //错误:不能把int变量直接赋给指针

建议:初始化所有指针

使用未初始化的指针是引发运行时错误的一大原因。

和其他变量一样,访问未经初始化的指针所引发的后果也是无法预计的。通常这一行为将造成程序崩溃,

而且一旦崩溃,要想定位到出错位置将是特别棘手的问题。

在大多数编译器环境下,如果使用了未经初始化的指针,则该指针所占内存空间的当前内容将被看作一个地址值。

访问该指针,相当于访问一个本不存在的位置上的本不存在的对象。糟糕的是,如果指针所占内存空间中恰好有内容,

而这些内容又被当做了某个地址,我们就很难分清它到底是合法的还是非法的。

因此建议初始化所有的指针,并且在可能的情况下,尽量等定义了对象之后在定义指向它的指针。如果实在不清楚

指针应该指向何处,就把它初始化为nullptr或者0.这样程序就能检测并知道它没有指向任何具体的对象了

指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中最重要的一点就是引用本身并

非一个对象。一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。

指针和它存放的地址之间就没有这种限制了。和其他任何变量(只要不是引用)一样,给指针赋值就是令它存放一个新的地址,

从而指向一个新的对象。

指向指针的引用

引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用:

int i=42;

int *p; //p是一个int型指针

int *&r=p; //r是一个对指针p的引用

r=&i; //r引用了一个指针,因此给r赋值&i就是令p指向i

*r=0; //解引用r得到i,也就是p指向的对象,将i的值改为0;

因为const对象一旦创建后其值就不能改变,所以const对象必须初始化。

const int k; //错误:k是一个未经初始化的常量

如果想在多个文件之间共享const对象,必须在变量的定义和声明之前添加extern关键字

初始化对常量的引用

const int ci = 1024;

const int &r1 = ci; //正确,引用极其对应的对象都是常量

r1 = 42; //错误:r1是对常量的引用

int &r2 = ci; //错误:试图让一个非常量引用指向一个常量对象

int i=42;

int &r1=i; //引用ri绑定对象i

const int &r2 = i; //r2也绑定对象i,但不允许通过r2修改i的值

r1 = 0; //r1并非常量,i的值修改为0

r2 = 0; //错误:r2是一个常量引用

指针和const

(指向常量的指针)

要想存放常量对象的地址,只能使用指向常量的指针:

const double pi = 3.14; //pi是个常量,它的值不能改变

double *ptr = π //错误:ptr是一个普通指针

const double *cptr = π //正确:cptr可以指向一个双精度常量

*cptr = 42; //错误:不能给*cptr赋值

double dval = 3.14; //dval是一个双精度浮点数,它的值可以改变

cptr = &dval; //正确:但是不通过cptr改变dval的值

tip:试试这样想吧:所谓指向常量的指针或引用,不过是指针或引用“自以为是”罢了,

它们觉得自己指向了常量,所以自觉地不去改变所指对象的值。

const 指针(常量指针)

指针是对象而引用不是,因此就像其他对象类型一样,允许把指针本身定为常量。常量

指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就

不能再改变了。把*放在const关键字之前用以说明指针是一个常量。

int errNumb = 0;

int *const curErr = &errNumb; //curErr将一直指向errNumb

const double pi = 3.14159;

const double *const pip = π //pip是一个指向常量对象的常量指针

指针本身是一个常量并不意味着不能通过指针修改其所指对象的值,能否这样做完全依赖于所指对象的类型

*pip = 2.72; //错误:pip是一个指向常量的指针

//如果curErr所指的对象(也就是errNumb)的值不为0

if(*curErr)

{

errorHandler();

*curErr = 0; //正确:把curErr所指的对象的值重置

}

int i = 0;

int *const p1 = &i; //不能改变p1的值,这是一个顶层const

const int ci = 42; //不允许改变ci的值。这是一个顶层const

const int *p2 = &ci; //允许改变p2的值,这是一个底层const

const int *const p3 = p2; //靠右的const是顶层const,靠左的是底层const

const int &r = ci; //用于声明引用的const都是底层const

当执行对象的拷贝操作时,常量是顶层const还是底层const区别明显。其中,顶层

const不受什么影响

i = ci; //正确:拷贝ci的值,ci是一个顶层const,对此操作无影响

p2 = p3; //正确:p2和p3指向的对象类型相同,p3顶层const的部分无影响

执行拷贝操作并不会改变被拷贝对象的值,因此,拷入和拷出对象是否是常量都

没什么影响。

另一方面,底层const的限制却不能忽视。当执行对象的拷贝操作时,拷入和拷出

的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一

般来说,非常量可以转换成常量,反之则不行:

int *p = p3; //错误:p3包含底层const含义,而p没有

p2 = p3; //正确:p2和p3都是底层const

p2 = &i; //正确:int*能转换成const int*

int &r = ci; //错误:普通的int&不能绑定到int常量上

const int &r2 = i; //正确:const int&可以绑定到一个普通int上

const int &r2 = i; //正确:const int&可以绑定到一个普通int上

p3既是顶层const也是底层const,拷贝p3时可以不在乎它是一个顶层const,但是

必须清楚它指向的对象得是一个常量。因此,不能用p3去初始化p,因为p指向的是

一个普通的(非常量)整数。另一方面,p3的值可以赋给p2,因为这两个指针都是

底层const,尽管p3同时也是一个常量指针(顶层const),仅就这次赋值而言不会

有什么影响。

时间: 2024-10-11 00:43:39

C++ Primer 笔记之指针和引用的相关文章

C++Primer 学习笔记之指针和引用

1.引用概念 引用引入了对象的一个同义词.定义引用的表示方法与定义指针相似,只是用&带起了*. 例如:Point pt1(10,10); Point &pt2 = pt1;//定义pt2为pt1的引用.通过这样的定义,pt1和pt2表示同意对象,需要特别强调的是引用并不是产生对象的副本,仅仅是对象的同义词.因此,当下面的语句执行后: pt1.ofset(2,2); pt1和pt2都具有了(12,12): 引用必须在定义的时候马上初始化,因为它必须是某个东西的同义词.你不能先定义一个引用后才

《c++primer笔记》复合类型--引用

复合类型:基于其他类型定义的语言 这里介绍两种 引用 指针 声明语句:一个基本数据类型,一个声明符列表组成 引用:为对象起另外一个名字 引用类型:引用另外一种类型 int ival = 1024; int &refVal = ival;        //refVal指向ival(是ival的另一个名字) int &refVal2;              //报错,引用必须被初始化 一般初始化变量:初始值会被拷贝到新建的对象中 定义引用:把引用和他的初始值绑定在一起,而不是拷贝给引用.

c++ primer笔记------智能指针

智能指针的陷阱 基本规范: 1.不使用相同的内置指针值初始化(或reset)多个智能指针.  double free 2.不delete get() 返回的指针.  double free 3.不使用get() 初始化或reset另一个智能指针.  double free 4.如果你使用get() 返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效了.   空悬指针 5.如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器.    非new类型指针需要自己指定

C++ Primer笔记8_动态内存_智能指针

1.动态内存 C++中,动态内存管理是通过一对运算符完成的:new和delete.C语言中通过malloc与free函数来实现先动态内存的分配与释放.C++中new与delete的实现其实会调用malloc与free. new分配: 分配变量空间: int *a = new int; // 不初始化 int *b = new int(10); //初始化为10 string *str = new string(10, ); 分配数组空间: int *arr = new int[10];//分配的

0715-----C++Primer听课笔记-----------函数指针 、单例模式

1.函数指针 1.1 普通成员函数指针包含类名信息以及const属性,指向具体函数是必须加上&符号. #include <iostream> using namespace std; class Test{ public: void setValue(const string &s, int a){ s_ = s; a_ = a; } void print() const{ cout << s_ << endl << a_ <<

C++学习笔记31,指向引用的指针(3)

我们来看一个简单的指向引用的指针的例子. #include <iostream> using namespace std; int main(){ int x=10; int y=20; int &rtx=x; //不要写成了int& *ptrx=&rtx; //因为rtx的本质是一个int int *ptrx=&rtx; *ptrx=15; ptrx=&y; rtx=y; cin.get(); } 依次单步执行:(注意rtx值的变化和ptrx的变化)

C++学习笔记30,指针的引用(2)

可以创建任何类型的引用,包括指针类型. 看一个简单的指针的引用的例子.例如: #include <iostream> using namespace std; int main(){ int x=10; int y=20; int z=30; int* ptx=&x; int* ptz=&z; //指针的引用,声明从右往左看,rtp与&结合, //剩余的符号和左边结合 //引用一旦创建,不能改变其指向,只能改变其值 int* &rtp=ptx; cout<

C++学习笔记----2.4 C++引用在本质上是什么,它和指针到底有什么区别

从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量). 在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的: 指针传递参数本质上是值传递的方式,它所传递的是一个地址值.值传递过程中,被调

C++_系列自学课程_第_8_课_指针和引用_《C++ Primer 第四版》

C语言最富有迷幻色彩的部分当属指针部分,无论是指针的定义还是指针的意义都可算是C语言中最复杂的内容.指针不但提供给了程序员直接操作硬件部分的操作接口,还提供给了程序员更多灵活的用法.C++继承这一高效的机制,同时引入了另一个与指针相似但不相同的机制: 引用. 一.引用 简单的来说,引用就是变量的别名(alias), 通过别名我们可以操作引用代表的变量. 定义一个引用的语法如下所示: 变量类型   &引用标识符 = 变量名. Exp: int iVar=10; int &iRef = iVa