02 2020 档案

摘要:“placement new” 通常是专指指定了位置的 new(std::size_t size, void mem),用于 vector 申请 capacity 剩余的可用内存。 但广义的 ”placement new” 指的是拥有额外参数的 operator new。 new 和 delete 阅读全文 »
posted @ 2020-02-29 12:06 刘-皇叔 阅读(136) 评论(0) 推荐(0) 编辑
摘要:new 和delete 必须遵循的惯例 operator new 需要无限循环地获取资源,如果没能获取则调用 ”new handler”,不存在 ”new handler” 时应该抛出异常。 operator new 应该处理 size == 0 的情况。 operator delete 应该兼容空 阅读全文 »
posted @ 2020-02-29 11:57 刘-皇叔 阅读(129) 评论(0) 推荐(0) 编辑
摘要:为什么需要自定义 operator new 或 operator delete 检测使用错误。new 得到的内存如果没有 delete 会导致内存泄露,而多次 delete 又会引发未定义行为。如果自定义 operator new 来保存动态内存的地址列表,在 delete 中判断内存是否完整,便可 阅读全文 »
posted @ 2020-02-29 11:36 刘-皇叔 阅读(181) 评论(0) 推荐(0) 编辑
摘要:new 申请内存失败时会抛出 异常,此前会调用一个由 std::set_new_handler() 指定的错误处理函数。 set_new_handler() “new handler” 函数通过 std::set_new_handler() 来设置,std::set_new_handler() 定义 阅读全文 »
posted @ 2020-02-27 20:39 刘-皇叔 阅读(96) 评论(1) 推荐(1) 编辑
摘要:模板元编程(Template Metaprogramming,TMP)就是利用模板来编写那些在编译时运行的C++程序。 模板元程序(Template Metaprogram)是由C++写成的,运行在编译器中的程序。当程序运行结束后,它的输出仍然会正常地编译。 C++并不是为模板元编程设计的,但自90 阅读全文 »
posted @ 2020-02-27 20:14 刘-皇叔 阅读(225) 评论(0) 推荐(0) 编辑
摘要:STL迭代器回顾 最简单的迭代器是输入迭代器(input iterator)和输出迭代器(output iterator), 它们只能向前移动,可以读取/写入它的当前位置,但只能读写一次。比如 ostream_iterator 就是一个输出迭代器。 比它们稍强的是前向迭代器(forward iter 阅读全文 »
posted @ 2020-02-27 20:02 刘-皇叔 阅读(108) 评论(0) 推荐(0) 编辑
摘要:如果所有参数都需要隐式类型转换,该函数应当声明为非成员函数。在类模板中,需要所有参数隐式转换的函数应当声明为友元并定义在类模板中。 模板化的 Rational 模板参数推导出错 看起来很完美但它是有问题的。比如我们有如下的调用: 为什么第二条会出错呢?因为编译器无法推导出合适的模板参数来实例化 。 阅读全文 »
posted @ 2020-02-27 19:32 刘-皇叔 阅读(110) 评论(0) 推荐(0) 编辑
摘要:提到智能指针可用来自动释放堆中的内存,STL中的迭代器也是一种智能指针,它甚至支持链表元素指针的 操作。 这些高级特性是普通指针所没有的。本文以智能指针为例,介绍成员函数模板的使用。 隐式类型转换 智能指针虽然比普通指针提供了更多有用的特性,但也存在一些问题,比如我们有一个类的层级: 普通指针可以做 阅读全文 »
posted @ 2020-02-27 12:53 刘-皇叔 阅读(151) 评论(0) 推荐(0) 编辑
摘要:模板是个好东西,你可以在实现类型安全的同时少写很多代码。但模板提供的是编译期的多态, 即使你的代码看起来非常简洁短小,生成的二进制文件也可能包含大量的冗余代码。 因为模板每次实例化都会生成一个完整的副本,所以其中与模板参数无关的部分会造成代码膨胀。 把模板中参数无关的代码重构到模板外便可以有效地控制 阅读全文 »
posted @ 2020-02-27 11:50 刘-皇叔 阅读(104) 评论(0) 推荐(0) 编辑
摘要:从面相对象 C++ 转移到模板 C++ 时,你会发现类继承在某些场合不在好使了。 比如父类模板中的名称对子类模板不是直接可见的,需要通过 this 前缀、using 或显式地特化模板父类来访问父类中的名称。 因为父类模板在实例化之前其中的名称是否存在确实是不确定的,而 C++ 偏向于早期发现问题(e 阅读全文 »
posted @ 2020-02-26 21:02 刘-皇叔 阅读(144) 评论(0) 推荐(0) 编辑
摘要:模板参数声明 上面 class 和 typename 的使用没有区别,然而 typename 和 class 对编译器而言却是不同的东西。 typename 可以用来帮编译器识别嵌套从属类型名称,基类列表和成员初始化列表除外。 声明一个类型 typename 的第一个作用在于声明一个类型。为什么类型 阅读全文 »
posted @ 2020-02-26 20:36 刘-皇叔 阅读(268) 评论(0) 推荐(0) 编辑
摘要:面向对象设计中的类考虑的是显式接口和运行时多态, 而模板编程中的模板考虑的是隐式接口和编译期多态。 对类而言,显式接口是由函数签名表征的,运行时多态由虚函数实现; 对模板而言,隐式接口是由表达式的合法性表征的,编译期多态由模板初始化和函数重载的解析实现。 显式接口和运行时多态 一个类的显式接口是由 阅读全文 »
posted @ 2020-02-26 20:14 刘-皇叔 阅读(141) 评论(0) 推荐(0) 编辑
摘要:多继承 多继承是 C++ 特有的概念,在是否应使用多继承的问题上始终争论不断。一派认为单继承是好的,所以多继承更好; 另一派认为多继承带来的麻烦更多,应该避免多继承。 多继承比单继承复杂,引入了歧义的问题,以及虚继承的必要性; 虚继承在大小、速度、初始化/赋值的复杂性上有不小的代价,当虚基类中没有数 阅读全文 »
posted @ 2020-02-26 19:48 刘-皇叔 阅读(118) 评论(0) 推荐(0) 编辑
摘要:private 继承 public 继承表示 "is a" 的关系,这是因为编译器会在需要的时候将子类对象隐式转换为父类对象。 然而 private 继承则不然: Person 可以 eat,但 Student 却不能 eat。这是 private 继承和 public 继承的不同之处: 编译器不会 阅读全文 »
posted @ 2020-02-26 19:24 刘-皇叔 阅读(147) 评论(0) 推荐(0) 编辑
摘要:一个类型包含另一个类型的对象时,我们这两个类型之间是组合关系。组合是比继承更加灵活的软件复用方法。对象组合也同样拥有它的语义: 就对象关系来讲,组合意味着一个对象拥有另一个对象,是 "has a" 的关系; 就实现方式来讲,组合意味着一个对象是通过另一个对象来实现的,是 "is implemente 阅读全文 »
posted @ 2020-02-26 17:45 刘-皇叔 阅读(113) 评论(0) 推荐(0) 编辑
摘要:静态绑定与动态绑定 静态绑定是在编译期决定的,又称早绑定;动态绑定是在运行时决定的,又称晚绑定。 举例来讲,Rect 和 Circle 都继承自 Shape,Shape 中有虚方法draw。那么: 在编译期是不知道应该调用哪个 draw 的,因为编译期看到的类型都是一样的: 。 在运行时可以通过虚函 阅读全文 »
posted @ 2020-02-26 17:30 刘-皇叔 阅读(267) 评论(0) 推荐(0) 编辑
摘要:不要重写继承来的非虚函数 Derived 继承自 Base。如果 Base有一个非虚函数 func,那么客户会倾向认为下面两种调用结果是一样的: 然而重写非虚函数 func 将会造成上述调用结果不一致: 因为 pb 类型是 ,pd 类型是 ,对于普通函数 func 的调用是静态绑定的(在编译期便决定 阅读全文 »
posted @ 2020-02-26 17:07 刘-皇叔 阅读(519) 评论(0) 推荐(0) 编辑
摘要:替代虚函数实现的几种方案 比如你在开发一个游戏,每个角色都有一个 healthValue() 方法。很显然你应该把它声明为虚函数,可以提供默认的实现,让子类去自定义它。 这个设计方式太显然了你都不会考虑其他的设计方法。但有时确实存在更好的,本节便来举几个替代的所涉及方法。 非虚接口范式(NVI id 阅读全文 »
posted @ 2020-02-26 16:58 刘-皇叔 阅读(433) 评论(0) 推荐(0) 编辑
摘要:继承时的函数接口传递 当你 public 继承一个类时,接口是一定会被继承的,你可以选择子类是否应当继承实现: 不继承实现,只继承方法接口:纯虚函数。 继承方法接口,以及默认的实现:虚函数。 继承方法接口,以及强制的实现:普通函数。 Rect 和 Ellipse 都继承自 Shape。 public 阅读全文 »
posted @ 2020-02-25 20:33 刘-皇叔 阅读(120) 评论(0) 推荐(0) 编辑
摘要:隐藏名称是作用域的问题。 在 C++ 中每一对 都会开启一个新的作用域,并嵌套在当前作用域中。 可以看到 double x 隐藏了 int x,因为C++的名称隐藏规则隐藏的是名称,和类型无关! 继承作用域 子类可以访问父类中的名称,是因为子类的作用域是嵌套在父类的作用域中的。 这一点也很符合直觉: 阅读全文 »
posted @ 2020-02-25 20:07 刘-皇叔 阅读(89) 评论(0) 推荐(0) 编辑
摘要:public 继承意味着 "is a" C++ 面向对象程序设计中,最重要的规则便是:public 继承应当是"is a"的关系。当 Derived public 继承自 Base时, 相当于你告诉编译器和所有看到你代码的人:Base 是 Derived 的抽象,Derived 就是一个 Base, 阅读全文 »
posted @ 2020-02-25 09:13 刘-皇叔 阅读(105) 评论(0) 推荐(0) 编辑
摘要:避免编译依赖 为了保证 Person 能够被编译,需要包含依赖的头文件: 这样就建立了定义 Person 的文件和这些头文件之间的编译依赖关系。如果这些头文件中的一些发生了变化,或者这些头文件所依赖的文件发生了变化,包含 Person 类的文件和使用了 Person 的文件一样必须重新编译,这样的层 阅读全文 »
posted @ 2020-02-14 10:22 刘-皇叔 阅读(193) 评论(0) 推荐(0) 编辑
摘要:inline 函数 inline 函数的优缺点 内联函数的好处太多了: 它没有宏的那些缺点,而且不需要付出函数调用的代价。 同时也方便了编译器基于上下文的优化。 但 inline 函数也并非免费的午餐: 在有限内存的机器上,过分热衷于 inline 化会使得程序对于可用空间来说过于庞大。即使使用了虚 阅读全文 »
posted @ 2020-02-12 21:37 刘-皇叔 阅读(219) 评论(0) 推荐(0) 编辑
摘要:异常安全 异常安全 是指当异常发生时: 不会泄漏资源, 也不会使系统处于不一致的状态。 通常有三个异常安全级别:基本保证、强烈保证、不抛异常保证: 基本保证 。抛出异常后,对象仍然处于合法(valid)的状态。但不确定处于哪个状态。 强烈保证 。如果抛出了异常,程序的状态没有发生任何改变。就像没调用 阅读全文 »
posted @ 2020-02-11 08:50 刘-皇叔 阅读(89) 评论(0) 推荐(0) 编辑
摘要:不要返回对象私有成员的句柄 句柄(handle)可以理解为持有其它对象的方法,引用,指针,和迭代器都是句柄。 不要返回对象私有成员的句柄。这样可以增加类的封装性、使得 const 函数更加 const, 也避免了空引用的创建。 直接返回私有成员的指针会导致私有成员被完全暴露。例如: class Po 阅读全文 »
posted @ 2020-02-09 21:55 刘-皇叔 阅读(151) 评论(0) 推荐(0) 编辑
摘要:C++的类型检查只在编译时执行,运行时没有类型错误的概念。 理论上讲只要你的代码可以编译那么就运行时就不会有不安全的操作发生。 但 C++ 允许类型转换,也正是类型转换破坏了理论上的类型系统。 C++ 中的类型转换 旧风格的强制类型转换 C 风格的类型转换: 函数风格的类型转换: 以上两种形式之间没 阅读全文 »
posted @ 2020-02-09 20:43 刘-皇叔 阅读(98) 评论(0) 推荐(0) 编辑
摘要:存在控制流转移的代码中,你可能会不经意间定义无用的变量。例如: 推迟构造函数的执行 当抛出异常时,encrypted 是无用的根本不需要构造它。所以更好的写法是推迟 encrypted 的构造: 推迟到有构造参数时 构造一个对象再给它赋值不如直接用一个值初始化它, 所以上述代码还有改进的余地:直接用 阅读全文 »
posted @ 2020-02-09 11:38 刘-皇叔 阅读(190) 评论(0) 推荐(0) 编辑
摘要:swap 函数最初由 STL 引入,已经成为异常安全编程的关键函数, 同时也是解决自赋值问题的通用机制。 std 中它的基本实现是很直观的: 可以看到,上述 swap 是通过赋值和拷贝构造实现的。所以 std::swap 并未提供异常安全, 但由于 swap 操作的重要性,我们应当为自定义的类实现异 阅读全文 »
posted @ 2020-02-08 21:23 刘-皇叔 阅读(158) 评论(0) 推荐(0) 编辑
摘要:当类型转换应该用于所有参数时,声明为非成员函数 最好不要提供隐式的类型转化, 但这条规则也存在特例,比如当我们需要创建数字类型的类时。正如 double 和int 能够自由地隐式转换一样, 我们的数字类型也希望能够做到这样方便的接口。 当然这一节讨论的问题不是是否应当提供隐式转换,而是如果运算符的所 阅读全文 »
posted @ 2020-02-08 20:47 刘-皇叔 阅读(129) 评论(0) 推荐(0) 编辑
摘要:在类的是实现中,常常会面临成员函数和非成员函数的选择。比如一个浏览器类: 此时你要实现一个 clearEverything() 有两种方式: 面向对象原则指出,数据和数据上的操作应当绑定在一起,那么前者更好。 这是对面向对象的误解,面向对象设计的精髓在于封装,数据应当被尽可能地封装。 相比于成员函数 阅读全文 »
posted @ 2020-02-08 18:10 刘-皇叔 阅读(142) 评论(0) 推荐(0) 编辑
摘要:数据成员声明为私有可以提供一致的接口语法,提供细粒度的访问控制,易于维护类的不变式,同时可以让作者的实现更加灵活。而且我们会看到,protected 并不比 public 更加利于封装。 语法一致性 你肯定也遇到过这种困惑,是否应该加括号呢: 如果我们把所有数据都声明为私有,那么在调用语法上,统一用 阅读全文 »
posted @ 2020-02-08 17:38 刘-皇叔 阅读(118) 评论(0) 推荐(0) 编辑
摘要:不要返回空的引用或指针 一个典型的场景如下: 注意 返回的是 Rational 实例, 时便会调用 , 返回值被拷贝后用来初始化 c。这个过程涉及到多个构造和析构过程: 函数调用结束前,返回值被拷贝,调用拷贝构造函数。 函数调用结束后,返回值被析构。 c 被初始化,调用拷贝构造函数。 c 被初始化后 阅读全文 »
posted @ 2020-02-08 17:23 刘-皇叔 阅读(112) 评论(0) 推荐(0) 编辑
摘要:传引用更加高效 缺省情况下,C++ 以传值方式将对象传入或传出函数。除非你特别指定其它方式,否则函数的参数就会以实际参数的拷贝进行初始化,而函数的调用者会收到函数返回值的一个拷贝。这个拷贝由对象的拷贝构造函数生成。这就使得传值成为一个代价不菲的操作。 例如,考虑下面这个类层级结构: 考虑以下代码,在 阅读全文 »
posted @ 2020-02-08 16:55 刘-皇叔 阅读(262) 评论(0) 推荐(0) 编辑
摘要:类设计时的思考 实际上每一个类都需要你面对下面这些问题: 新的类型的对象应该如何创建和销毁? 影响构造函数,析构函数的设计。 影响内存分配和回收函数(operator new,operator new[],operator delete,operator delete[])的设计。 对象的初始化和对 阅读全文 »
posted @ 2020-02-08 16:12 刘-皇叔 阅读(119) 评论(0) 推荐(0) 编辑
摘要:正确地构造一个 Date Date 对象的构造函数需要传入月、日、年。但客户在调用时常常传错顺序,这时可以将参数封装为对象来提供类型检查: 即使这样,用户的 Month 构造函数仍然会传入一个不合理的参数(例如 32),或者搞不清楚下标从0还是1开始。 解决方案是预定义所有可用的 Month: 可以 阅读全文 »
posted @ 2020-02-07 10:41 刘-皇叔 阅读(132) 评论(0) 推荐(0) 编辑

点击右上角即可分享
微信分享提示