分类目录归档:C++系列

SomeCpp

零碎知识点

  • fflush仅仅是为了输出而设计的, 标准中并没有说明它对输入缓冲的效果.
  • 一元运算符和=是右结合的,这和<<是完全不同的,a=b=c意味着a=(b=c),b=c将先执行
  • C++11后引入了thread_local型的生命周期,这种对象和线程的生命周期是一致的
  • C++11中很多类型都支持列表初始化
Read More

[Cpp基础][09]C++11多线程编程基础

C++11为多线程开发准备了一套标准的基础设施,主要为<thread>,<mutex>,<condition_variable>, 这套组件基本是”pthread”的标准化.本文主要介绍C++11多线程开发相关的基础内容.如果需要更多的细节,可以直接google或者查手册进行了解.

可调用对象

可调用对象在C++ 11中是非常重要的概念, 它使得”函数”变得更加像对象. 线程库完整的支持可调用对象.

  • 支持()运算符的对象都是可调用对象,这些工具的设计比较独立,可以在使用时再查reference,常见的有
    • bind创建的对象
    • lambda
    • 重载了()的类
    • 函数/函数指针
Read More

C++模板编程简述

模板作为一个C++中的一个高级特性,广泛应用于各种场合,以尽可能弥补自身作为编译语言的动态能力.然而,除非必要,模板应当尽可能的简单,避免本末倒置.

模板使用基础

  • 模板特性是编译器和链接器相互配合实现的.这可以辅助我们理解它的特性.
  • 编译期实例化时
    • 模板类型参数通常得是完全类型.
    • 模板非类型参数必须是constexpr
  • 在模板编程中,编译器对T::foo型语法分析可能会有歧义,例如,T::vl * p这样的语句可能是使用T的静态成员做乘法,也可能是创建一个T::vl *型的指针.
    • 默认情况下,编译器认为给出的所有名字都是变量/函数,而非类型.
    • 此时需要
Read More

C++ OOP编程综述

嵌套类和局部类

  • 定义在类X内的类Y称为嵌套类,它就是一个普通类,只不过使用的作用域被限制了.
  • 定义在函数内的类Y称为局部类,这种类限制很多,主要用于在语言层面支持Lambda,实践中基本不用.

struct和class的内存布局

  • C++的struct和class实例大小不会为0,即便它内部没有任何成员函数或成员变量.主要是因为编译器总是需要为实例分配内存,不占内存的实例是不存在的
  • 数据成员全为全public,无构造函数,无类内初始值,不使用继承及virtual的类,称为聚合类.聚合类可以通过{}进行逐成员初始化
  • 如果需要把sturct或class对象通过C风格直接从内存层面管理(如memcpy),那么就必须要求它的内存布局是平凡的(标准的),这写类型称为POD. 标准库中,is_pod::value可以判断其是否是可以直接操作的
  • 如果类内存在多个publicprivate声明,则编译器可能重排该类的内存布局(移动部分对象),这将导致成员排布和程序员的预期产生差异.
  • 对齐方法一般是由编译选项或编译指示决定的.

OOP基础特性:

  • 如果没有特别说明,就永远只使用public继承
Read More

C++特性”异常”

异常

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

重载运算符

重载运算符

规定

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

习惯

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

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

数组类型和函数类型

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

  • T[N]型对象为数组类型,其中N必须为constexpr,称T array[N]中的arrayT[N]型的一个实例.
    • N是数组的一个属性,当N不同时,是不同类型的数组.
  • 数组类型变量可以被隐式转换为指向数组首元素的指针.
Read More

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

强制类型转换

  • C++风格的类型转换为四个xxx_cast<TYPE>(var),每个都有特定的应用场合.
  • 转换总是通过创建临时量实现的,相当于TYPE tmp(var). 转换语句整体为tmp
  • 规定:仅有xxx_cast<TYPE &>(var)得到的临时量可以作为左值,其他情况都是右值。

const_cast

  • const_cast是只用于处理const
Read More

[Cpp基础] [05] const与constexpr

const限定符

  • const是一种编译期特性,用于限定对象的编译期写入权限
    • 换言之,其实可以通过各种trick在运行期写入对象.
    • 当项目很大,或者开发人员很多时,使用const就能利用编译器约束开发人员的行为,给开发人员做一定提醒.
    • const值往往还可以供编译器进行优化.
  • 原则上,当我们创建一个变量时,若认为它不应当被修改,就应当声明称const型.最常用的场合是函数形参/返回值中的const T
Read More