C++primer-learning-notes-chapter2

237 阅读6分钟

指针字面值


  • nullptr

变量定义


  • 定义形式:类型说明符(type specifier) + 一个或多个变量名组成的列表。如int sum = 0, value, units_sold = 0;
  • 初始化:对象在创建时获得了一个特定的值
    • 初始化不是赋值
    • 初始化 = 创建变量 + 赋予初始值
    • 赋值 = 擦除对象的当前值 + 用新值代替
    • 列表初始化:使用花括号{},如int units_sold{0};
    • 默认初始化:定义时没有指定初始值会被默认初始化;在函数体内部的内置类型变量将不会被初始化。
    • 建议初始化每一个内置类型的变量。

变量声明和定义


  • 只声明而不定义: 在变量名前添加关键字 extern,如extern int i;。但如果包含了初始值,就变成了定义:extern double pi = 3.14;
  • 变量只能被定义一次,但是可以多次声明。

复合类型


引用(指的是左值引用) 右值引用见chapter13,主要用于内置类

  • 引用:引用是一个对象的别名,引用类型引用(refer to)另外一种类型。如int &refVal = val
  • 引用必须初始化。
  • 引用和它的初始值是绑定在一起的,而不是拷贝。

指针

  • 空指针 int *p1 = nullptr(c++11) 或 int *p2 = 0 或者首先#include cstdlib 再 int *p3 = NULL
  • 指向指针的引用 :引用本身不是一个对象,所以不能定义指向引用的指针。但指针是对象,所以存在对指针的引用。
int i = 42;
int *p;
int *&r = p;   //r是一个对指针p的引用
r = &i;   //即令p指向i
*r = 0;  //即将i的值改为0

const限定符


初始化和const

  • const对象必须初始化,且不能被改变
const int i = get_size() // 可行:运行时初始化
const int j = 42; //可行,编译时初始化
const int k; //错误
  • 默认状态下,const对象只在文件内有效
    以编译初始化方式定义一个const对象时,const int bufSize = 512;编译器将在编译过程中把用到该变量的地方都替换成对应的值。
  • 多个文件之间共享const对象,须在变量定义之前添加extern关键字
//file_1.cc
extern const int bufSize = fcn();
//file_1.h
extern const int bufSize;  //是同一个bufSize

const引用(对常量的引用)

const int ci = 1024;
const int &r1 = ci;
r1 = 42;   //错误 r1是对常量的引用
int &r2 = ci;  //错误 试图让一个非常量引用指向一个常量对象
int i = 42;
const int &r1 = i;//允许将const int& 绑定到一个普通int对象上
cosnt int &r2 = 42;
const int &r3 = r1*2;
int &r4 = r1*2;//错误 r4是一个普通的非常量引用
  • 常量引用对引用的对象本身是不是常量未作限定。
int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0;  //可修改
r2 = 0;  //错误,r2是一个常量引用。

指针和const(存放常量对象的地址,只能使用指向常量的指针)

const double pi = 3.14;
double *ptr = π //错误
const double *cptr = π
*cptr = 42;//错误,不可变
  • 与常量引用其引用对象是非常量相同,指向常量的指针也可指向非常量,只是"自以为是“的觉得自己指向了常量,所以自觉地不去改变所指对象的值。

const指针

  • 常量指针必须初始化,初始化完成后值不变。
int errNumb = 0;
int *const curErr = &errNumb; //将一直指向errNumb
const double pi = 3.14;
const double *const pip = π

顶层const(指针本身是常量)和底层const(指向对象是常量)

  • 拷贝时严格要求相同的底层const资格。注意两点:(1)int* 能转换成const int* (2)const int&可以绑定到一个普通int上

constexpr和常量表达式

  • 常量表达式指值不会改变并且编译过程就能得到计算结果的表达式。
const int max_files = 20;  //max_files是常量表达式
const int limit = max_files + 1; //limit是常量表达式
int staff_size = 27; //staff_size不是常量表达式
const int sz = get_size();  //sz不是常量表达式
  • constexpr变量:允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式,声明为constexpr的变量一定是一个常量,且必须用常量表达式初始化:
constexpr int mf = 20; //20是常量表达式
constexpr int limit = mf + 1; //mf+1是常量表达式
constexpr int sz = size() //当size是一个constexpr函数时才正确
  • 普通函数不能作为constexpr的初始值。若你认定变量是一个常量表达式,就把它声明成constexpr类型。
const int *p = nullptr;
constexpr int *q = nullptr;//constexpr把它所定义的对象置为了顶层const

处理类型

类型别名

  • 传统方法typedef
typedef double wages;
typedef wages base,*p;  //p是double*的同义词
  • c++11新标准:
using SI = Sales_item;
SI item; //相当于 Sales_item

指针,常量和类型别名

typedef char *pstring;
const pstring cstr = 0;
const pstring *ps;

auto类型说明符

  • 让编译器自动推断类型
auto item = val1 + val2;
auto i = 0,*p = &i;
auto sz = 0,pi = 3.14; //错误 类型不一致
  • 会忽略掉顶层const,同时底层const则会保留下来。
const int ci = i,&cr = ci;
auto b = ci;
auto c = cr;
auto d = &i;
auto e = &ci;//对常量对象取地址是一种底层const
  • 若希望推断出的auto类型是一个顶层const,需明确指出:
const auto f = ci;
  • 还可将引用的类型设为auto
auto &g = ci;
auto &h = 42;//错误 不能为非常量引用绑定字面值
const auto &j = 42;//可以为常量引用绑定字面值
  • decltype类型指示符(从表达式的类型推断出要定义的变量的类型,选择并返回操作数的数据类型)
decltype(f()) sum = x; //sum的类型就是函数f的返回类型
const int ci = 0,&cj = ci;
decltype(ci) x = 0;  //x的类型是const int
decltype(cj) y = x; //y的类型是const int&,y绑定到变量x
decltype(cj) z;  //错误 z是一个引用,必须初始化

int i = 42,*p = &i,&r = i;
decltype(r+0) b; //加法结果是int
decltype(*p) c; //c是int&,必须初始化

decltype((i)) d;  //d是int&,必须初始化
decltype(i) e;  //正确 e是一个未初始化的int

编写自己的头文件

  • 头文件通常包含哪些只能被定义一次的实体:类、const和constexpr变量。
  • 预处理器(preprocessor):确保头文件多次包含仍能安全工作。
  • 当预处理器看到#include标记时,会用指定的头文件内容代替#include
  • 头文件保护符(header guard):头文件保护符依赖于预处理变量的状态:已定义和未定义。
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data{
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
#endif