C++ 小白 学习记录--2

c++博大精深, 越看越觉得不会的太多. 回炉一下吧~ 大神们 请勿浪费时间~

第二章

细节比较多

2.1 算术类型和空类型  空类型 比较常见的就是void 函数无返回值时. 算术类型 整型 浮点型. 长度不同机器不同, c++只规定各种类型最短尺寸!!

C++ 小白 学习记录--2_第1张图片

一个char的大小和一个几个机器字节一样

一个int>=short, long >=int, long long >=long 类型长度的定义很有意思.

int short long  long long  有 有符号和无符号两种  而 char 则有三种, 编译器决定了char是有符号还是无符号的    这个为啥要这样定义啊?  所以使用char还是明确使用signed char 还是 unsigned char

选择 数据类型的推荐方案:

  1. 明确知道数据不可能为负时, 选无符号的

  2. 如果数值有可能超过int , 直接 long long

  3. signed char  or unsigned char   而  char  就当不存在吧

  4. 浮点 就 double

2.2 类型转换     假设char占8位

  1. bool 只认 0 和非零, 转换到int 就只有0 1

  2. signed char c2 =256  超出返回  则认为c2 未定义

  3. unsigned char c = -1,  涉及到原码 反码 补码,  正数 原 反 补 一样

      负数 -1=[10000001]原 =  [11111110]反 = [11111111]补  作为正数 结果就是255, 快速方法是 将-n  赋值给unsigned char = -n+256

字符面值常量   20(十进制) = 0x14(十六进制) = 024(八进制)

单引号 字符字面值, 双引号"hello world" 字符串字面值, 字符串字面值的长度=实际长度+1, 结尾 '\0'

转义字符 \n \v \t \b 等

指定字面值的类型  字符类型在字符前面如 L'a'   u8"hi"  数值在后   42 ULL 1E-3F  3.1415L

C++ 小白 学习记录--2_第2张图片

2.3 变量  {} () = 的区别

函数体外的变量会自动给个默认值, 函数体内的不会自动初始化, 直接使用会报错

long double ld = 3.1415926

int a{ld}, b={ld} 大括号如果存在丢失信息的风险则报错

int a(ld), b = ld  这种初始化 会丢信息,但是不报错.

声明和定义的区别

只声明 使用extern, 可以声明多次, 未申请空间 未初始化

定义, 只能定义一次, 申请空间了

作用域: 局部变量会覆盖全局变量, 如需使用全局变量需要使用::

int p44() {
    int unique = 0;
    std::cout << reused << "  " << unique << std::endl;
    int reused = 22;
    for (int reused = 0; reused < 2; ++reused) {
        std::cout << reused << " in for1  " << unique << std::endl; // 输出 0 0
        // todo 如何使用外部的reused, 即 reused=22 这个呢?
        std::cout << ::reused << " in for2  " << unique << std::endl; // 输出 42 0
    }
    std::cout << reused << "   " << unique << std::endl; // 输出 22 0
    std::cout << ::reused << "   " << unique << std::endl; // 输出 42 0
    return 0;
}

 

引用 &

1. 引用=别名 不是对象, 一旦定义 将无法再绑定到其余的对象

2. 引用必须被初始化

3. 引用不能定义引用 

4. 不能用字面值常量定义引用 如int &ref=10

指针 *  取地址&  如 int i =42, int *p = &i; 使用  cout<< *p 输出42

1. 指针是对象, 允许对指针赋值和拷贝, 可以先后指向不同的对象

2. 无须在定义时赋初值

定义空指针时 不能使用变量 如下:

    int zero = 0;
    int* p = 0; //可以这么定义空指针
    int* p1 = zero; // 不能这样定义空指针
    int* p2 = nullptr; //建议如此定义空指针

指针定义时初始化和再定义时 不同

    int ival = 1024;
    int* p1 = 0;
    int* p2 = &ival;
    p1 = &ival; //p1 是地址 *p1 是指向的对象

void*  用于存放任意对象的地址,  该地址中的对象未知; 类似于其他语言中的object, 用于装箱, 然后将来拆箱.  因为 指向的对象未知, 所以不能直接操作该指针指向的对象.

指向指针的引用  这个太绕了

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

// 从右向左分析: 离r最近的修饰符是&, 则r是一个引用. 再往左是*, 则说明r引用的是指针,  最后是int
    r = &i; // 因r是引用, 可以直接替换r为p 理解
    *r = 0; // 因r是引用, 可以直接替换r为p 理解

const 一旦创建就不能再改变, 必须初始化(编译时初始化, 运行时初始化) 文件级别的作用域, 如果想跨文件作用域 需要使用 extern const int bufSize=124; 

    const int readOnly = 200;
    const int& refReadOnly = readOnly;
    int readOnly2 = 100;
    const int& refReadOnly2 = readOnly2;
    int& refReadOnly3 = readOnly2;
    readOnly2 = 21;
    refReadOnly2 = 20; // 错误 不能修改 值
    refReadOnly3 = 222; // 正确 可以修改 值

可以定义一个常量引用, 保护可以修改的变量不被修改.  指针也可以这样

int &r1=42  (x)  const int &r1 = 42 (√)

注意以下指针定义的区别

int i=10;

const int *p = &i;  // *p不能修改, *p是const  值不能变

int *const p = &i; // p不能修改 而 *p 指向的对象值可以修改 地址不能变 必须定义时初始化

const int *const p =&i  // 值和地址 都不能变

顶层const 指针本身是常量, 即地址是常量

底层const 指针所指对象是常量

转换时 非常量转换为常量可以 反之 则不行( 只针对 底层 const, 顶层不受影响)

这个比较难, 下面是例子:

    int i = 0;
    int* const p1 = &i;
    const int ci = 42;
    const int* p2 = &ci;
    const int* const p3 = p2;
    const int& r = ci;

    int* p = p3;  // 错误, p3 含有const, p 没有
    p2 = p3; // 正确, p3 p2 都具有底层const
    p2 = &i; // 正确, int * 可以转 const int * 即非常量 转常量
    int& r = ci; //错误, ci是常量, 常量不能被非常量 绑定
    const int& r2 = i; // 正确, const int&可以绑定到一个常量上

常量表达式: 值不会改变且在编译过程就能得到计算结果的表达式. 字面值,const对象都是

const int sz=get_size(); // 这种不是常量表达式

constexpr 由编译器来验证变量值是否是一个常量表达式.

const int *p = nullptr; // p 是一个指向整型常量的指针 p可以变, *p不能变

constexpr int *q = nullptr;

// q 是一个指向整数的常量指针, q不能变, *q可以变. 类似 int *const q=nullptr 

constexpr const int *p =nullptr; // 实现双锁定

constexpr 仅对指针有效, 与其所指的对象无关

类型别名  typedef 这个很难理解, 必须把typedef 定义的内容 作为一个整体来看, 不能原样带入, 原样带入后 会出现理解错误. 例如下面的例子. 所以不能简单的通过带入理解, 需要把typedef定义的类型作为一个整体来理解

    char a = 'a';
    typedef char* pstring;
    const pstring cstr = 0;
    cstr = 1; // 错误 cstr 值是常量
    *cstr = 1; // 正确, *cstr指向的对象 不是常量
    const char* cstr2 = 0;
    cstr2 = &a;  // 正确 cstr2 不是常量
    *cstr2 = 'b'; // 错误 cstr2 指向的对象 是常量
    char* const cstr3 = 0; // typedef的等效定义
    cstr3 = 1;  // 错误 cstr3 是常量
    *cstr3 = 1; // 正确 cstr3 指向的对象 不是常量

auto类型 必须有初始值, 编译器自动分析类型. 好像var, let ?

auto一次定义多个变量时, 变量类型应该一致.

auto一般会忽略顶层const, 需要显示声明

int i=0;

const int ci =i, &cr=ci;

auto e = &ci; // e 指向整数常量的指针 常量取地址是一种底层const

auto f1 = ci; // 顶层const 被忽略掉. 相当于int f1 = ci

const auto f =ci;

decltype  获取操作/函数的类型, 但是不使用该操作/函数 初始化变量

int i=42, *p=&i, &r=i;

decltype(r+0) b; // b 的类型为int , 因为r+0运算后结果为int

 decltype(*p) c = i; // c为int& 引用类型, 引用类型必须初始化. 如果是解引用 则使用引用类型

decltype(i) d != decltype((i)) d // 右侧永远是引用, 此处是int&, 左侧是int类型 

decltype(a = b) d2; // 错误的声明. 赋值表达式 是引用类型, 引用必须初始化.

decltype 和 auto的区别

decltype 会返回一个变量的类型 包括顶层const, auto 不会

decltype 如果表达式解引用, 则会得到引用类型

decltype 加不加内部括号 影响重大, 赋值表达式会返回引用.

auto 必须要有初始值, 表达式的值会初始化变量

 

结构体(以前c中的称呼)  c++中也叫类 struct

struct xxx {} xxxName1, xxxName2, *xxxPtr;

预处理器, 用于处理头文件被多次包含的问题 

#define 预处理变量一般全大写, 用于检查下面的代码是否已经定义.  #ifdef, #ifndef  #endif

预处理变量 无视 作用域

这章好难~ 没个几遍 真的没法掌握

你可能感兴趣的:(c++学习,c++)