Skip to content

C++ 基本语言

2282字约8分钟

cpp程序语言

2022-06-09

本文记录了cpp基本语言查缺补漏部分。

参考书籍:《C++ Primer 第五版》

变量和基本类型

如何选择类型

  • 明知值不为负时,选用无符号类型
  • 优先使用 int 执行整数运算,如果超过,选择 long long
  • 在算术表达式中不使用 char 或者 bool,非要使用的话,加上 signedunsigned
  • 执行浮点数运算采用 double ,精度够且计算代价相差无几

切勿混用带符号类型和无符号类型

如果说表达式既有带符号类型和无符号类型,当带符号类型为负时会出现异常结果,这是因为带符号数会自动转换成无符号数。

变量的列表初始化

int units_sold = 0;
int units_sold(0);
int units_sold = {0};
int units_sold{0};

作为 C++11 新标准的一部分,用花括号来初始化变量得到了全面应用。当用于内置类型的变量时,这种初始化形式有一个重要特点:如果我们使用列表初始化且初始值存在丢失信息的风险时,编译器将报错

概念:静态类型

C++ 是一种静态类型语言,其含义是在编译阶段进行类型检查。

C++关键字查缺补漏

// alignas 设置内存中对齐方式,最小是 8 字节对齐,可以是16,32,64,128等
// alignof 求变量在内存中的对齐方式
// 注意这里对齐的修饰对象
struct alignas(8) struct {
    char c;
    // ...
    // ...
}
// asm 内嵌汇编语句
// Intel 与 AT&T 可选
// e.g.
asm("mov eax, 1");
asm(
    "mov eax, 1\n"
    "leave\n"
    "ret\n"
);
// 如果类的成员函数不会改变对象的状态,那么这个成员函数一半会声明成 const
// 但如果需要修改跟类状态无关的数据成员,那么这个数据成员就应该由 mutable 修饰
class example {
    public:
    // ...
    // const 函数需要修改与类状态无关的变量
    void outPut() const {
        // ...
        // 改变类状态无关的变量
        ++iTimes;
    }
    private:
    	mutable iTimes = 0;
}

引用

引用即别名,非对象。必须赋初值,不能更改。

const 限定符

  1. 对普通变量的 const 限定:
const int i = get_size(); // 正确,运行时初始化
const int j = 5;          // 正确,编译时初始化,即进行替换
const int k;              // 错误,未初始化
  1. const 限定符只在本文件有效,若要在多文件共享,需加上 extern 关键字
// file_1.cc
extern const int bufsize = fcn();
// file_2.cc
extern const int bufsize;         // 这两个文件的 bufsize 是同一个
  1. 对 const 引用简称为 “常量引用”。明确 const 限定符只是引用变量的自我约束(约束自己不修改自己的引用对象)(引用对象是常量则是两情相悦,引用对象不是常量就是一厢情愿),明确引用即别名(即可以通过它来修改变量)。
int i = 42;
const int ki = 42;
const int &kqi1 = i;     // 正确,const 只是引用的一厢情愿
const int &kqi2 = ki;    // 正确,正统的两情相悦
const int &kqi3 = i * 3; // 正确,对象是一个临时常量
int &qi1 = i;            // 正确,引用即别名
int &qi2 = ki;           // 错误,引用是别名,无法修改常量
int &qi3 = i * 3;        // 错误,引用是别名,无法修改一个临时常量
  1. const 指针,阅读方式为从右往左阅读。
// 使用时常量要遵循初始化的要求。
const int *p1;          // 是指针,指向一个常量 int,自我约束不修改指向的内容
int * const p2;         // 是常量,这个常量是指针
const int * const p3;   // 是常量,这个常量是指针,指向一个常量 int(自以为的)
  1. 顶层 const 可以理解为变量本身的 const 属性,底层 const 理解为隐藏在变量之下的变量(如指针指向的变量、引用所引用的对象)的 const 属性。前者可以用普通 const 限定符考究,后者则是记住顶层对底层的可修改性确定是否能够这样赋值。
  2. constexpr 常量表达式(作用于指针时,它是顶层的,显而易见,需要在编译期间得到地址值):加此限定符保证表达式是由常量构成(能够在编译期就能确定的值,不一定需要字面值,也可以是由 constexpr 组成的)。

类型别名

  1. typedef Sales_item SI
  2. using SI = Sales_item

:::default

注意 const 对别名的修饰,是别名的顶级 const

:::

auto 类型说明符

  • 引用是属于变量的别名,auto 推断时并不会推出来,故需要一个引用要显式地加上
  • auto 一般会忽略顶层 const,如果需要要显式地加上 const;底层的 const 会保存下来
  • 符号 & 和 * 从属于某个声明符,故初始值必须为同一类型。
int i = 1;
const int ki = 1;
// 错误,前者来说 auto 被推断为 int,后者因为底层 const 被保留被推断为 const int
// 所以类型不一致,故错误
auto &n = i, *p = &ki;

decltype 类型指示符

(C++ 11 新增 C++ 14取消)通过 decltype 推出变量的类型。auto 是要利用初始化来推测,而 decltype 则不然。

// decltype(declared type)
// 如果表达式是变量,返回变量的类型(包括顶层 const 和 引用 在内。
const int ci = 0, &cj = ci;
decltype(ci) x = 0; // x 类型是const int
decltype(cj) y = x; // y 类型是const int &
decltype(nj) z;     // 错误,引用类型需要初始化

// 对于指针解引用(生成左值)得到的是引用变量
int i = 1, *p = &i;
decltype(*p) c = i; // c 的类型是 int& 而不是 int

// decltype 普通变量加括号得到的可作为左值赋值语句的特殊表达式
decltype((i)) d = i;// d 的类型是 int&
decltype(i) e;      // e 的类型是 int

表达式

算术类型转换

  • 先整型提升 (比 int 小的转换至 int)
  • 对于相同大小的有符号与无符号的变量转换至无符号
  • 对于不同大小的,谁表示大的转换至谁

其他类型的隐式转换

  1. 数组转换至指针(在 decltype & sizeof typeid不会发生)
  2. 转换成 bool 类型
  3. 转换至常量
  4. 指针至 void 相关转换

显式转换

// cast-name<type>(expression)
// cast-name: static_cast, dynamic_cast, const_cast, reinterpret_cast
// 旧式的显示转换 (type)expr type(expr) 难以调试不建议使用

// static_cast
double slope = static_cast<double>(j) / i;

// const_cast 改变底层 const,不再一厢情愿地不修改
const char *pc;
char *p = const_cast<char*>(pc);

// reinterpret_cast 重新解释一个指针,非常危险
// 对于这个操作权限交予程序员
int *pi = i;
char *pc = reinterpret_cast<char*>(pi);

函数

含有可变参数的形参

initializer_list<type> ls 具有若干操作

函数的默认参数

  1. 可以对部分进行参数默认,一旦一个参数默认,其后所有的参数都得默认实参
  2. 可以对函数进行重新声明,此时不能修改原有实参,只能在原函数参数列表中没有默认实参的新添加默认实参
  3. 在函数调用时,只能从前往后依次更改默认实参的值。(要求设计时经常需要自定义参数的参数出现在前面)

函数重载

同一作用域内几个函数名字相同而形参列表不同称之为重载(overload)函数

对于形参列表来说,顶层 const 不区分而区分底层 const.

函数参数匹配

  1. 精确匹配
    • 实参类型和形参类型相同。
    • 实参从数组类型或函数类型转换至相对应的指针类型
    • 向实参中添加或删除顶层 const
  2. 通过 const 转换实现的匹配
  3. 通过类型提升实现的匹配
  4. 通过算术类型转换或指针转换实现的匹配
  5. 通过类类型转换实现的匹配

关于返回

:::default

在含有 return 语句循环的后面也应该有 return 语句。很多编译器无法发现这种错误。

:::

尾置返回类型 auto func(int i) -> int(*)[10];

inline 函数与 constexpr 函数

inline 函数

内联函数在每个调用点“内联地”展开。内联说明只是向编译器发起请求,编译器可以忽略这个请求。

constexpr 函数

constexpr 要求返回值类型和所有形参的类型都是字面值类型,而且函数体中有且只有一条 return 语句。C++ 不要求 constexpr 函数必须返回常量表达式,但要求 constexpr 函数一定时可以在编译期间就确定返回值的(必须可以返回常量表达式)。

调试帮助

  1. assert(expression)
  2. 依赖于预处理变量 NDEBUG ,也可自定义调试 bug