C++系列

C++系列

SomeCpp

零碎知识点 * fflush仅仅是为了输出而设计的, 标准中并没有说明它对输入缓冲的效果. * 一元运算符和=是右结合的,这和<<是完全不同的,a=b=c意味着a=(b=c),b=c将先执行,而a<<b<<c则是(a<<b)<<c,a<<b将先执行. * C++11后引入了thread_local型的生命周期,这种对象和线程的生命周期是一致的 * C++11中很多类型都支持列表初始化,a = {value1,value2...} * 列表初始化会额外检测是否可能存在信息丢失,如果可能存在丢失,则无法通过编译期检查. * 对于自定义类,支持列表初始化意味着存在这样的ClassName(initializer_list&

By Edimetia3D

C++系列

Some Multi Thread

C++标准库提供的 mutex 在大部分场合都足以保证线程安全, 但是当问题变得更加极端时,就可能需要使用lockfree风格的并行编程了. 而为了正确实现lockfree, 你将打开一扇新的大门, 接下来的名词都是在学习过程中必须正确理解的: memory model, reordering, weak(relax), strong(strict), fence, barrier, release, acquire, seq_cst, consume, mutex, futex, lockfree(lockless), synchronize-with, happens-before, ABA, DCLP, 背景 在单核时代,CPU设计中引入了很多特性,其中很重要的一点就是乱序执行,乱序执行的基本原则是:乱序执行的最终效果应当和顺序执行一致,这个原则在单线程下是比较容易保证的.例如,对于下面的代码,先写入a还是先写入b都不影响后序的指令执行的正确性,所以编译器及硬件都可以自行选择先执行哪一个. a = 8; b = 9; 当进入并行环境之后,乱序执行的基本原

By Edimetia3D

C++系列

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

C++11为多线程开发准备了一套标准的基础设施,主要为<thread>,<mutex>,<condition_variable>, 这套组件基本是"pthread"的标准化.本文主要介绍C++11多线程开发相关的基础内容.如果需要更多的细节,可以直接google或者查手册进行了解. 可调用对象 可调用对象在C++ 11中是非常重要的概念, 它使得"函数"变得更加像对象. 线程库完整的支持可调用对象. * 支持()运算符的对象都是可调用对象,这些工具的设计比较独立,可以在使用时再查reference,常见的有 * bind创建的对象 * lambda * 重载了()的类 * 函数/函数指针 * function<T>. std::thread * 标准库提供了std::thread来进行基础的线程管理,

By Edimetia3D

C++系列

C++ OOP编程综述

嵌套类和局部类 * 定义在类X内的类Y称为嵌套类,它就是一个普通类,只不过使用的作用域被限制了. * 定义在函数内的类Y称为局部类,这种类限制很多,主要用于在语言层面支持Lambda,实践中基本不用. struct(class)的内存布局 * C++的struct和class实例大小不会为0,即便它内部没有任何数据变量.主要是因为编译器总是需要为实例分配内存,使得对象能获得有效的地址,且不同对象的地址总应该是不同的. * 类型布局相关的名词有很多,如POD,Aggragate,naive等,具体我也不是很清楚,总之很麻烦. * 如果需要把sturct或class对象通过C风格直接从内存层面管理(如memcpy),那么要求它的内存布局是平凡的. * 只要类型涉及到virtual, 那类型实例的首地址存储的一定是vptr, 涉及virtual只需满足任意一个条件: * 直接或间接使用虚继承 * 直接或间接使用虚函数 一般规则 C++对象的内存布局对于理解多继承/虚继承非常重要, 基本要关注的有三点 1

By Edimetia3D

C++系列

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

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

By Edimetia3D

C++系列

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

重载运算符 规定 * 赋值,取下标,调用,箭头,类型转换,这些运算符只能作为成员函数. * 用户重载的&&与||不会进行短路求值. 习惯 * 重载主要分为全局重载和类内重载,一般而言,二元运算都应该在全局重载(顺序问题). * 改变对象状态的运算符一般应当设为成员函数,拥有对称性的运算符一般应在类外重载. * 从技术层面说,重载运算符的参数可以是指针类型的,但是这一般与我们使用语言的习惯不符,所以形参类型最好只是T或T&,而不要是指针. * 定义有关联的重载运算符时一定要考虑兼容性,例如a<b为假,a>b也为假,那么a==b就应该返回真. * 重载中应当尽可能的保持语义与内置风格一致. * 例如,如果重载了++运算符,那么应当保证++x和x+=1及x=x+1有同样的效果. * 尽量不要重载全局的operator new,仅在类内重载它. * 类内重载的new和delete都是static的,因此,无法使用this相关的量

By Edimetia3D

C++系列

[Cpp基础] [08] 动态内存与智能指针

动态内存与智能指针 new+delete * new delete是C++中的两个主要的表达式. * new相当于C的malloc() + T(),delete相当于C的free() + ~T(). * operator new()以及operator delete()仅能用于覆盖内存分配操作,构造和析构的调用是无法被替换的. * delete能否正常执行依赖于指向的对象能否delete,而不依赖于指针自身(指针类型不含任何额外信息). * new T[n]中,n可以为0,返回的将是一个不能解引用的指针值,该指针值保证非nullptr,可以做正常的比较. * new和delete表达式总是会触发构造和析构,必要时可以使用malloc+placement new方案进行更精细的控制. * placement new仅仅用于在特定地址上触发构造函数, 后续需要手动调用析构函数来销毁对象. shared_ptr与unique_ptr * 智能指针都不是为数组型数据设计的,所以都没有重载[],+,-等运算符.

By Edimetia3D

C++系列

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

数组类型和函数类型 尽管在大部分场合中,数组类型和函数类型都被转换成某种指针使用,然而它们确实是某种类型. 这一点在类型推导中很重要,因为类型推导可以推导出数组类型/函数类型 * 数组类型逻辑上类似于std::array,由T和N两个属性决定唯一的数组类型,称T[N]型对象为数组类型,其中N必须为constexpr,称T array[N]为T[N]的一个实例. * T和N必须都相同才是同一个array类型. * 数组类型可以被隐式decay为指向数组首元素的指针. * 转换为指针后,长度信息就丢失了. * 在T[N]做函数形参时,编译器一定会将形参转换为指针,从而可以接受长度不同的数组,长度信息也随之丢失. * 数组的长度信息未丢失时,编译器及用户是可以获取到这些长度信息的.sizeof(array)/sizeof(array[0])就是元素数量. * 可以使用引用型形参来保留数组的长度信息,对于模板,还可以使用自动推断. * 自动推断:template<class T>fun(T &

By Edimetia3D

C++系列

[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 T * p_c_obj=&c_obj; const T * const cp_c_obj=p_c_obj; T&

By Edimetia3D

C++系列

[Cpp基础] [05] const与constexpr

Const Expression, constexpr 是一个非常复杂的话题, 幸运的是, 我们在实践中 需要记住的内容并不太多,因为常见的应用场景其实比较简单. const限定符 * const是一种编译期特性,用于限定对象的编译期写入权限, 编译器也可能将const对象视作constexpr进行优化. * 理论上可以通过各种trick在运行期写入对象,但是不要这么做. * 当项目很大,或者开发人员很多时,使用const就能利用编译器约束开发人员的行为,给开发人员做一定提醒. * 原则上,当我们创建一个变量时,若认为它不应当被修改,就应当声明称const型.最常用的场合是函数形参/返回值中的const T &. * const限定的全局对象默认是static的,不会跨文件共享,正因此,若const对象可能被共享,也应当优先定义在头文件中. * const在与指针联用时,要注意限定的部分.规定:若限定指针自身为不可写,则将const写在*右侧;若是指向的对象不可写,则const写在*左侧. * 若const用于限定对象自身不可写

By Edimetia3D

C++系列

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

名字查找 * 当我们使用一个名字时,编译器就会向上查找名字的声明语句.变量名的查找和函数名的查找结果有一定区别. * 变量名查找:最终仅会确定唯一的对象,从内到外碰到的第一个名字将被使用. * 函数名查找:会先确定搜索域,然后按重载规则选择最有匹配. 搜索域包含一般scope和参数scope. * 常规搜索域确定:从当前作用域开始,逐层向上查找名字声明,名字首次出现的作用域就是常规搜索域 * 对于函数调用的每个实参arg_i,其类型decltype(arg_i)及其所有基类所在的作用域都加入实参类型搜索域 * 函数名和变量名都是名字,彼此可以相互hide. 重载匹配规则 * 函数重载的基础是传入参数的类型,和返回类型无关.对于非引用的形参,const不作为重载的依据. * 首先根据实参类型排除不能产生调用的函数,得到一个可行函数列表. 在可行列表中: 类型转换越少越好,在此前提下, 特化的优先于一般的. * 注意: * 仅函数/数组向指针的转换是精确匹配,只要类型不同,都被视作类型转换,例如:派生类指针到基类指针,非

By Edimetia3D

C++系列

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

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

By Edimetia3D