zoukankan      html  css  js  c++  java
  • 《c++primer》笔记 第2章 变量和基本类型

    2.1 基本内置类型

    如何选择类型:

    • 当明确知晓数值不可能为负时,选用无符号类型。
    • 使用int执行整数运算。
    • 在算术表达式中不要使用char或bool。
    • 执行浮点数运算选用double。

    提示:切勿混用带符号类型和无符号类型.

    2.2 变量

    初始化不是赋值,初始化的含义是创建变量时賦予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代.

    初始化的4种形式:

    int units_sold = 0;
    int units_sold = {0};
    int units sold{0}; //{}列表初始化,c++11
    int units_sold(0);

    如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错.

    • 默认初始化

      1. 定义于任何函数体之外的变量被初始化为0。

      2. 定义在函数体内部的内置类型变量将不被初始化(uninitialized)。一个未被初始化的内置类型变量的值是未定义的,如果试图拷贝或以其他形式访问此类值将引发错误。

    #include <iostream>
    #include <string>
    using namespace std;
    int main() {
        int a;
        cout << a << endl;
        return 0;
    }
    这样会报错。
    #include <iostream>
    #include <string>
    using namespace std;
    int a;
    int main() {
        cout << a << endl;
        return 0;
    }
    这样会输出0.

    建议初始化每一个内置类型的变量。虽然并非必须这么做,但如果我们不能确保初始化后程序安全,那么这么做不失为一种简单可靠的方法。

    变量声明规定了变量的类型和名字,在这一点上定义与之相同。但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。

    如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量。

    2.3 复合类型

    • 引用与指针的区别

    引用:

    1. 引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字.
    2. 引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起.
    3. 无法令引用重新绑定到另外一个对象,因此引用必须初始化。

    指针:

    1. 指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
    2. 指针无须在定义时赋初值。

      • 几个生成空指针的方法:
    int *pl = nullptr;  //等价于int*pl=0; c++11
    int *p2 = 0;        //直接将p2初始化为字面常量0
    //需要首先#include <cstdlib>
    int *p3 = NULL;     //等价于int*p3=0;
    1. 在新标准下,现在的C++程序最好使用nullptr,尽量避免使用NULL。
    2. 把int变量直接赋给指针是错误的操作,即使int变量的值恰好等于0也不行。

      • 建议:初始化所有指针

      尽量等定义了对象之后再定义指向它的指针。如果实在不清楚指针应该指向何处,就把它初始化为nullptr或者0,

      • void指针

    由于void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值,因此还可以用void指针来作为函数形参,这样函数就可以接受任意数据类型的指针作为参数(参考:深入理解void以及void指针的含义)。例如:

    void * memcpy( void *dest, const void *src, size_t len );
    void * memset( void * buffer, int c, size_t num );
    • 面对一条比较复杂的指针或引用的声明语句时,从右向左阅读有助于弄清楚它的真实含义
    int *p;      //p是一个int型指针
    int *&r = p;   //r是一个对指针p的引用

    要理解r的类型到底是什么,最简单的办法是从右向左阅读r的定义。离变量名最近的符号(此例中是&r的符号S)对变量的类型有最直接的影响,因此r是一个引用。声明符的其余部分用以确定r引用的类型是什么,此例中的符号*说明r引用的是一个指针。最后,声明的基本数据类型部分指出r引用的是一个int指针。

    2.4 const限定符

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

    默认情况下,const对象被设定为仅在文件内有效。

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

    • 对常量的引用
    const int ci = 1024;
    const int &rl = ci; //正确:引用及其对应的对象都是常量
    rl = 42;            //错误:rl是对常量的引用
    int &r2 = ci;      //错误:试图让一个非常量引用指向一个常量对象
    • 容易搞反的两个定义

    1.pointer to const

    const double pi = 3.14;//pi是个常量,它的值不能改变
    double *ptr = &pi;//错误:ptr是一个普通指针
    const double *cptr = &pi;//正确:cptr可以指向一个双精度常量

    2.const指针(const pointer)

    int errNumb=0;
    int *const curErr = &errNumb; // curErr将一直指向errNumb
    • 两个const

    用名词顶层const(top-level const)表不指针本身是个常量,而用名词底层const(low-levl const)表示指针所指的对象是一个常量。

    const int *const p3 = 
    底层        顶层

    可以这样理解,上面是指针,下面是指针指向的位置。

    image

    上面说的是指针的,一般情况,顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算术类型、类、指针等。底层const则与指针和引用等复合类型的基本类型部分有关。

    2.5处理类型

    为什么要用类型别名?

    一是一些类型难于“拼写”,它们的名字既难记又容易写错,还无法明确体现其真实目的和含义。二是有时候根本搞不清到底需要的类型是什么,程序员不得不回过头去从程序的上下文中寻求帮助。

    两种方法可用于定义类型别名:

    1.typedef

    typedef double wages;//wages是double的同义词
    typedef wages base, *p;//base是double的同义词,p是double*的同义词

    2.别名声明(alias declaration)

    using SI = Sales_item;//SI是Sales_item的同义词 c++11
    • auto

    auto让编译器通过初始值来推算变量的类型。显然,auto定义的变量必须有初始值

    //由vail和val2相加的结果可以推断出item的类型
    auto item = val1 + val2;//item初始化为val1和val2相加的结果

    auto—般会忽略掉顶层const,同时底层const则会保留下来

    int i = 0,&r = i;
    auto a = r;//a是一个整教(r是i的别名,而i是一个整数)
    const int ci = i, &cr = ci;
    auto b = ci;//b是一个整数(ci的顶层const特性被忽略掉了)
    auto c = cr;//c是一个整数(cr是ci的别名,ci本身是一个顶层const)
    auto d = &i;//d是一个整型指针(整数的地址就是指向整数的指针)
    auto e = &ci;//e是一个指向整数常量的指针(对常量对象取地址是一种底层const)
    • auto与decltype:

    1.auto从表达式推断出要定义的变量的类型,并用表达式进行初始化。
    2.decltype也从表达式推断出要定义的变量的类型,但是不用该表达式的值初始化。

    2.6 自定义数据结构

    • 为什么类体右侧的表示结束的花括号后必须写一个分号?

      这是因为类体后面可以紧跟变量名以示对该类型对象的定义,所以分号必不可少。

    struct Sales_data { /* ... */ } accum, trans, *salesptr;
    // 与 上 一 条 语 等 价 ,但 可 能 更 好 一 些
    struct Sales_data { /* ... */ };
    Sales_data accum, trans, *salesptr;

    最好不要把对象的定义和类的定义放在一起。

    类的初始化:

    #include <iostream>
    #include <string>
    using namespace std;
    struct sale{
        int a;
        string s; // 会初始化为空
    };
    int main()
    {
        struct sale sa;
        //cout << """ << sa.s << """ << endl;
        cout << """ << sa.a << """ << endl;
        return 0;
    }

    当sale里面有string和int时,a将被初始化为一个不确定的数。如下是多次运行后的结果:

    "-858993460"
    "-858993460"
    "-858993460"

    当把string s注释后被报错,说a没有初始化,忽略错误后依然会得到一个不确定的数,为什么上面的不报错呢?因为string可以初始化为空串?

    #include <iostream>
    #include <string>
    using namespace std;
    struct sale{
        int a;
        //string s;
    };
    int main()
    {
        struct sale sa;
        //cout << """ << sa.s << """ << endl;
        cout << """ << sa.a << """ << endl;
        return 0;
    }
    • 预处理器

    预处理器是在编译之前执行的一段程序,可以部分地改变我们所写的程序。
    1. #include

    之前已经用到了一项预处理功能#include,当预处理器看标记时就会用指定的头文件的内容代替#include。
    

    2. 头文件保护符

    #ifndef SALES_DATA_H
    #define SALES_DATA_H
    /*.......*/
    #endif
    • 整个程序中的预处理变量包括头文件保护符必须唯一

    通常的做法是基于头文件中类的名字来构建保护符的名字,以确保其唯一性。为了避免与程序中的其他实体发生名字冲突,一般把预处理变量的名字全部大写。

    • 头文件即使(目前还)没有被包含在任何其他头文件中,也应该设置保护符
  • 相关阅读:
    【译】用 Chart.js 做漂亮的响应式表单
    【译】快速高效学习Java编程在线资源Top 20
    【译】理解Spring MVC Model Attribute 和 Session Attribute
    Github 恶搞教程(一起『玩坏』自己的 Github 吧)
    Effective Java 读书笔记(一):使用静态工厂方法代替构造器
    JavaScript 中 onload 事件绑定多个方法的优化建议
    【译】常见 Java 异常解释(恶搞版)
    Java 重写 equals 与 hashCode 的注意事项
    【译】Java语言速览:StackOverflow
    【译】StackOverflow——Java 中的 finally 代码块是否总会被执行?
  • 原文地址:https://www.cnblogs.com/shanchuan/p/8150288.html
Copyright © 2011-2022 走看看