标签归档:c++

[Cpp基础][11]”异常”

异常

  • C++内所说的异常是指:可以预见的非正常状况,例如输入的指针为空;而非不可预见的问题,例如突然停电,或者突然被用户把进程kill掉,并不是C++需要处理的"异常"
  • 异常特性会导致程序的执行流程不可控,且往往对OS及runtime有一定的要求(可移植性差),所以没有特殊需求时,不应当使用这个特性.
  • throw-try-catch是异常系统的典型三个环节.
    • throw出的可以是任意对象,只要catch处声明的对象可以用throw的对象初始化即可.习惯上我们会专门设计一个类,因为自定义类可以承载更多的信息
    • 异常catch中的形参可以使用引用,以使用多态机制
  • 异常抛出后寻找catch的过程称为栈展开,被展开的函数栈内所有局部对象都将被销毁,因此抛出的异常对象必须不依赖局部对象.
  • 标准库内提供了以exception为基类的若干异常,我们可以使用这个类,也可以自定义类,该类的const char * what()成员用于给用户提供信息.
    • catch时,优先使用引用, 从而保证能派生类实例能绑定到基类参数上.
Read More

[Cpp基础][10]重载运算符

重载运算符

规定

  • 赋值,取下标,调用,箭头,类型转换,这些运算符只能作为成员函数.
  • 用户重载的&&||不会进行短路求值.

习惯

  • 重载主要分为全局重载和类内重载,一般而言,二元运算都应该在全局重载(顺序问题).
  • 改变对象状态的运算符一般应当设为成员函数,拥有对称性的运算符一般应在类外重载.
  • 从技术层面说,重载运算符的参数可以是指针类型的,但是这一般与我们使用语言的习惯不符,所以形参类型最好只是TT&,而不要是指针.
  • 定义有关联的重载运算符时一定要考虑兼容性,例如a<b为假,a>b
Read More

[Cpp基础] [07] 复杂类型和类型推断

数组类型和函数类型

尽管在大部分场合中,数组类型和函数类型都被转换成某种指针使用,然而它们确实是某种类型.
这一点在类型推导中很重要,因为类型推导可以推导出数组类型/函数类型

  • 数组类型逻辑上类似于std::array,由TN两个属性决定唯一的数组类型,称T[N]型对象为数组类型,其中N必须为constexpr,称T array[N]T[N]的一个实例.
    • T
Read More

[Cpp基础] [06] 强制类型转换

  • C++风格的类型转换有四种,每个都有特定的应用场合.转换可以分为隐式或显式的,显式转换也被称为强制类型转换.
    • 隐式类型转换语义上只能对应static_cast或const_cast二者其一.
  • 规定:仅有xxx_cast<TYPE &>(var)整体可以作为左值,其他情况转换得到的都是右值临时对象。

强制类型转换

const_cast

  • const_cast是只用于处理const相关的类型转换,主要是移除const(加const完全可以用隐式转换), const_cast只有向引用/指针的转换有实际意义.
const T c_obj;
const 
Read More

[Cpp基础] [05] const与constexpr

Const Expression, constexpr 是一个非常复杂的话题, 幸运的是, 我们在实践中
需要记住的内容并不太多,因为常见的应用场景其实比较简单.

const限定符

  • const是一种编译期特性,用于限定对象的编译期写入权限, 编译器也可能将const对象视作constexpr进行优化.
    • 理论上可以通过各种trick在运行期写入对象,但是不要这么做.
Read More

[Cpp基础] [04] 名字查找与函数重载

名字查找

  • 当我们使用一个名字时,编译器就会向上查找名字的声明语句.变量名的查找和函数名的查找结果有一定区别.
    • 变量名查找:最终仅会确定唯一的对象,从内到外碰到的第一个名字将被使用.
    • 函数名查找:会先确定搜索域,然后按重载规则选择最有匹配. 搜索域包含一般scope和参数scope.
  • 常规搜索域确定:从当前作用域开始,逐层向上查找名字声明,名字首次出现的作用域就是常规搜索域
  • 对于函数调用的每个实参arg_i,其类型decltype(arg_i)及其所有基类所在的作用域都加入实参类型搜索域
  • 函数名和变量名都是名字,彼此可以相互hide.

重载匹配规则

Read More

[Cpp基础] [03] 变量声明-定义及初始化

变量的"声明,定义"以及"初始化"

想要彻底理解这一部分的内容,你应该对OS/进程/虚拟内存/编译/链接过程有一定的了解,仅从语言层次学习是比较抽象的.

声明和定义

  • 从编译的角度看:
    • 仅声明某个变量,那么就只创建了Symbol,这个Symbol对应的存储空间需要在后续的链接过程中resolve.
    • 定义某个变量,在创建Symbol的同时,编译器为其分配了存储空间, 这个存储空间可以被链接器reslove道引用这个Symbol的地方.
  • 对开发者而言:
    • 如果你希望使用某个已经存在的对象,那么就应该使用声明语句.
    • 每个定义语句都有"声明"的效果,不存在只定义不声明的语句.
  • 就C++而言,若语句仅有extern,且不含任何初始化部分,则该语句就是一个纯粹的声明,例如
    • extern T &val;//纯声明
    • extern T val;//纯声明
    • extern
Read More

[Cpp基础] [02] 拷贝/移动与引用

这一部分主要介绍左值,右值,引用,拷贝和移动.这些可以说是C++11显著区别于以前的C++(还有C)的特性.本文主要从易用的角度介绍值类型(值类型实际要更多一些).

拷贝与移动的基本定义

  • "拷贝"和"移动"是从逻辑层面定义的,在最终的指令层面,只有"拷贝",没有"移动"操作.
  • 对于自定义类,拷贝与移动的实现有基本的准则(非硬性),这些准则是:
    • 拷贝:不应对源对象做任何修改.
    • 移动:可以对源对象做修改,但是源对象被修改后必须可以被赋值,且可以安全的析构.
  • 注意,对于移动操作.我们不能对移出后剩余的部分做任何假定,在没有重新赋值前,不应该使用移出后剩下的对象
    • 例如:对一个vector<int> a;,其中的所有元素都被auto b = std::move(a);移出后,不能假定a.size()值为0了.
  • 一般而言:
    • 对于同时支持拷贝和移动的类型,我们一般认为,移动操作的性能开销应该优于拷贝.
Read More

[Cpp基础] [01] Cpp编译系统基础

编译系统基础

工具链

  • 一个典型的工具链是:链接器-编译器(汇编器一般不接触)-预处理器-文本编辑器-构建系统

    • 调试工具及profile工具通常作为独立的部分出现.
  • 构建系统,或者称为工程管理器,对于开发意义重大,是工具链中重要的一环,其意义在于组织文件,管理源代码,明确目标输出,组织编译/链接的顺序和关系.

    • 常见的构建系统有,VS的nmake,QT的qmake,跨平台的CMAKE(使用makelist.txt),unix平台的make(使用makefile).其中CMAKE是目前事实上的标准构建系统,bazel也随着google项目的扩张越来越流行
    • VS和QT这样的IDE对自己的构建系统更加友好,往往可以按GUI的形式设置构建参数,CMAKE等则都需要开发者手动写构建文件.
  • 使用#ifndef 或者#pragma once

Read More

字符集与编码

近义名词

  • 多字节字符(Multibyte char) ~ 变长编码, 一个字符可能由多个字节(字节数不定)表示, 因此每个需要按一定规则添加额外的信息,以分割字符.
  • 宽字节字符(Wide char) ~ 定长编码, 使用定长的字节表示字符,因此字符之间没有额外的分割信息.

字符绘制基础

  • 假设有函数drawChar(charSet,code_point), drawChar将根据(charSet,code_point)两个参数在
Read More