efficient c++
文章平均质量分 83
swbb
这个作者很懒,什么都没留下…
展开
-
近期问题汇总
栈和堆的区别、优缺点inline的优缺点构造函数和析构函数是否可以调用虚函数构造函数是否可以定义为虚函数在c++中怎么实现某个方法只调用一次?原创 2024-05-17 17:12:05 · 710 阅读 · 0 评论 -
学完Efficient c++ (53-55)
Boost 是若干个程序库的集合,并且当中的许多库已经被 C++ 吸纳为标准库的一部分。不过在现在的 Modern C++ 时代,是否该在项目中使用 Boost 仍然有一定的争议,一些 Boost 组件并无法做到像 C++ 标准库那样高性能,零开销抽象,但毫无疑问的是,Boost 的参考价值是无法忽视的,你可以在 Boost 中找到许多非常值得学习和借鉴的实现。如今 TR1 草案已完全融入 C++ 标准当中,没有再过多了解 TR1 标准库的必要。原创 2024-03-21 11:48:56 · 139 阅读 · 0 评论 -
学完Efficient c++ (50-52)
又例如,编译器所带的内存管理器是线程安全的,但如果你的程序是单线程的,你也可以考虑写一个不线程安全的分配器来提高速度。如果你知道特定的某个数据结构往往被一起使用,而你又希望在处理这些数据时将“内存页错误(page faults)”的频率降至最低,那么可以考虑为此数据结构创建一个堆,将它们成簇集中在尽可能少的内存页上。定制 new 和 delete 动态内存的相关信息:分配区块的大小分布,寿命分布,FIFO(先进先出)、LIFO(后进先出)或随机次序的倾向性,不同的分配/归还形态,使用的最大动态分配量等等。原创 2024-03-20 17:58:10 · 1043 阅读 · 0 评论 -
学完Efficient c++ (48-49)
当operator new无法满足某一内存分配需求时,会不断调用一个客户指定的错误处理函数,即所谓的new-handler,直到找到足够内存为止,调用声明于中的set_new_handler可以指定这个函数。由于模板元程序执行于 C++ 编译期,因此可以将一些工作从运行期转移至编译期,这可以帮助我们在编译期时发现一些原本要在运行期时才能察觉的错误,以及得到较小的可执行文件、较短的运行期、较少的内存需求。当然,副作用就是会使编译时间变长。原创 2024-03-19 11:41:09 · 468 阅读 · 0 评论 -
学完Efficient c++ (46-47)
traits classes 可以使我们在编译期就能获取某些类型信息,它被广泛运用于 C++ 标准库中。traits 并不是 C++ 关键字或一个预先定义好的构件:它们是一种技术,也是 C++ 程序员所共同遵守的协议,并要求对用户自定义类型和内置类型表现得一样好。以迭代器为例,标准库中拥有多种不同的迭代器种类,它们各自拥有不同的功用和限制:input_iterator:单向输入迭代器,只能向前移动,一次一步,客户只可读取它所指的东西,而且只能读取一次。(istream_iterators)原创 2024-03-18 10:57:31 · 1102 阅读 · 0 评论 -
学完Efficient c++ (44-45)
因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或类成员变量替换模板参数。因类型模板参数而造成的代码膨胀,往往可以降低,做法是让带有完全相同二进制表述的具现类型共享实现代码。C++ 视模板类的不同具现体为完全不同的的类型(如果用带有base-derived关系的B、D分别具现化同一个template,产生出来的两个具现体并不带有base-derived关系),但在泛型编程中,我们可能需要一个模板类的不同具现体能够相互类型转换。原创 2024-03-15 10:16:50 · 971 阅读 · 0 评论 -
学完Efficient c++ (41-42)
类与模板都支持接口和多态。而对模板参数而言,接口是隐式的,奠基于有效表达式,多态则是通过模板具现化和函数重载解析(function overloading resolution)发生于编译期。加诸于模板参数身上的隐式接口,就像加诸于类对象身上的显式接口“一样真实”,两者都在编译期完成检查,你无法在模板中使用“不支持模板所要求之隐式接口”的对象(代码无法通过编译)。第三种做法是最令人不满意的,如果被调用的是虚函数,上述的明确资格修饰(explicit qualification)会使“虚函数绑定行为”失效。原创 2024-03-14 21:31:27 · 626 阅读 · 0 评论 -
学完Efficient c++ (39-40)
当派生类需要访问基类的protected的成员时或需要重新定义继承而来的virtual函数时,才被迫使用private继承,否则一般使用复合或public继承。由于虚继承会在派生类中额外存储信息来确认成员来自于哪个基类,虚继承通常会付出更多空间和速度的代价,并且由于虚基类的初始化责任是由继承体系中最底层的派生类负责,就导致了派生类必须认知其虚基类并且承担虚基类的初始化责任。因此我们应当遵循以下两个建议:非必要不使用虚继承。如果必须使用虚继承,尽可能避免在虚基类中放置数据。原创 2024-03-13 10:39:23 · 999 阅读 · 1 评论 -
学完Efficient c++ (36-38)
非虚函数和虚函数具有本质上的不同:非虚函数执行的是静态绑定(statically bound,又称前期绑定,early binding),由对象类型本身(称之静态类型)决定要调用的函数;而虚函数执行的是动态绑定(dynamically bound,又称后期绑定,late binding),决定因素不在对象本身,而在于“指向该对象之指针”当初的声明类型(称之动态类型)。原创 2024-03-12 10:26:18 · 523 阅读 · 0 评论 -
学完Efficient c++ (34-35)
我们在条款32讨论了public继承的实际意义,我们在本条款将明确在public继承体系中,不同类型的接口——纯虚函数、虚函数和非虚函数——背后隐藏的设计逻辑。首先需要明确的是,成员函数的接口总是会被继承,而public继承保证了,如果某个函数可施加在父类上,那么他一定能够被施加在子类上。不同类型的函数代表了父类对子类实现过程中不同的期望。将纯虚函数、虚函数区分开的并不是在父类有没有实现——纯虚函数也可以有实现,其二者本质区别在于父类对子类的要求不同,前者在于从编译层面提醒子类主动实现接口,后者则侧重于给予原创 2024-03-11 11:06:34 · 939 阅读 · 0 评论 -
学完Efficient c++ (32-33)
同样的道理,面对矩形和正方形,生活经验告诉我们正方形是特殊的矩形,但这并不意味着在代码中二者可以存在public的继承关系,矩形具有长和宽两个变量,但正方形无法拥有这两个变量——没有语法层面可以保证二者永远相等,那就不要用public继承。之前我们了解过 C++ 名称查找法则,这在继承体系中也是类似的,当我们在派生类中使用到一个名字时,编译器会优先查找派生类覆盖的作用域,如果没找到,再去查找基类的作用域,最后再查找全局作用域。,如果不是,则无论生活经验是什么,都不能视作”is-a”的关系。原创 2024-03-10 18:43:36 · 259 阅读 · 0 评论 -
学完Efficient c++ (28-31)
避免返回 handles(包括引用、指针、迭代器)指向对象内部。遵循这个条款可增加封装性,使得const成员函数的行为符合常量性,并将发生 “空悬句柄” 的可能性降到最低。当异常被抛出时,带有异常安全性的函数会:不泄漏任何资源。不允许数据败坏。nline函数无法随着程序库的升级而升级,如果func函数是程序库的一个inline函数,客户将func函数编进其程序中,一旦程序设计者改变func函数,所有的func的客户端程序必须重新编译。而如果func函数是一个non-inline函数。一旦它有修改,客户原创 2024-03-09 20:03:49 · 827 阅读 · 0 评论 -
学完Efficient c++ (26-27)
当变量定义出现时,程序需要承受其构造成本;当变量离开其作用域时,程序需要承受其析构成本。因此,避免不必要的变量定义,以及延后变量定义式直到你确实需要它(甚至应该尝试延后变量的定义直到能够给它初值实参为止)。// 效率低// 效率高对于循环中变量的定义,我们一般有两种做法:Widget w;i < n;++i) {w = 取决于 i 的某个值;...这种做法产生的开销:1 个构造函数 + 1 个析构函数 + n 个赋值操作。i < n;++i) {原创 2024-03-08 10:53:46 · 560 阅读 · 0 评论 -
学完Efficient c++ (24-25)
这个条款告诉了我们。作者想给我们提个醒,如果我们在使用操作符时我们首先说明:如果一个操作符是成员函数,那么它的。现在我们有一个Rational类,并且它可以和int首先简单讲解一下当操作符被重载成员函数时,第一个操作数特殊的身份。操作符一旦被设计为成员函数,它在被使用时的特殊性就显现出来了——单从表达式你无法直接看出,不是吗?例如下方的有理数类重载的操作符”+”,当我们在调用时,调用操作符函数的对象并没有直接显示在代码中——这个操作符的this指针指向x还是y呢?作为成员函数的操作符的第一个隐形参数”原创 2024-03-07 10:38:20 · 809 阅读 · 0 评论 -
学完Efficient c++ (21-23)
一个量化某成员封装性好坏的简单方法是:看类内有多少(public或protected)函数直接访问到了这个成员,这样的函数越多,该成员的封装性就越差——该成员的改动对类外代码的影响就可能越大。回到我们的问题,高颗粒度函数在设计之时,设计者的本意就是。又或者采用不同namespace来明确责任,将不同的高颗粒度函数和浏览器类纳入不同namespace和头文件,当我们使用不同功能时就可以include不同的头文件,而不用在面对cache的需求时不可避免的将cookies的工具函数包含进来,降低编译依存性。原创 2024-03-05 10:35:31 · 645 阅读 · 0 评论 -
学完Efficient c++ (18-20)
当使用按值传参时,程序会调用对象的拷贝构造函数构建一个在函数内作用的局部对象,涉及大量参数的复制,这些副本大多是没有必要的,这个过程的开销可能会较为昂贵。对于任何用户自定义类型,使用按常引用传参是较为推荐的(因为没有任何新对象被创建,这种传参方式不会调用任何构造函数或析构函数,所以效率比按值传参高得多。)对于内置类型、STL的迭代器和函数对象,使用按值传参是比较合适的。原创 2024-03-04 10:26:33 · 614 阅读 · 0 评论 -
学完Efficient c++ (14-17)
每一次复制对象就使引用计数+1,每一个对象离开定义域就调用析构函数使引用计数-1,直到引用计数为0就彻底销毁资源。当我们在设计自己的资源管理类时,也要考虑在提供对原始资源的访问时,是使用显式访问还是隐式访问的方法,还是两者皆可。,例如在一个对象中有一个指针,那么在复制这个对象时就不能只复制指针,也要复制指针所指向的数据。的行为类似,永远保持只有一个对象拥有对资源的管理权,当需要复制对象时转移资源的管理权。我们应该永远保持这样的思考:当一个RAII对象被复制,会发生什么事?原创 2024-03-03 14:35:32 · 418 阅读 · 0 评论 -
学完Efficient c++ (13) + 智能指针详解和实现
内存只是众多被管理的资源之一,对待其他常见的资源如互斥锁、文件描述器、数据库连接等时,我们要遵循同一原则——如果你不再使用它们,确保将他们还给系统。1. C++ 98 中产生了第一个智能指针auto_ptr.2. C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.3. C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。4. C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr原创 2024-03-02 11:34:49 · 538 阅读 · 0 评论 -
学完Efficient c++ (11-12)
除此之外,拷贝构造函数和拷贝赋值操作符,他们两个中任意一个不要去调用另一个,这虽然看上去是一个避免代码重复好方法,但是是荒谬的。那么对于管理一定资源的对象重载的operator = 中,一定要对是不是自我赋值格外小心并且增加预判,因为无论是深拷贝还是资源所有权的转移,原先的内存或所有权一定会被清空才能被赋值,如果不加处理,这套逻辑被用在自我赋值上会发生——先把自己的资源给释放掉了,然后又把已释放掉的资源赋给了自己——出错了。如果你忘记处理,编译器也不会报错。指向的是相同的对象,就会导致访问到已删除的数据。原创 2024-03-01 11:11:27 · 589 阅读 · 1 评论 -
学完Efficient c++ (08-10)
如果析构函数执行期间某个时刻抛出了异常,就说明抛出异常后的代码无法再继续执行,这是一个非常危险的举动——因为析构函数往往是为类对象兜底的,甚至是在该对象其他地方出现任何异常的时候,析构函数也有可能会被调用来给程序擦屁股。在多态环境中,我们需要重新理解构造函数和析构函数的意义(在创建派生类对象时,基类的构造函数永远会早于派生类的构造函数被调用,而基类的析构函数永远会晚于派生类的析构函数被调用),这两个函数在执行过程中,涉及到了对象类型从基类到子类,再从子类到基类的转变。原创 2024-02-29 10:59:09 · 455 阅读 · 1 评论 -
学完Efficient c++ (06-07)
如果一个类有多态的内涵,那么几乎不可避免的会有基类的指针(或引用)指向子类对象,因为非虚函数没有动态类型,所以如果基类的析构函数不是虚函数,那么在基类指针析构时会直接调用基类的析构函数,造成子类对象仅仅析构了基类的那一部分,有内存泄漏的风险。但若此时从该基类中派生出新的类,会发生报错,这是因为派生类的析构函数中将会调用基类中的纯虚析构函数,而编译器无法找到基类的析构函数的实现。虚析构函数的运作方式是:最深层派生的那个类的析构函数最先被调用,然后是其上的基类的析构函数被依次调用。原创 2024-02-28 10:58:42 · 414 阅读 · 0 评论 -
学完Efficient c++ (05)
编译器会主动为你编写的任何类声明一个拷贝构造函数、拷贝复制操作符和一个析构函数,同时如果你没有声明任何构造函数,编译器也会为你声明一个default版本的拷贝构造函数,这些函数都是public且inline的。对于拷贝构造函数,你要考虑到类内成员有没有深拷贝的需求,如果有的话就需要自己编写拷贝构造函数/操作符,而不是把这件事情交给编译器来做。对于拷贝构造函数,如果类内有引用成员或const成员,你需要自己定义拷贝行为,因为编译器替你实现的拷贝行为在上述两个场景很有可能是有问题的。原创 2024-02-27 16:11:58 · 247 阅读 · 0 评论 -
学完Efficient c++ (04)
对于类中的成员变量而言,需要通过构造函数进行初始化,确保每一个构造函数都将对象的每一个成员初始化。对象的成员变量的初始化发生在进入构造函数之前,在构造函数内使用“=”是给成员变量赋值,而不是初始化,初始化发生在成员变量的default构造函数被自动调用时。(基于赋值的构造函数,首先调用default构造函数为成员变量设初值,然后再对它们赋新值)使用构造函数成员初始化列表(基于初值列的构造函数,对成员变量设实参,每个成员变量以实参为初值进行copy构造,效率更高;对于内置类型的成员变量,两个方法效率相同)原创 2024-02-27 13:52:20 · 240 阅读 · 0 评论 -
学完Efficient c++ (01-03)
编译器对待const对象的态度通常是 bitwise constness(const成员函数不能修改对象内任何非静态成员变量),而我们在编写程序时通常采用 logical constness,这就意味着,在确保客户端不会察觉的情况下,我们认为const对象中的某些成员变量应当是允许被改变的,使用关键字。需要注意的是,反向做法:令const版本调用non-const版本以避免重复——并不被建议,一般而言const版本的限制比non-const版本的限制更多,因此这样做会带来风险。原创 2024-02-26 21:48:49 · 674 阅读 · 1 评论