《Effectirve C++》笔记

c++编码规范 - 前置声明 之所以讨论这个问题,是在使用inline namespace的时候,遇到了模块代码中的头文件中使用了前置声明,导致了代码修改时, 修改一个模块的时候,需要同时修改其他的模块的代码, 也不能通过namespace aliase 进行处理, 符合下面确定缺点2的部分的说明。 有不少文章是推崇使用前置声明的,这边判断下来各有功过吧,libc++ 对前置声明还是有不少的使用的,比如iosfwd头文件就是个很好的说明。 下面规则来源: https://zh-google-styleguide.readthedocs. 阅读详情
 

作者通过一系列条款的方式讲述了开发经验,看了以后受益匪浅,下面还保留原有条款目录进行笔记记录。通过块引用方式增加了自己的想法。

条款1:视C++为一个语言联邦

C++是多种范式汇集的语言,包括C的过程、C++面向对象、Template泛型(STL模板库)、模板元编程、lambda。(后面自己补充可能由曲解原文意思)

条款2:尽量以const、enum、inline替换#define

单纯变量用const或enum替换define,形势函数的宏用inline函数替换。

class ttt{
    enum{NumTurns=5};
    int scores[NumTurns];
}

用枚举替换更像define,因为枚举和define均不可取地址,而const的变量可以被取地址。

条款3:尽可能使用const

const char *p;指针非常量,数值为常量–我还是喜欢声明时把*和变量放一起,构成一个整体,这样也容易理解,const就是对这个整体修饰的这个整体不可变也就是*p也就是值不可变。char * const p;指针常量,值非常量

对函数指定const:void a() const;此时函数不可调用非const成员(可以读非const数据成员),如某个数据成员需要被const方法操作,可以用mutable修饰:mutable boos bbb;

const char& operator[](std::size_t position)const {
  XXXXX
  return pText[position];
}
char& operator[](std::size_t position){
  return const_cast<char&>(static_cast<const CTextBlock&>(*this)[position]);
}

避免代码重复可用const_cast去掉const符号。先将非const调用过程的this转换为const class object调用,实现对const函数调用,并将返回值的const去掉。注意可以用非const函数使用此方式,不要用const函数通过此方式调用非const函数。

条款4:确定对象使用前已先被初始化

内置(class、struct内)对象需要手工初始化。
构造函数最好用初始化列表,而不是在函数体内进行赋值(函数体内赋值前会自动先对成员进行构造,若没有初始化列表构造时的值是随机的,这样等于两次操作),初始化列表次序随意,但初始化顺序以声明顺序为准。
不同文件中的外部对象初始化顺序不一定,若跨文件调用(以extern方式引用全局变量)不保证其已经被初始化。尽量使用内置对象。

条款5:了解C++默认编写并调用哪些函数

这个对么?这里说编译器会自动创建构造、拷贝、析构函数,但《深度探索C++对象模型》中说的是只有必要的时候才会创建出来。文中说如果有自定义的构造函数则不会创建,但探索中说的是也会在自定义构造前面加东西如果必要。。。。我更原因相信深度探索里写得,因为那样更合理,更高效,通过编译期的复杂判断保证了runtime时的高效。

条款6:若不想使用编译器自动生成的函数,就该明确拒绝

将拷贝构造、赋值操作符声明为私有可以禁止此行为。可通过建立一个具有私有拷贝和赋值函数的类并继承此类以避免每个类都重复私有化函数的撰写。

条款7:为多态基类声明virtual析构函数

带多态性质的base class应具有虚析构函数,而不是普通析构函数(这样会导致以base class指针释放派生类时不调用派生类的析构函数,派生类空间未释放),也不是纯虚析构函数(析构调用过程是最深层次的base class的析构最先被调用,若为纯虚将无法调用)

继承stl中的也要注意是否有虚析构,若没有不建议继承。

可以用组合,感觉还是多用组合好。接口要保证虚析构+所有其他均为纯虚函数,没有构造函数。

条款8:别让异常逃离析构函数

析构函数不应该抛出异常:对象在函数结束时自动析构,若出现异常会中断后续对象的析构,若未中断后续析构再出现异常会有多个异常同时出现,这些行为都不可控。

析构函数中若有可能出异常的调用,应自己try catch,并在catch中消除异常不继续上传,或直接调用std::abort结束程序。对于可能发生异常的方法最好提供普通接口供用户直接调用处理,并在析构时亦自动调用以减少一部分错误可能。

条款9:绝不在构造和析构过程中调用virtual函数

对于C++:在构造和析构中不要调用虚函数,因为这样的调用不会真正调用到派生类。java C#没事。

编译期间会自动添加代码完成vptr的指向,并完成基类、成员对象的构造代码的添加以及数据成员构造代码,这些都会增加到函数代码之前(初始化列表的初始化值对应添加到相应构造初始值中)。这样来说构造函数中的代码执行时vptr已经正确指定了,只要派生类重写了就已经指向派生类了。

析构是先调用函数本体,然后进行从深层次到当前的内存释放。

也就是说,如果保证这个构造函数是最后的派生类的构造函数时,是可以在构造、析构中调用虚函数的。如果是基类的构造和析构那么真出现了调用绝对不会调用到派生类。

条款10:令operator=返回一个reference to *this

赋值采用的右结合律 x=(y=(z=15)));

赋值操作符要返回一个对当前对象的引用:

MyStr& operator =(const MyStr& str) {
    return *this;
}

条款11:在operator=中实现“自我赋值”

避免x=x发生时出现自己的资源被自己释放的问题。要精心处理复制过程的new delete以及拷贝过程,包括对源和目标的分析。其余的任何函数也应注意被操作的多个对象实质是一个对象时的问题。

MyStr& operator =(const MyStr& str) {
    if(this == &str) return *this;
    return *this;
}

条款12:复制对象时勿忘其每一部分

赋值、拷贝函数应确保对所有成员变量及base class成分的复制。不要一个copy调用另一个copy而应该共同机制放在第三个函数。

条款13:以对象管理资源

通过对象的构造函数创建资源,并在析构函数释放。尽量不要在一个函数内new后delete以防止delete之前return导致资源未释放。(资源取得时机便是初始化时机“Resource Acquisition Is Initialization,RAII”)。

可以使用std::auto_ptr,auto_ptr在调用赋值/拷贝函数时,会将源中存储的指针改为null,避免共同管理同一个对象,否则析构时会多次释放导致不可控。

sharedptr通过引用计数方式记录(引用计数型智慧指针reference-counting smart pointer,RCSP),而不是在赋值/拷贝时直接转移所有权(源定义为null),以避免对源智能指针的操作导致错误问题。注意环状引用时会出错,两个指针互指且不再使用也不会自动释放。

std::tr1::shared_ptr的析构函数是delete不是delete []不要对动态分配得到的array使用,std::tr1::shared_ptr<int> spi(new int[10]);此时最后只会释放int[0]这一个空间。boost中的boost::scoped_array和boost::shared_array可以进行动态分配空间的delete,但一般用string、vector等容器即可。

c++11中auto_ptr已经被弃用。应使用shared_ptr。

C++ Technical Report 1 (TR1)是ISO/IEC TR 19768, C++ Library Extensions(函式库扩充)的一般名称。TR1是一份文件,内容提出了对C++标准函式库的追加项目。这些追加项目包括了正则表达式、智能指针、哈希表、随机数生成器等。TR1自己并非标准,他是一份草稿文件。然而他所提出的项目很有可能成为下次的官方标准。这份文件的目标在于「为扩充的C++标准函式库建立更为广泛的现成实作品」。

C++ tr1是针对C++标准库的第一次扩展。即将到来的下一个版本的C++标准c++0x会包括它,以及一些语言本身的扩充。tr1包括大家期待已久的smart pointer,正则表达式以及其他一些支持范型编程的东东。草案阶段,新增的类和模板的名字空间是std::tr1。

C++11,先前被称作C++0x,即ISO/IEC 14882:2011,是目前的C++编程语言的正式标准。它取代第二版标准ISO/IEC 14882:2003(第一版ISO/IEC 14882:1998公开于1998年,第二版于2003年更新,分别通称C++98以及C++03,两者差异很小)。新的标准包含核心语言的新机能,而且扩展C++标准程序库,并入了大部分的C++ Technical Report 1程序库(数学的特殊函数除外)。最新的消息被公开在 ISO C++ 委员会网站(英文)。

条款14:在资源管理类中小心copying行为

对RAII对象复制要同时复制器管理的资源。常见的复制行为是抑制复制、引用计数。

条款15:在资源管理类中提供对原始资源的访问

应提供对原始资源获取的接口,有些需要直接操作原始资源的情况。可通过get函数或者通过operator A() const {}方式提供隐式转换函数。

条款16:成对使用new和delete时要采取相同形式

new和delete要同时使用[]或者都不用。

条款17:以独立语句将newed对象置入智能指针

对于智能指针的使用应独立一个语句,而不是把对象创建、智能指针构建作为一个其他函数的参数,这样可能出现内存泄漏。
如双参数函数,第一个参数是智能指针,第二个是其他操作,当new出新的对象以后,先执行了第二个参数操作并出现异常,此时new的新对象并未作为参数使智能指针构造,process(std::tr1::shared_ptr<Wideget>(new Widget), priority());应分为两个函数。

条款18:让接口容易被正确使用,不容易被误用

shared_ptr可以定制删除器(第二个参数),防止DLL问题,可用于自动解除互斥所。cross-DLL problem对象在动态链接库dll中被new创建,却在另一个dll内被delete销毁。

保证接口一致性、误用上要有足够的约束。

条款19:设计class犹如设计type

每个类都是一个类型,应充分考虑一个类型的各方面,包括重载函数和操作符、内存分配于释放、对象的创建和销毁。

条款20:宁以pass-by-reference-to-const替换pass-by-value

用const引用替换传值,这样高效也能避免切割问题,对于内置类型传值更好。

多态只有在引用、指针才有效,若将派生类以值的方式给予父类可能出现切割情况。

条款21:必须返回对象时,别妄想返回其reference

不要返回函数中new而存在于堆中对象的引用(不知道谁负责delete),不要返回函数局部变量的引用(栈中,这样函数结束实例已经被销毁)。不要反悔局部静态成员的引用,对于多线程不安全,对于连续调用行为也不可控,当真的需要返回一个对象时就返回对象而不是引用。

任何一个引用必然存在一个和他不同名的相同实例,不得不考虑另一个名字的行为。

条款22:将成员变量声明为private

protected并不比public更具封装性。数据成员的操作应具有一致性,public下无法保证调用者行为。

条款23:宁以non-member、non-friend替换member函数

可以把成员函数放到另一个class做为静态函数,以保证一个class就是对数据的封装,超出目的范围的提出来,保证封装性。

条款24:若所有参数皆需要类型转换,请为此采用non-member函数

能避免friend就避免,变成非成员函数以后若非必要不要加friend,朋友带来的麻烦往往多余价值。

条款25:考虑写出一个不抛出异常的swap函数

当swap对自定义类型效率不高时,应提供swap成员函数,并确保不发生异常。并提供一个非成员函数调用他。

对于建立的非成员函数,std命名空间中不能够扩充新的函数,重载也不行,对于函数的非成员swap要用全特化。template<> void swap<TT>(xx& a, xx& b){};

调用swap时神队std::swap使用using声明式,然后调用swap并不带任何命名空间资格修饰

条款26:尽可能延后变量定义式的出现时间

尽量在用的时候建立对象,不要提前建立。

对于循环,除非明确知道复制成本小于“构造+析构”成本,并且正在处理效率高度敏感的代码,否则不要把变量初始化放到循环外面。

确实从来没考虑过赋值成本与构造+析构的大小问题,单纯的只考虑了提出循环避免构造+析构。。。。从来都是写到外面的

条款27:尽量少做转型动作

提倡使用四个C++风格转型,不要用T(a)/(T)a这样的C方式转型。

const_cast常量性转换,唯一有此能力的C++转型符号

dynamic_cast主要用来进行安全向下转型,从父类引用或指针向派生类转换。派生类转父类用static_cast就行。这个需要消耗重大的运行成本

reinterpret_cast低级转型,行为和结果取决于编译器,不可移植。

static_cast强制转型,但无法进行const、non-const转换

static_cast也不能去掉volatile、__unaligned

reinterpret_cast以二进制形式对原有对象进行重新解释(可进行无关类型的转换),可以把指针转换为int等等,对一段区域的二进制内容进行赋值构成新的值并赋予新的类型解释,不会对二进制上的内容做任何修改,可用于:

  • 从指针类型到一个足够大的整数类型
  • 从整数类型或者枚举类型到指针类型
  • 从一个指向函数的指针到另一个不同类型的指向函数的指针
  • 从一个指向对象的指针到另一个不同类型的指向对象的指针
  • 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
  • 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针

条款28:避免返回handles指向对象内部成分

对于const函数避免返回指针、引用、迭代器指向内部成员(包括const的指针引用迭代器),使一个const函数更像一个常量函数,并避免dangling handles虚吊号码牌。

条款29:为“异常安全”而努力是值得的

异常安全函数即使发生异常也不会泄露资源或允许任何数据结构破坏。这样的函数分为三种保证:

基本型:发生时,程序内任何事物仍然保持在有效状态下,不破坏对象和数据结构。但当前状态无法预料。

强烈类型:发生时,程序状态不变。

不抛异常型:int aa() throw();告知它宗科正常运行,不是永远不抛异常,而是如果发生将是致命错误。

绝大部分都在基本和强烈之间。一个系统有一个函数不具备异常安全性则整个系统都不具备。

一个函数的异常安全性不会超过所调用函数的最低异常安全性级别。

条款30:透彻了解inlining的里里外外

inline应用在小型、被频繁调用的函数上,对后续调试过程、二进制升级更好也可降低代码膨胀问题。

不要因为是函数模板就一定要声明为inline。

条款31:将文件间的编译依存关系降至最低

1想依赖于声明式,而不是定义式。头文件中应以完全且仅有声明式的形势存在,无论是否涉及模板。也就是私有成员用指针,前置声明对应class并在源文件中include。

2进一步的将私有成员过多情况下可以构建一个针对此class的实现类,并在声明类中的private只提供一个实现类的指针(用智能指针),将所有需要涉及到多个私有成员的实现操作转移到实现类。

3还可以用接口类。

对于2,类似于Qt库的实现,每个公开调用的类只有一个私有成员类名+private类的指针。

对于3,接口类要求无构造、虚析构、其他全部纯虚函数。无数据成员。

谷歌C++编程规范(当然这部分说了不要前置声明任何std内容):

尽可能地避免使用前置声明。使用 #include 包含需要的头文件即可。

优点:

  • 前置声明能够节省编译时间,多余的 #include 会迫使编译器展开更多的文件,处理更多的输入。
  • 前置声明能够节省不必要的重新编译的时间。 #include 使代码因为头文件中无关的改动而被重新编译多次。

缺点:

  • 前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的重新编译过程。

  • 前置声明可能会被库的后续更改所破坏。前置声明函数或模板有时会妨碍头文件开发者变动其 API. 例如扩大形参类型,加个自带默认参数的模板形参等等。

  • 前置声明来自命名空间 std:: 的 symbol 时,其行为未定义。

  • 很难判断什么时候该用前置声明,什么时候该用 #include 。极端情况下,用前置声明代替 includes 甚至都会暗暗地改变代码的含义:

    // b.h:
    struct B {};
    struct D : B {}
    
    // good_user.cc:
    #include "b.h"
    void f(B*);
    void f(void*);
    void test(D* x) { f(x); }  // calls f(B*)
如果 #include 被 B 和 D 的前置声明替代, test() 就会调用 f(void*) .
  • 前置声明了不少来自头文件的 symbol 时,就会比单单一行的 include 冗长。
  • 仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员)会使代码变得更慢更复杂.

结论:

  • 尽量避免前置声明那些定义在其他项目中的实体.
  • 函数:总是使用 #include.
  • 类模板:优先使用 #include.

条款32:确定你的public继承塑模出is-a关系

条款33:避免遮掩继承而来的名称

继承时对基类替换只看名字,不看参数、类型。也就是派生类中一个函数会覆盖掉基类中多个同名的重载函数,同时派生类中一个数据对象会覆盖掉基类的其他类型的同名对象。

可以通过派生类中写using BaseClassName::funciton_nam;方式让基类的所有function_name的重载函数都可见。

话说所有函数不是都被重命名了么?命名过程考虑了函数名和参数列表,那这样了话重载的两个函数实际上不是一个名字,派生过程为什么会覆盖掉同名的所有重载函数?

条款34:区分接口继承和实现继承

public继承下派生类继承了基类所有接口。

纯虚函数:只继承了接口

常规虚函数:继承了接口和一个缺省实现,可以重写。

非虚函数:继承了接口和强制性实现。

非虚函数不应该重写,但可以有这个函数(不算重写了),意义不一样。最后生成的object中实际上没有存储派生类的非虚函数的指针(没再vptl中存储),不支持多态,对应的函数是在class中的代码段存储了。这样当用多态切换成基类时会直接去基类的代码段找实现(vptl中没有),也就是多态性质不存在。

条款35:考虑virtual函数以外的其他选择

找virtual函数替代方式,可以用NVI收发及Strategy设计模式。NVI是特殊形式的template method设计模式。缺点是函数移到了class外部成为非成员函数无法访问class的非public成员。

tr1::function行为像函数指针,但可接纳与指定目标签名式兼容的所有可调用物。

调用一个virtual函数是有成本的,要经过最少一次vptr跳转到vtbl然后找到对应的函数指针

条款36:绝不重新定义继承而来的non-virtual函数

34条补充写了。。。。

条款37:绝不要重新定义继承而来的缺省参数值

缺省函数值是静态绑定的,继承以后不应该再给任何一样/不一样的缺省值。

继承后重写只应该做动态绑定的事情。(就是vtbl中的函数指针指向的一个新的function object的实现)

条款38:通过复合塑模树has-a 或“根据某物实现出”

复合?composition我还是喜欢说组合。。。public继承是is-a关系,在私有成员中包含其他class的object是组合。

is-implemented-in-terms of根据某实物出现

条款39:明智而审慎的使用private继承

private继承不是is-a关系。而是类似于组合的形势,内部可以调用base class,不会有外部公开的接口。尽可能用组合而不是private继承。相比于组合可以造成empty base最优化,对于对象尺寸较友好。

谷歌规范里好像禁止还是不建议private继承,总会有办法解决不需要base公开接口,但想调用base的protected接口的情况。

组合 > 实现继承 > 接口继承 > 私有继承

条款40:明智而审慎的使用多重继承

多重继承可能需要虚基类。。。就这个就可以尽可能拒绝了

条款41:了解隐式接口和编译期多态

类对接口是显示的,以函数前名为中心,多态通过virtual发生于运行期。

对template参数而言,接口是隐式的,基于有效的表达式。多态通过模板具现化和函数重载解析并发生于编译器。

条款42:了解typename的双重意义

声明模板参数时class和typename同义。对嵌套从属类型名称需要用typename标识但不得在base class lists或初始化列表内作为基类的修饰符

嵌套从属类型

template<typename C> void xx() {

typename C::const_iterator* x;

}

必须这么声明告诉编译器C::const_iterator是个类型,这就是个声明。否则可能出现:const_iterator是C的一个静态数据成员,或者x是全局变量这就是个乘法运算。

条款43:学习处理模板化基类内的名称

模板支持继承,但这个继承不符合面向对象的概念。因为模板类有全特化特性,所以在派生模板类中调用基模板类时,并不知道基类是否有这个函数,所以编译解析函数过程中不会检索基类是否有,而是直接报错。可以通过this->或者直接写基类名称::或者通过using包含基类的接口实现在对函数编译时顺利通过。

但上述所有指定方式只是对编译器的一个承诺,若最后因为全特化而破坏了承诺,那么在解析具体实例化模板时会出现错误。

全特化、偏特化:模板类支持任意类型,不同类型传入均会获得所有模板类的方法。但全特化可对指定类型赋予单独的类实现。偏特化是模板类中有多个模板参数,但是只对个别几个参数指定固定值并赋予特别的实现。

条款44:将与参数无关的代码抽离templates

避免代码膨胀,与类型参数无关的抽离,与参数有关的可通过完全相同二进制表述的局限类型共享实现码。

条款45:运用成员函数模板接受所有兼容类型

请使用member function templates(成员函数模板)生成“可接受所有兼容类型”的函数。

如果声明member templates用于泛化copy构造函数或泛化assignment操作,还是要声明正常的copy构造函数和copy assignment操作符。

条款46:需要类型转换时请为模板定义非成员函数

当编写一个class template时,它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为class template内部的friend函数。

条款47:请使用traits class表现类型信息

STL源码中5中迭代器。

  1. input迭代器,它是read only,只能读取它指向的对象,且只能读取一次。它只能向前移动,一次一步。它模仿指向输入文件的阅读指针(read pointer);C++程序中的istream_iterators就是这类的代表。
  2. output迭代器,和input迭代器相反,它是write only。它也是只能向前移动,一次一步,且只能涂写一次它指向的对象,它模仿指向输出文件的涂写指针(write pointer);ostream_iterators是这一类代表。
  3. forward迭代器。这个迭代器派生自input迭代器,所以有input迭代器的所有功能。并且他可以读写指向的对象一次以上。
  4. bidirectional迭代器继承自forward迭代器,它的功能还包含向后移动。STL中的list、set、multiset、map、和multimap迭代器就是这一类迭代器。
  5. random access迭代器继承自bidirectional迭代器。它厉害的地方在于可以向前或向后跳跃任意距离,这点类似原始指针,内置指针就可以当做random access迭代器使用。vector、deque和string的迭代器就是这类。

traits是一种技术,是C++程序员共同遵守的协议。这个技术要求之一就是,它对内置类型和自定义类型表现的一样好。

traits classes使得类型相关信息在编译器可用,以模板和模板特化完成实现。并整合重载技术后使其有可能在编译器对类型进行if..else测试。

条款48:认识template元编程

将运行期代码提前到编译器。

条款49:了解new-handler的行为

class Widget{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
std::new_handler Widget::currentHandler=0;
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler=currentHandler;
currentHandler=p;
reutrn oldHandler;
}

Widget的operator new做以下事情:

  1. 调用标准set_new_handler,告知Widget错误处理函数。这会将Widget的new-handler安装为global new-handler。
  2. 调用global operator new,如果失败,global operator new会调用Widget的new-handler,因为第一步。如果global operator new最终无法分配足够内存,会抛出一个bad_alloc异常。这时Widget的operator new要恢复原本的global new-handler,之后在传播异常。
  3. 如果global operator new调用成功,Widget的operator new会返回一个指针,指向分配的内存。Widget析构函数会管理global new-handler,它会将Widget’s operator new被调用前的那个global new-handler恢复回来。

set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用。
nothrow new是一个颇为局限的工具,因为它只适用于内存分配;后继的构造函数调用还是有可能抛出异常。

以后好好研究一下

条款50:了解new和delete的合理替换时机

  1. 用来检测运用上的错误。如果delete new的内存失败,会导致内存泄漏。如果在new所得内存多次delete会导致不确定行为。使用编译器提供的operator new和operator delete不能检测上述行为。如果operator new持有一个链表,其存储动态分配所得内存,operator delete则将内存从链表删除,这样就能呢检测上述错误用法。如果编程错误,可能在分配内存的之前区域或之后区域写入数据;这时可以自己定义operator new分配超额内存,在多出部分写上特定byte patterns(即签名,signature),自己定义operator delete检测签名是否更改。
  2. 为了强化效能。operator new和operator delete如果开辟大内存、小内存,持续这样做会造成内存碎片,这在服务器的后台程序上,可能会导致无法满足大区快内存需求,即使有足够但分散的小区块自由内存。使用自己定制的operator new和operator delete可以避免这样的问题。针对特定的需求,有时还可以提升性能。
  3. 为收集使用上的统计数据。在定制operator new和operator delete之前,应该首先了解软件如何使用动态内存。分配区块如何分布?寿命如何?它们是FIFO先进先出还是LIFO后进先出,或随机分配和归还?软件在不同执行阶段有不同的分配归还形态吗?任何时刻使用的最大动态分配量是多少?自己定义的operator new和operator delete可以轻松收集到这些信息。
  4. 为了增加分配和归还的速度。使用定制的针对特定类型对象的分配器,可以提高效率。例如,Boost提供的Pool程序库便是。如果在单线程程序中,你的编译器所带的内存管理具备线程安全,你可以写个不具备线程安全的分配器而大幅度改善速度。
  5. 为了降低缺省内存管理器带来的空间额外开销。泛用型分配器往往(虽然并非总是)不只比定制型慢,还使用更多空间,因为它们常常在每一个分配区块上招引某些额外开销。针对小型对象开放的分配器,例如Boost库的Pool,本质上消除了这样的额外开销。
  6. 为了弥补缺省分配器的非最佳对齐(suboptimal alignment)。X86体系结构上的double访问最快–如果它们是8-byte对齐。但是编译器自带的operator new并不保证分配double是8-byte对齐。
  7. 为了将相关对象成簇集中。如果特定的某个数据结构往往被一起使用,我们希望在处理这些数据时将“内存页错误”(page faults)的频率降至最低,那么为此数据结构创建另一个heap就有意义,这样就可以将它们成簇集中到尽可能少的内存也上。
  8. 为了获得非传统的行为。有时候我们需要做operator new和delete没做的事。例如,在归还内存时将其数据覆盖为0,以此增加应用程序的数据安全。

有许多理由需要写个自定的new和delete,包括改善效能、对heap运用错误进行调试、收集heap使用信息。

条款51:编写new和delete时需固守常规

operator new应该内涵死循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler。它也应该有能力处理0 bytes申请。class专属版本的还应该处理“比正确大小更大的(错误)申请”。

operator delete应该在收到指针时不做任何事。class专属版本则还应该处理“比正确大小更大的(错误)申请”。

条款52:写了placement new也要写placement delete

当编写一个placement operator new时,也要编写对应版本的placement operator delete。否则就可能造成隐蔽的内存泄露。

当声明了placement new和placement delete时,就会掩盖正常版本。

条款53:不要轻忽编译器的警告

严肃对待编译器发出的警告信息。努力在你的编译器最高警告级别下做到无任何警告。

不要过度依赖编译器的报警能力,因为不同编译器对待事情的态度不相同。一段有警告的代码,移植到另一个编译器上,可能没有任何警告。

条款54:让自己熟悉包括TR1在内的标准程序库

C++98加入的:

STL,即Standard Template Library标准模板库。
iostreams,包含用户自定义缓冲功能、国际化I/O,以及先定义好的对象cin、cout、cerr和clog。
国家化支持,包括多区域能力。
数值处理,包括复数模板(complex)和纯数值数值(valarray)。
异常阶层体系(exception hierarchy)。
C89标准程序库。

TR1

  • 智能指针
  • tr1::function,可以表示任何callable entity(可调用物,即任何函数或函数对象),只要签名复合目标。
  • tr1::bind,它能做STL绑定器(binders)bind1st和bind2nd所做的每一件事,且更多。

把TR1组件划分为2组,第一组提供彼此互不相干的机能:

  • Hash table,可以用来实现set、map等。
  • 正则表达式(Regular expression),包括以正则表达式为基础的字符串查找和替换等。
  • Tuples(变量组),这是标准程序库中的pair template的新一代制品。
  • tr1::array,本质是一个支持成员函数begin和end的数组。
  • tr1::mem_fn,这是个语句构造上与成员函数指针(member function pointer)一致的东西。
  • tr1::reference_wrapper,一个让reference的行为更像对象的设施。
  • 随机数(random number)生成工具,它大大超越了rand。
  • 数学特殊函数。
  • C99兼容扩充

第二组TR1组件由更精巧的template编程技术构成

  • Type traits,一组traits classes(**条款**47),用以提供类型的编译期信息。
  • tr1::result_of,这是个template,用来推导函数调用的返回类型。

总结

C++的标准程序的主要机能由STL、iostream、locales组成。并包含C99标准程序库。
TR1添加了智能指针、一般化函数指针、hash-based容器、正则表达式以及另外10个组件的支持。
TR1只是一份规范,为获得TR1提供的好处,需要一个实物,例如Boost。

条款55:让自己熟悉Boost

Techie亮博客,转载请注明:Coologic » 《Effectirve C++》笔记

Coologic 博客域名已更改,已从 www.techieliang.com 改为 www.coologic.cn,上述链接地址受到影响,若需查看源文请手动修改,多有不便,敬请谅解。
利用MATLAB 4f光学系统,在Python中进行弗劳恩霍夫衍射和傅里叶面滤波——用镜头模拟光学图像处理。.zip 1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 立即下载

相关推荐

利用MATLAB和Excel对逆流双管热交换器的热性能进行分析。.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

《Effective C++》学习笔记

文章目录一、让自己习惯C++条款01:视C++为一个语言联邦条款02:尽量以const,enum,inline替换 #define条款 03:尽可能使用const条款 04:确定对象被使用前已先被初始化二、构造/析构/赋值运算条款05:了解C++默默编写并调用了哪些函数条款06:若不想使用编译器自动生成的函数,就应该明确拒绝条款07:为多态基类声明virtual析构函数条款08:别让异常逃离析构函数条款09:绝不再构造和析构函数中调用virtual函数条款10:令operator= 返回一个referenc

一念之间 一心向阳 1006

利用MATLAB对5G NR波束成形技术的性能分析.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

关于前置声明:#include类和前置class类名的区别

今天看别人写的代码,用到了QTreeWidgetItem,这是QT自带的类,但是并没有#include<QTreeWidgetItem> 而是在头文件里写了类的声明: 然后用到这个类的函数接口: 你看用到的都是指针! 原来是因为当你在头文件声明成员变量或成员函数时,如果只需要用到某个类的指针而不需要用到类的对象,那么就可以直接只是声明一下这个类,不用include,这样可以...

$firecat利白的代码足迹$ 3133

上市公司赫芬达尔指数、企业勒纳指数(1999-2025年)

上市公司赫芬达尔指数(HHI)和勒纳指数(LI)是衡量市场结构和市场势力(垄断力量)的两个重要指标 赫芬达尔指数(HHI)可衡量一个公司在市场中的相对份额或集中度,通过计算公司在市场中份额的平方和得出。数值越小,说明市场份额分布越分散,竞争程度越高;数值越大,说明市场集中度越高,少数企业占据较大份额,垄断程度越高 勒纳指数(LI)用于衡量企业的市场势力,即企业偏离完全竞争价格、制定高于边际成本的价格的能力,数值越接近1,表示企业市场势力越强,垄断程度越高 本文借鉴刘贯春等(2021)、孙茹峰和李楠博(2024)的思路和方法,分别测算上市公司赫芬达尔指数、勒纳指数: 赫芬达尔指数 = (公司营业收入/同行业同年营业收入合计)的平方和 企业勒纳指数 =(营业收入-营业成本-销售费用-管理费用)/营业收入 一、数据介绍 数据名称:上市公司赫芬达尔指数、企业勒纳指数 数据范围:上市公司 时间范围:1999-2025年 样本数量:85810条 数据来源:上市公司年报 更新时间:2026年11月 数据说明:内含原始数据、测算结果 二、数据指标 股票代码 股票简称 年份 省份 城市 区县 省份代码 城市代码 区县代码 行业代码 行业名称 赫芬达尔指数 勒纳指数 营业收入 营业成本 销售费用 管理费用 三、参考文献 [1]刘贯春,叶永卫,张军.社会保险缴费、企业流动性约束与稳就业——基于《社会保险法》实施的准自然实验[J].中国工业经济,2021,(05):152-169. [2]孙茹峰,李楠博.金融化对企业绿色转型意向的影响研究[J].管理学报,2024,21(11):1612-1620+1677. [3]张倩肖,段义学.数字赋能、产业链整合与全要素生产率[J].经济管理,2023,45(04):5-21. [4]伍兆祥,代吉林,

工业互联网工控安全与工业质检AI大模型建设方案.pptx

工业互联网工控安全与工业质检AI大模型建设方案.pptx

Effect C++ 学习笔记4

条款4:尽量使用C++风格的注释 第一部分:关于两种注释 1.C语言的注释很容易产生错误 例如: if(a > b) { /* swap(a, b); /*交换A和B的位置*/ */ }这样会使注释提前结束。 2.有些老的专门为C语言设计的预处理程序,可能不知道C++风格的注释 例如: #define Pi 3.1415926 //圆周率//后

You can you up 672

通信行业CentOS 7内核网络栈优化:高并发连接下UDP/TCP性能调优与系统资源管理

内容概要:本文聚焦于CentOS 7系统在通信行业高并发场景下的性能调优实践,重点围绕内核网络栈优化展开,涵盖UDP/TCP缓冲区调整、文件句柄与连接跟踪表扩容、TIME_WAIT状态复用、软中断分布优化及tuned工具的自动化调优。通过具体配置参数和实际案例(如VoIP软交换系统)深入解析各项调优措施的设计原理与实施效果,有效解决高CPS下丢包、连接失败等问题,并展望了向云化、eBPF等动态智能调优方向的发展趋势。; 适合人群:从事通信系统运维、网络开发或Linux系统性能优化的技术人员,具备一定操作系统和网络基础知识,工作经验1年以上的中初级工程师。; 使用场景及目标:①应用于软交换、媒体网关、信令网关等高并发通信设备的系统调优;②解决UDP丢包、conntrack表满、TCP连接队列溢出等典型性能瓶颈;③提升系统在小包高频、低时延要求场景下的稳定性和吞吐能力。; 阅读建议:建议结合实际生产环境或测试平台同步验证文中配置,重点关注参数背后的物理意义(如BDP、softirq、conntrack机制),并配合ss、conntrack、dmesg等命令进行调优前后对比分析,确保安全可控地实施变更。

《Effective C++》 总结笔记

1. 让自己习惯C++ 1:视C++为一个语言联邦 c++是多重范型编程语言,视c++包括4种次语言: 1:C; 2:Object-Oreinted C++;3:Template C++;4:STL(template程序库,包括容器、迭代器、算法和函数对象)。 2:尽量以const,enum,inline替换 #define const: #define直接替换导致名称从未被编译...

stIck_NHNP的博客 8707

2024 《计算机网络》实验教学指导书.doc

2024 《计算机网络》实验教学指导书.doc

不平衡电网下自抗扰顺序模型预测PWM整流器控制仿真模型(Simulink仿真实现)

内容概要:本文围绕面向城市空中交通的多无人机路径规划问题展开研究,提出了一套融合全局规划、动态避障与局部协同调整的综合路径规划方法。研究内容涵盖基于改进RRT算法与图论的全局路径规划、基于动态风险场和动态窗口法(DWA)的模型预测控制实现动态避障,以及基于分布式协同控制的多无人机局部路径协同策略。通过Matlab平台构建仿真环境,在多种典型城市交通场景下对所提方法进行了系统性验证,结果表明该方法在路径安全性、编队稳定性与任务效率方面表现优异。文章还深入探讨了算法的创新点与未来研究方向,为复杂城市环境下多无人机系统的自主协同提供了理论支撑与技术路径。; 适合人群:具备自动化、控制理论、计算机科学或相关领域基础知识,从事无人机系统、智能交通、路径规划、多智能体协同控制等方向研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①为城市空中交通系统中的多无人机安全、高效飞行提供可靠的路径规划解决方案;②支持在复杂、动态的城市场景中开展无人机自主导航、编队飞行与协同控制的技术研究与仿真验证。; 阅读建议:建议结合提供的Matlab代码进行实践操作,重点关注算法的具体实现细节、参数配置对性能的影响以及不同仿真场景下的实验设计,以深入理解各模块的集成逻辑与实际应用效果。

《Effectirve C++》笔记(条款1~20)

条款1:视C++为一个语言联邦 C是多种范式汇集的语言,包括C的过程、C面向对象、Template泛型(STL模板库)、模板元编程、lambda。(后面自己补充可能由曲解原文意思) 条款2:尽量以const、enum、inline替换#define 单纯变量用const或enum替换define,形势函数的宏用inline函数替换。 class ttt{ enum{NumTurn...

supperlinux的博客 244

【选址和定容】模拟退火改进多目标粒子群算法在分布式电源选址和定容中的应用【IEEE69节点】(Matlab代码实现)

内容概要:本文研究了模拟退火改进多目标粒子群算法在分布式电源选址和定容中的应用,以IEEE69节点配电系统为算例进行仿真分析。通过融合模拟退火算法的强全局搜索能力与粒子群算法的高效局部收敛特性,构建了一种改进的多目标优化算法,用于解决分布式电源在配电网中的最优位置选择与容量配置问题。研究旨在实现多重优化目标,包括最小化网络有功损耗、提升节点电压质量、增强系统运行的经济性与稳定性。文中详细阐述了多目标优化模型的数学构建、算法的改进机制与实现流程,并基于Matlab平台完成了算法编程与仿真验证,结果表明所提方法在求解质量和收敛性能上均优于传统算法。; 适合人群:具备电力系统分析、优化理论基础及Matlab编程能力,从事配电网规划、分布式能源系统优化、智能优化算法应用等相关领域的科研人员、工程师以及高校研究生。; 使用场景及目标:①解决高比例分布式电源接入背景下配电网的科学规划问题;②为新型电力系统中源网协调设计提供有效的优化工具;③为相关领域的科研工作提供可复现、可拓展的算法模型与仿真代码实例,支持进一步的算法对比与功能升级。; 阅读建议:建议读者结合提供的Matlab代码与IEEE69节点标准数据,动手复现实验结果,深入理解算法的参数设置、迭代过程与多目标折衷分析方法,并可根据实际研究需求修改目标函数或引入新的约束条件以适应不同的应用场景。

《Effectirve C++》笔记(条款21~40)

条款21:必须返回对象时,别妄想返回其reference 不要返回函数中new而存在于堆中对象的引用(不知道谁负责delete),不要返回函数局部变量的引用(栈中,这样函数结束实例已经被销毁)。不要反悔局部静态成员的引用,对于多线程不安全,对于连续调用行为也不可控,当真的需要返回一个对象时就返回对象而不是引用。 任何一个引用必然存在一个和他不同名的相同实例,不得不考虑另一个名字的行为。 条款...

supperlinux的博客 242

RAG——从0到1快速搭建RAG应用

本文介绍了基于LangChain框架构建RAG(检索增强生成)应用的技术方案,涵盖核心模块选型与实现流程。技术栈包括:LangChain应用框架、pypdf文档解析、RecursiveCharacterTextSplitter文本分块、bge-small-zh-v1.5向量模型、Faiss向量库和通义千问大模型。文章详细说明了开发环境配置步骤,包括虚拟环境创建、依赖库安装和模型下载。核心代码分为索引、检索、生成三大流程,索引流程完成PDF文档解析、分块和向量化存储;检索流程实现查询向量化和相似度匹配;生成流

小志的博客 589

大模型推理常见采样策略:Top-k, Top-p, Temperature, Beam Search

大模型推理采样策略,举例通俗易懂汇总介绍

qq_43243579的博客 2万+

《Effective C++中文版 第三版》下载仓库介绍

《Effective C++中文版 第三版》下载仓库介绍 去发现同类优质开源项目:https://gitcode.com/ 欢迎来到《Effective C++中文版 第三版》资源文件下载仓库!本书深入浅出地阐述了如何开发高效的C++系统,详细介绍了各种成熟的设计模式和方法。 本书内容全面,覆盖了C++编程的各个方面,旨在帮助读者掌握C++编程技巧,提高代码质量,提升系统性能。通过阅读本书,您将学...

gitblog_06750的博客 247

高频面试题-项目介绍篇

聊聊如何介绍项目经历。

m0_73359763的博客 1908

C/C++编程:前置声明

什么是前置声明 一般的前置函数声明 #include <iostream> using namespace std; void fun(char ch, int *pValue, double dValue); void main() { int nValue = 100; double dValue = 111.22; fun('a', &nValue, dValue); system("pause"); } void fun(char ch, int

OceanStar的博客 2405

2022-04-06 RK3566 上调试百兆以太网卡YT8512C 调试记录

一、原理图 二、dts &gmac1_clkin{ clock-frequency = <50000000>; status = "okay"; }; &gmac1 { phy-mode = "rmii"; clock_in_out = "input"; snps,reset-gpio = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>; snps,reset-active-low; sn...

海月汐辰 1万+

国税局发票查验中英文验证码识别最新版,识别率99.9%

采用深度学习进行发票查验验证码模型的训练,在我电脑上模型训练的环境如下: 显卡:RTX 2080TI tensorflow-gpu:2.5.3 1 训练集和测试集的准备 发票查验的验证码分为4种类型,分别为黑色、红色、黄色、蓝色,经过我的测试,采用4个模型的效果最好,每个模型对应识别一种类型的验证码。 首先我们4种类型的验证码都准备10万张。 00黑色类型的验证码: 01红色类型的验证码: 02黄色类型的验证码: 03蓝色类型的验证码: 验证码图像图像准备好后,对其进行标记,也就是对验证码重命名:

Keras深度学习 83万+

C++编程规范 - Google

英文文档路径:http://google.github.io/styleguide/cppguide.html 中文文档路径:https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/ 1. 头文件   通常每一个.cc(或.cpp,常用于非Unix系统)文件都有一个对应的.h文件。也...

badang8693的博客 602

【Matlab】matlab代码实现迭代学习控制

该文介绍了在Matlab中实现迭代学习控制(ILC)的方法。ILC通过利用历史轨迹数据来优化重复任务的控制性能。文章提供了一个步进电机控制示例,包括:1)定义正弦轨迹;2)设置100次迭代和0.1学习率;3)建立二阶系统模型;4)执行ILC算法计算控制输入;5)绘制期望轨迹与实际输出的对比图。该示例展示了ILC的基本实现过程,并指出实际应用可能需要更复杂的设计。代码演示了误差计算、控制输入更新和结果可视化等关键步骤。

unber的博客 200
上一篇: Qt容器类汇总说明
下一篇: 记一次面试:进程、线程与协程
coologic
博客等级 码龄7年 35粉丝 161原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值