C++Primer变量

来源:互联网 发布:h3c配置多个端口 编辑:程序博客网 时间:2024/05/16 12:19

变量初始化

作为C++11新标准的一部分,用花括号来初始化变量,还有一种方式是列表初始化。现在无论是初始化对象还是某些时候为对象赋新值,都可以使用这样一组由花括号括起来的初始值。
当使用内置类型的变量时,这种初始化形式有一个重要特点。如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错
long double ld=3.1415926536;
int a{ld},b={ld};
int c(ld),d=ld;

默认初始化

  • 如果内置类型的变量未被显式初始化,它的值由定义的位置决定。定义于任何函数体之外的变量被初始化为0。
  • 一个例外情况是,定义在函数体内部的内置类型变量将不被初始化。一个未经初始化的内置类型变量的值是未定义的,试图拷贝或者以其他形式访问此类设置值将引发错误
  • 变量的声明和定义

    C++支持分离式编译(separate compilation)机制,该机制允许将程序分割为若干个文件,每个文件可以被独立编译。

    声明

    使得名字为程序所知,一个文件如果想使用别处定义的名字,则必须包含那个名字的声明。
    变量声明规定了变量的类型和名字,在这一点上定义与之相同,但是定义申请存储空间,也可能会为变量赋初始值。

    定义

    负责创建与名字关联的实体。
    如果想声明一个变量而不是定义它,就在变量名前添加关键字extern,而且不要显式初始化变量。

    extern int i; //声明i而非定义iint j;          //声明并定义j//任何包含了显式初始化的声明即成为定义。我们能给由extern关键字标记的变量赋一个初始值,但是这么做就抵消了extern的作用。extern语句如果包含初始值就不是声明,而是定义extern double pi=3.1416;  //定义

    函数内部,试图初始化一个由extern关键字标记的变量,将引发错误;
    变量能且只能被定义一次,但是可以被多次声明。

    名字的作用域

    不论在程序的什么位置,使用到的每个名字都会指向一个特定的实体:变量、函数、类型等,同一个名字出现在不同的实体,页可能指向的是不同实体。
    作用域是程序的一部分,在其中名字有其特定的含义,C++语言中大多数作用域以花括号分隔。

    复合类型

    1.引用

    引用为对象起了另外一个名字,引用类型引用另外一种类型。
    引用并非对象,它只是为一个已经存在的对象起一个另外的名字。
    引用本身不是一个对象,所以不存在定义引用的引用。
    引用只能绑定在对象上,而不能与某个字面值或者表达式的计算结果绑定在一起
    int &refVal4=10 ;//❌
    double dval=3.14; int &refVal5=dval ;//❌ 此处引用的类型初始值必须是int型对象

    2.指针

    指针是指向另外一种类型的复合类型,与引用类似,指针页体现了对其他对象的间接访问。

  • 指针本身是一个对象,允许对指针赋值和拷贝,指针在声明周期可以指向不同对象
  • 指针无需在定义时候赋值
  • 特点:
    引用和指针都提供对对象的间接访问。一旦定义了引用,就无法在令其绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象;
    指针和它存放的地址之间没有这种限制,给指针赋值就是令其存放一个新地址,从而指向一个新对象。
    任何而非0指针对应的条件值都是true。

    指向指针的指针

    指针是内存中的地址,像其他对象一样也有自己的地址,因此允许把指针的地址在存放到另一个指针当中。
    解引用一个int型指针会得到一个int型的数,解引用指向指针的指针会得到一个指针。

    指向指针的引用

    int i=42;
    int *p; //p是一个int型的指针
    int *&r=p; //r是一个对指针p的引用
    从右往左读:&表示r是引用,*表示r引用的是一个指针,最后基本数据类型部分指出r引用的是一个int指针
    r=&i; //r引用了一个指针,因此给r赋值$i就是令p指向i
    *r=0; //解引用r得到i,也就是p指向的对象,将i的值改为0

    const限定符

    const对象一旦创建成功其值就不能被改变,所以const对象必须初始化。
    const int i=get_size(); //正确
    const int j=42; //正确
    const int k; //错误
    如果要在多个文件之间共享const对象,必须在变量定义之前添加extern关键字。

    const的引用

    可以把引用绑定到const对象上,称为对常量的引用;
    const int ci=1024;
    const int &r1=ci;//引用及其对应的对象都是常量
    r1=42; //错误,r1是对常量的引用;(不允许直接为ci赋值,当然也不能通过引用去改变ci)
    int &r2=ci; //错误,试图让一个非常量引用指向一个常量对象

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

    常量引用仅对引用可参与的操作作出了限定,对于引用的对象本身是不是一个常量未作出限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值:
    int i=42;
    int &r1=i;
    const int &r2=i; //r2也绑定对象i,但是不能通过r2修改i的值。
    r1=0; //r1并非常量,i的值可以改为0;
    r2=0; //r2是一个常量引用,不允许通过r2修改i的值。

    指针和const

    指向常量的指针不能用于改变其所指向对象的值。要想存放常量对象的地址,只能使用指向常量的指针;
    const double pi=3.14;
    double *ptr=π //错误:ptr是一个普通指针
    const double *cptr=π //正确,ptr可以指向一个双精度常量
    *cptr=42; //❌,不能给*cptr赋值

    指向常量的指针没有规定其所指向的对象必须是一个常量,指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变。

    const指针

    指针是对象,因此允许把指针本身定位常量。常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变。
    指针本身是一个常量并不意味着不能通过指针来修改其所指对象的值,能否这样做完全依赖于所指向的类型;如果一个指向常量的常指针,那么指向的对象值还是自己存储的那个地址都不能被改变;如果指向的是一个 一般的非常量整数,那么可以修改值。

    static:局部静态对象

    某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成static类型从而获得这样的对象、局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

    size_t count_calls(){    static size_t ctr=0;      return ++ctr;}int main(){    for(size_t i=0;i!=10;++i)    cout<<count_calls()<<endl;  }

    在控制流第一次经过ctr定义之前,ctr被创建并且初始化为0,每次调用ctr加1并返回新值。每次执行count_calls函数,ctr的值都已经存在并且等于上一次退出时ctr的值。

    参数传递

    每次调用函数时候都会重新创建它的形参,并用传入的实参对形参进行初始化。
    如果实参是引用类型,它将绑定到对应的形参上。否则将实参的值拷贝后赋给形参。
    当形参是引用类型时,我们说它对应的实参被引用传递或者函数被传引用调用。和其他引用一样,引用形参也是它绑定的对象的别名;也就是说,引用形参是它对应实参的别名。

    使用引用而非常量引用可以极大地限制函数所能接受的实参类型;我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。
    string ::size_type find_char(string &s,char c,string::size_type &occurs);
    则find_char函数作用于string对象,
    类似于下面的调用将出错find_char(“hello world”,’c’,ctr); //❌