C++中文版本primer 第二章变量和基本类型 学习笔记

2.2变量

2.2.1 变量定义

列表初始化

  • 定义一个名字为units_sold的int变量并初始化为0
int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);
  • C++11 用花括号来初始化变量,上面这个步骤也称之为列表初始化。这种初始化有一个重要的特点,如果在初始化信息的时候存在丢失信息的风险那么编译器就会报错。丢失信息,是指在逆类型转换的时候,丢失数据的精度。
long double ld = 3.241592653;

int a {ld}, b = {ld}; //错误,转化未执行,因为存在丢失信息的风险

int c(ld), d = ld;    //正确,转换执行,但是会丢失部分数值

默认初始化

  • 如果定义变量没有指定初始值,则变量会默认初始化。初始化是由于变量的类型(标准类型/自定义类型)和所处的位置所决定的。定义在任何函数体之外的变量会被初始化为0。
  • 定义在函数体之内的变量不会被初始化,其数值是未被定义的,如果试图拷贝或者以其他方式来访问这个数值是会引发错误。
  • 类的对象如果没有显示的初始化,则其数值由类确定。

2.2.2 变量声明和定义的关系

  • C++使用分离式编译机制,该机制允许将程序分割为若干个文件,每个文件都可以被独立的编译。简单的例子就是,头文件的使用,使得程序代码之间可以互相共享代码。如引用头文件iostream,调用输入输出函数std::out。
  • 为了支持分离式编译机制,C++将声明和定义区分开来。声明使得程序的名字被程序所知,定义负责创建与名字相互关联的实体。变量声明规定了变量的类型和名字,这一点上定义与之相同。除此之外,定义还申请存储空间,也可能会给变量赋予一个初始化的数值。
  • 如果想声明变量但不定义它,在变量的名字之前使用extern关键字,而且不要显示的初始化变量。
extern int i; //声明i,并非定义i
int j;  //声明并未定义了j
  • 任何包含了显示初始化的声明就会变成了定义。当然也可以使用extern标记的变量赋一个初始化的数值,这就会抵消了extern的作用,一旦初始化就叫定义。
  • 变量只可以被定义一个,但是可以多次声明。

2.2.3 标识符

  • 用户自定义的标识符不可以连续出现两个下划线,也不可以使用下划线紧连大写字母开头,定义函数体外的标识符也不可以以下划线开头。

变量的命名规范

  • 标识符要体现实际的含义
  • 变量名使用小写
  • 用户自定义的类名一般使用大写
  • 如果用户定义的标识符由多个单词组成,则单词之间要有明显的区分。

名字的作用域

  • 作用域是程序的一部分,一般使用花括号进行分割。同一个名字在不同的作用域里面,可能指向不同的实体。名字的有效作用域始于名字的声明语句,结束于声明语句所在的作用域末端。

2.3 符合类型

2.3.1 引用

  • 引用为对象起了另外一个名字,引用类型引用另外一种类型。通过将声明符号写成&d的形式来引用类型,其中d是声明的变量名字。初始变量的时候,初始数值会被拷贝到新建的对象中。定义引用的时候,程序把引用和初始值绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和他的初始值绑定在一起。因为无法使引用重新绑定到另外一个对象,因此引用必须要初始化。
  • 引用并非对象,它只是为一个已经存在的对象所起的另外一个名字。
  • 引用不是一个对象,因此无法定义引用的引用。
  • int &a = 10; //错误,引用类型的初始值必须是一个对象
  • double dav = 3.15; int &rte = dav;错误,引用类型和被引用的对象的类型必须是一致的。
int ival = 1024;
int &refVal = ival;// refVal指向ival(是ival的另外一个名字)
int &refVal2 ;//错误,引用必须被初始化

2.3.2 指针

  • 指针是指向的另外一种类型的符合类型。和引用是类似的,指针也实现了对于其他对象的间接访问。
  • 指针本身就是一个对象,允许对于指针赋值和拷贝,而且指针的生命周期内可以先后指向相同类型的不同对象;指针无需在定义的时候进行赋值,和其他类型是一样的,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的数值。
  • 引用不是对象,没有实际的地址,因此不能定义指向引用的指针。
double dval;
double *pd = &dval;// 正确,初始值是double类型的对象地址
double *pd2 = pd;//   正确,初始值是指向double对象的指针

指针值

指针的值(即地址)应属于以下几种状态之一

  • 指向一个对象
  • 指向紧领对象所占空间的下一个位置
  • 空指针,意味着指针没有指向任何对象
  • 无效指针,也就是上面所述情况之外的其他数值

利用指针指向对象

  • 如果指针指向了一个对象,则可以允许使用解引用符*来访问该对象
  • 解引用操作适用于那些确定了指向某个对象的有效指针
int val = 42;
int *p = &val;//p存放着变量val的地址,或者说p是指向变量val的指针
cout << *p;   //由符号*得到指针p所指的对象,输出42

空指针

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

int *p1 = nullptr; //等价于int *p1 = 0;
int *p2 = 0;       //直接将p2初始化字面常量0
int *p3 = NULL;    //需要首先使用#include<cstdlib>  等效于int *p3 = 0
  • 使用nullptr字面值来初始化指针,这是一种特殊类型的字面值,可以被转化为任意其他的指针类型。另一种方法就如对p2的定义一样,也可以将指针初始化为字面值0来生成指针。

void*指针

  • void*是一个特殊的指针类型,用于存放人以对象的地址。一个void*指针可以存放一个地址,这个和其他地址是类似的。

  • void*指针的功能单一,拿他和别的指针比较、作为函数的输入和输出、或者赋值给另外一个void*指针,但是不可以直接操作这个void*所指的对象。

2.3.3 理解复合类型的声明

定义多个变量

  • int * p1,p2;//p1是指向int的指针,p2是单纯的int

指向指针的指针

指向指针的引用

2.4  const限定符

  • const对象一旦创建后其数值就不会被再次改变,因此const对象必须初始化。
  • const对象只在文件中有效
  • 在不同的文件中使用不同的const来定义不同的常量,那么每个文件定义的变量只会在自己所属的文件中有效。如果想让多个文件共享同一个const变量,那么使用关键字extern即可

2.4.1 const的引用

把引用绑定到const对象上,就像绑定到其他对象上一样,称之为对于常量的引用。和普通信用不同,对于常量的引用不能被用于修改它所绑定的对象。

const int ci = 1024;
const int &r1 = ci;//正确,引用及其对应的对象都是常量
r1 = 42;          //错误,r1是对于常量的引用
int &r2 = ci;     //错误,试图让一个非常量去引用一个常量对象  
  • 因为不允许直接为ci赋值,当然也不可以通过引用去改变ci,因此,对于r2的初始化是错误的,假设初始化合法,就可以通过r2来改变他引用的对象的数值,这显然是不正确的。

初始化和对const的引用

  • 引用的类型必须和其所引用对象的类型是一致的,但是有两个例外。1,初始化常量引用时候允许用任意表达式来作为初始化的数值,只要该表达式结果可以转化为引用的类型即可。尤其,允许一个常量引用绑定非常量的对象、字面值甚至是一个一般表达式。

    int i = 42;
    const int &r1 = i; //允许将const int& 绑定到一个普通int对象上
    const int &r2 = 42;//r2是一个常量的引用
    const int &r3 = r1 * 2;//r3是一个常量的引用
    int &r4 = r1 * 2;  //错误,r4是一个普通的非常量的引用

对const引用可能引用一个并非const的对象

  • 常量的引用仅仅对于可以引用可以参与的操作进行了限定,对于引用的对象的本身是不是一个常量未做限定。因为对象也可能是一个非常量,所以可以通过其他途径来改变它的值。

    int i = 42;
    int &r1 = i; //引用r1绑定对象i
    const int &r2 = i;//r2也绑定对象i,但是不允许通过r2来修改i的数值
    r1 = 0; //r1并非常量,i的数值修改为0
    r2 = 0; //错误,r2是一个常量的引用,因此不可以修改引用的元素的数值

猜你喜欢

转载自blog.csdn.net/CHYabc123456hh/article/details/108842535