- 博客(222)
- 资源 (4)
- 收藏
- 关注
原创 条款33:对auto&&类型的形参使用decltype,以std::forward之
泛形lambda式是C++14的特性之一:lambda可以在形参中使用auto:auto f = [](auto x) { return func(normalize(x));};上述lambda式对x实施的唯一动作就是转发给normalize。如果normalize 区别对待左值和右值,则可以说lambda式撰写是有问题的。正确的撰写方式是把x完美转发给normalize,要求在代码中修改两处:1:x改为万能引用,2:使用std::forwardauto f = [](auto &&
2022-01-03 15:12:48
690
原创 第18章 分页:介绍
18.1 一个简单例子为了记录地址空间的每个虚拟页放在物理内存中的位置,操作系统通常为每个进程保存一个数据结构,称为页表(page table)。页表的主要作用是为地址空间的每个虚拟页面保存地址转换(address translation),从而让我们知道每个页在物理内存中的位置为了转换(translate)该过程生成的虚拟地址,我们必须首先将它分成两个组件:虚拟页面号(virtual page number,VPN)和页内的偏移量(offset)18.3 列表中究竟有什么页表就是一种数据结构,用
2022-01-02 16:24:41
335
原创 条款32:使用初始化捕获对象將对象移入闭包
如果你想把一个只移对象(std::unique_ptr或std::future)放入闭包,C++11没有提供任何办法做到此事。而C++14为对象移动入闭包提供了支持。使用初始化捕获,则你会得到机会指定:由lambda生成的闭包类中的成员变量的名字一个表达式,用以初始化该成员变量考虑如下代码:class widget{public: bool isValidated() const; bool isProcessed() const; bool isArchived(
2022-01-02 15:04:11
120
原创 第 17 章 空闲空间管理
以本章需要解决的问题是:要满足变长的分配请求,应该如何管理空闲空间?什么策略可以让碎片最小化?不同方法的时间和空间开销如何?17.1 假设在堆上管理空闲空间的数据结构通常称为空闲列表(free list)。该结构包含了管理内存区域中所有空闲块的引用。17.3 基本策略最优匹配:首先遍历整个空闲列表,找到和请求大小一样或更大的空闲块,然后返回这组候选者中最小的一块。最差匹配:,它尝试找最大的空闲块,分割并满足用户需求后,将剩余的块(很大)加入空闲列表首次匹配:找到第一个足够大的块,将请求的
2021-12-26 22:30:35
484
原创 第16章分段
如果我们将整个地址空间放入物理内存,那么栈和堆之间的空间并没有被进程使用,却依然占用了实际的物理内存。16.1 分段:泛化的基址/界限分段的机制使得操作系统能够将不同的段放到不同的物理内存区域,从而避免了虚拟地址空间中的未使用部分占用物理内存16.2 我们引用哪个段硬件在地址转换时使用段寄存器。它如何知道段内的偏移量,以及地址引用了哪个段?那么在我们的例子中,如果前两位是 00,硬件就知道这是属于代码段的地址,因此使用代码段的基址和界限来重定位到正确的物理地址。如果前两位是 01,则是堆地址,对
2021-12-26 21:33:14
237
原创 条款31:避免默认捕获模式
C++11中有两种默认捕获方式:按值或者按引用。按值的默认捕获方式可能会忽悠你,按引用的默认捕获方式可能导致空悬引用。按引用捕获会导致闭包包含涉及到局部变量的引用,或者涉及到定义lambda式子的作用域内形参的引用,考虑如下代码:using FilterContainer = std::vector<std::function<bool(int)>>;FilterContainer filters;void addDivisorFilter() { auto calc1
2021-12-26 21:13:03
401
原创 第15章 机制:地址转换
地址转换(address translation)。它可以看成是受限直接执行这种一般方法的补充。利用地址转换,硬件对每次内存访问进行处理(即指令获取、数据读取或写入),将指令中的虚拟(virtual)地址转换为数据实际存储的物理(physical)地址15.3 动态(基于硬件)重定位每个 CPU 需要两个硬件寄存器:基址(base)寄存器和界限(bound)寄存器,有时称为限制(limit)寄存器将虚拟地址转换为物理地址,这正是所谓的地址转换(address translation)技术。转换示例
2021-12-25 19:33:04
725
原创 条款30:熟悉完美转发的失败情形
“转发”的含义不过是一个函数把自己的形参传递(转发)给另外一个函数而已。完美转发的含义是我们不仅转发对象,还转发其显著特征:类型,左值,右值,以及是否带有const或volation修饰词等。假设有某函数f,然后我们呢打算撰写一个函数將f作为转发目标,考虑如下代码:template<typename T>void fwd(T && param) { //接受任意实参 f(std::forward<T>(param)); //转发到f}给定目标函数f和
2021-12-25 18:50:42
617
原创 条款29:假定移动操作不存在、成本高、未使用
在标准C++11库中,所有容器都支持移动操作,但如果因此就断言所有的容器的移动都是成本低廉的,就错误了。考虑std::array这个C++11新容器类型,它实质上就是带有STL接的内建数组。其他标准容器都是將其内容放在堆上的,只需持有一个指涉到存放容器内容的堆内存指针。如如下vector而std::array缺少这样的指针,因为其内容数据是直接存储在对象内的,如下:注意:aw1中的元素是被移动入aw2的。无论是移动还是复制std::array类型的对象都还是需要线性时间的计算复杂度,因为容器中的每
2021-12-23 21:19:27
1016
原创 第14章 插叙 :内存操作 API
14.1 内存类型第一种称为栈内存,它的申请和释放操作是编译器来隐式管理的第二种类型的内存,即所谓的堆(heap)内存,其中所有的申请和释放操作都由程序员显式地完成14.4 常见错误忘记分配内存char *src = "hello";char *dst; // oops! unallocatedstrcpy(dst, src); // segfault and die没有分配足够的内存char *src = "hello";char *dst = (char *) mal
2021-12-22 20:43:22
302
原创 条款28:理解引用折叠
考虑如下代码:template<typename T>void func(T &&parem);Widget widgetFactory(); // 返回右值的函数Widget w; // 变量(左值)func(w); // 调用func并且传入左值,T的推导类型为Widget&func(widgetFactor()); // 调用func并且传入右值,T的推导类型为Widget传递实参类型都为Widget,一个左值,一个右值,导致了针对模
2021-12-21 22:43:20
276
原创 第13章 抽象:地址空间
13.1 早期系统操作系统曾经是一组函数(实际上是一个库),在内存中(在本例中,从物理地址 0 开始),然后有一个正在运行的程序(进程),目前在物理内存中(在本例中,从物理地址 64KB 开始),并使用剩余的内存。13.2 多道程序和时分共享多道程序(multiprogramming)系统时代开启[DV66],其中多个进程在给定时间准备运行,比如当有一个进程在等待 I/O 操作的时候,操作系统会切换这些进程,这样增加了 CPU 的有效利用率(utilization)。实现时分共享的方法,是让
2021-12-20 22:51:19
326
原创 条款27:熟悉万能引用类型进行重载的替代方案
标签分派一个万能引用形参通常会导致的后果是无论传入了什么都给出一个精确匹配结果,不过,如果万能引用仅是形参列表的一部分,该列表中还有其他非万能引用类型的形参的话,那么只要该非万能引用形参具备充分差的匹配能力,则它足以將这个带有万能引用形参的重载版本剔除出局。举例条款26的代码:std::multiset<std::string> names; // 全局数据结构void logAndAdd(const std::string &name) { auto now = std::c
2021-12-20 21:47:07
352
原创 条款26:避免万能引用类型进行重载
考虑如下代码:std::multiset<std::string> names; // 全局数据结构void logAndAdd(const std::string &name) { auto now = std::chrono::system_clock::now(); log(now, "logAndAdd"); names.emplace(name); // 將名字添加到全局数据结构中}std::string petName("Darla");logAnd
2021-12-18 16:54:26
576
原创 条款25:针对右值引用实施std::move,针对万能引用实施std::foraward
右值引用仅会绑定到那些可移动的对象上(临时对象)class Widget {public: Widget(Widget && rhs) : name(rhs.name), p(std::move(rhs.p)) {}; // 移动构造函数private: string name; shared_ptr<int> p;};万能引用只有在使用右值初始化时候才会强制转换成右值类型,正好是std::forward作用class Widget {pu
2021-12-13 22:10:29
368
原创 条款24:区分万能引用和右值引用
考虑如下代码:void f(Widget &&parm); // 右值引用Widget &&var1 = Widget(); // 右值引用auto &&var2 = var1; // 非右值引用template<typename T>void f(std::vector<T> &¶m); // 非右值引用template<typename T>void
2021-12-09 23:44:42
708
原创 条款23:理解std::move和std::forward
std::move无条件地将实参强制转化到右值,而std::forward则仅在某个特定条件满足时才执行同一个强制转换右值是可以移动的,所以在一个对象上实施了std::move,就是告诉编译器该对象具备可移动的条件。考虑如下类:class Annotation { explicit Annotation(std::string text); //待复制的形参,按值传递};Annotation的构造函数只需要读取text值,不需要修改,所以遵循“只要有可能就使用const就使用”的原则,修改代
2021-12-07 21:25:18
616
原创 条款22:使用Pimpl习惯用法时,将特殊成员函数的定义放到实现文件中
Pimpl手法:把某类的数据成员用一个指涉到某实现类/结构提的指针代替,然后把原来的主类中的数据成员放到实现类中,并通过指针间接访问这些数据成员;class Widget{public: Widget();private: std::string name; std::vector<double> data; Gadget g1, g2, g3; // Gadget是某种用户自定义类型};如果头文件gadget.h的内容发生了改变,则Widget的客户必
2021-11-27 16:17:53
622
原创 条款21:优先选用std::make_unique和std::make_shared,而非直接new
std::make_shared是C++11一部分,std::make_unique为C++14才引入,std::allocate_shared它的行为和std::make_shared一样,只不过它的第一个实参是个用以动态分配内存的分配器对象;是否对make系列函数来创建一个智能指针坐最平凡的对比,也能揭示优先选用make系列函数的第一个原因,考虑如下代码:auto upw1(std::make_unique<Widget>()); // 使用make系列函数std::unique_p
2021-11-23 22:20:46
407
原创 条款20:对于类似std::shared_ptr但有可能空悬的指针使用std::weak+ptr
std::weak_ptr并不是一种独立的智能指针,而是std::shared_ptr的一种扩充std::weak_ptr一般是通过std::shared_ptr来创建的。当使用std::shared_ptr完成初始化std::weak_ptr的时刻,两者就治涉到了相同的位置,但是std::weak_ptr并不影响所指涉到的对象的引用计数:auto spw = std::make_shared<Widget>(); // spw构造完成后,指涉到Widget的引用计数置为1std::we
2021-11-20 14:32:02
759
原创 条款19:使用`std::shared_ptr`管理具备共享所有权的资源
std::shared_ptr可以用过访问资源的引用计数来确定是否自己是最后一个指涉到该资源的。std::shared_ptr的构造函数会使改计数递增,而析构函数会使该计数递减,而赋值运算符同时执行两种操作。
2021-11-14 14:52:43
764
原创 条款18:使用std::unique_ptr管理具备专属所有权的资源
一个非空的std::unique_ptr总是拥有其所涉到的资源;std::unique_ptr不允许复制,因为如果复制了一个std::unique_ptr,就会得到两个指涉到同一资源的std::unique_ptr;假设我们有一个以Investment为基类的投资类型的继承体系:class Investment {...};class Stock: public Iverstment {...};class Bond: public Iverstment {...};class RealEstat
2021-10-24 13:19:55
166
原创 条款17:理解特种成员函数的生成机制
C++11当你声明一个类什么也不做时会默认生成如下函数:默认构造函数析构函数复制构造函数复制赋值运算符移动构造函数移动赋值运算符class Widget {public: Widget(Widget&& rhs); // 移动构造函数 Widget& operator=(Widget&& rhs);// 移动赋值运算符};移动构造函数将依照其形参rhs的各个非静态成员对于本类的对应成员执行移动构造移动复制运算符则将依照其形参rhs的
2021-10-12 22:27:18
129
原创 条款16:保证const成员函数的线程安全性
有一个类来表示多项式会非常方便,而在这个类中,其中有一个函数能够计算多项式的根,即那些使得多项式求值结果为零的数值;在计算多项式的根时,我们就把这些根缓存起来,并以返回缓存数值的手法实现rootsclass Polynomial {public: using RootsType = std::vector<double>; // 持有值的数据结构,这些数值使得多项式求值结果为零 RootsType roots() const { if (!rootsAreVa
2021-10-08 22:50:25
298
原创 条款15:只要有可能使用constexpr,就使用他
constexpr当它应用与对象时,其实就是一个加强版const;但是应用于函数时,却有着相当不同的意义;constexpr 对象:constexpr对象具备const属性,在编译阶段已知int sz; // 非constexpr变量constexpr auto arryaSize1 = sz; // 错误!sz的数值在编译器未知std::array<int, sz> data1; // 错误!sz的数值在编译器未知constexpr auto arraySize2 =
2021-10-03 16:04:28
139
原创 条款13:优先使用const_iterator,而非iterator
考虑如下代码:std::vector<int> values;std::vector<int>::iterator it = std::find(values.begin(), values.end(), 1983);values.insert(it, 1998);如果vector中并没有1983,那么插入位置将是末尾处。但在这里使用iterator并非正确选择...
2021-09-17 22:52:17
261
原创 条款12:为意在改写的函数添加override声明
正是虚函数改写,使得通过基类接口调用派生类函数成为可能:class Base {public: virtual void doWork(); // 基类中的虚函数 ];class Derived : public Base {public: virtual void doWork(); // 改写了Base::doWork};std::unique_ptr<Base> upb = std::makr_unique<Derived>(); // 创建基类指针
2021-08-28 15:52:13
139
原创 条款11:优先选用删除函数,而非private为定义函数
如果你写了代码给其他开发人员使用,并且你想阻止他们调用某个特定函数的话,那你只需要不要声明该函数即可。函数未经声明,不可调用。但是有时C++会替你声明函数;C++98中为了阻止这些函数被使用采取的做法是声明其为private,并且不去定义它们;举例来说,在接近C++标准的输入输出流库继承谱系基类部位的,是类模板basic_ios,对输入输出流进行复制是不可取的;在C++98中的basic_ios是像下面这样规定的:template<class charT, char tarits = char_t
2021-08-25 22:04:18
254
1
原创 条款10:优先选用限定作用域的枚举类型,而非不限定作用域的枚举类型
如果在一个大括号里面声明一个名字,则该名字的可见性就被限定在括号扩起来的作用域内。但是这个规则不适用于C++98风格的枚举类型中定义的枚举量;这些枚举量的名字属于包含着这个枚举类型的作用域,意味这在此作用域内不能有其他实体取相同的名字;enum Color {black, white, red}; // black,white,red所在作用域和Color相同auto white = false; // 错误!white已经在范围内被申明过这些枚举常量的名字泄漏到枚举类型所在作用域的这一事
2021-08-24 22:11:01
245
原创 条款8:优先选用nullptr,而非0或NULL
字面常量0的类型是int而非指针,当C++在只能使用指针的语境中发现一个0,它也会把它勉强解释为空指针。C++的基本观点还是0的类型为int,而非指针;从实际效果来说,以上结论对于NULL也成立(标准允许各个实现给予NULL非int的整数类型(如long))。C++98中,这样的基本观点可能在指针和整形之间i进行重载时可能会发生意外。void f(int);void f(bool);void f(void *);f(0); // 调用f(in)f(NULL);// 可能会通不过编译,但一般会调
2021-07-31 23:38:46
182
原创 条款7:在创建对象时注意区分`()`与`{}`
指定初始化值的方式包括使用小括号、使用等号、或使用大括号:int x(0); //初始化物在小括号内int y = 0; //初始化物在等号之后int x{0}; //初始化物在大括号之内对于int这样的内置类型来说,初始化和赋值无区别,但是对于用户自定义的类型,初始化和赋值就有区别了:Widget w1...
2021-05-02 14:15:48
488
1
原创 宏的编写技巧
宏的编写技巧当C语言的宏定义包含多余一条语句时,就需要do { /*...*/} while(0)包裹成单个语句,否则会有如下问题:#define M() a(); b()if (cond) M();else c();/*预处理后*/if (cond) a(); b(); /* b();在if语句之外*/else /* 报错误:else without a previous ‘if’*/ c(); 只用{}也不行#define M() { ..
2021-04-17 21:03:36
518
原创 使用Uncrustify格式化你的代码
使用Uncrustify格式化你的代码介绍Uncrustify为一个支持C, C++, C#, Objective-C, D, Java, Pawn、VALA的代码格式化工具项目地址:https://github.com/uncrustify/uncrustify下载地址:https://github.com/uncrustify/uncrustify/tagsUncrustify自定义风格可以使用基于web的uncrustify_config配置工具进行生成:https://cdanu.git
2021-04-09 21:31:20
1624
原创 条款6:当auto推导的类型不符合要求时,使用带显示类型的初始化物习惯手法
有时候,你想往东,auto类型推导的结果偏偏往西。考虑如下代码:std::vector<bool> features(cosnt Widget& w);Widget w;bool highPriority = features(w)[5]; processWidget(w, hightPrioirty);上述代码没有什么问题,如果我们吧highPriority从显示类型改为auto呢?auto highPriority = featrues(w)[5];所有代码仍然可编
2021-03-14 14:45:50
286
原创 条款5:优先选用auto,而非显示类型声明
考虑如下代码:int x1; // 有潜在的未初始化风险auto x2; // 编译错误!必须有初始化物auto x3 = 0; // 没问题std::vector<int> v;unsigned sz = v.size();v.size()的返回值类型为std::vector<int>::size_type,在32位Windows上,unsigned和std::vector<int>::size_type的大小是一样的,但是在64位Window
2021-03-07 13:04:35
146
原创 条款4:掌握查看类型推导结果的方法
IDE编辑器IDE编辑器中的代码通常会在你将鼠标悬停至某个程序实体,如变量,形参,函数等时,显示出该实体的类型; const int theAnswer = 12; auto x = theAnwer; auto y = &theAnswer;IDE编辑器很可能显示出,x的类型推导为int,而y的推导类型为const int *编译器诊断信息:想要编译器显示推导出的类型,一条有效途径就是使用改类型导致的某些编译错误: template<typename T> //之声明
2021-02-28 13:40:59
193
原创 条款3:理解decltype
对于给定的名字或表达式,decltype能告诉你改名字或表达式的类型;考虑如下代码:const int i = 0; //decltype(i)是const intbool f(const Widget &w); // decltype(w)是const Widget & // decltype(f)是bool f(const Widget&)struct Point { // decltype(Point::x)是int int x,y; };
2021-02-27 20:04:15
184
《Flash.ActionScript3.0从入门到精通》笔记 (1)
2018-06-11
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人