Effective C++ 读书笔记

item2:
宏坑多,尽量用const和enum。类中定义的 static const int val = 0; 只是声明,如果要取它的地址必须在实现文件中定义,定义不能有赋值,因为声明里面已经赋值了。

item3:

const std::vector<int>::iterator iter    // acts like a T * const
std::vector<int>::const_iterator citer // acts like a const T *

重载运算操作符的返回值最好设成const。函数的const和非const是不一样的,可以重载。重载[]操作符最好重载两个版本。可以用下面这个方式来减少两个重载函数的重复代码

const char& operator[](std::size_t position)const
{
    ...
    return text[position];
}
char& operator[](std::size_t position)
{
    return const_cast<char&>(
        static_cast<const TextBlock&>(*this)[position];
    )
}

const成员函数不能保护成员指针指向的东西,如果要保护成员指针指向的东西,要用template写一个wrapper类,重载->操作符(具体看看二楼答案)。
用mutable声明的成员变量即使在const对象里面也能被修改。

item4:
用初始化列表来初始化成员函数效率高。
用函数中的static对象来解决一个全局对象的初始化依赖另外一个文件的全局对象,而我们不能控制全局对象初始化顺序的问题,为了效率可以把这个函数inline。

item6:禁止拷贝的方法:将拷贝构造函数和赋值操作符设为private防止类外调用,不实现它防止friend调用,将它们声明在基类可以把错误信息从链接期移到编译期,可以直接继承boost的noncopyable。不过如果因此而多重继承,noncopyable这个空基类的空间优化可能会失去。

item7:凡是用了虚函数的类一定要有虚析构函数。反正用了虚函数都要插多一个vptr,多个虚析构也不会增加成本。
std::string, vector, list, set, tr1::unordered_map没有虚构造,所以不要继承他。

item8:不要在析构函数里面抛异常。如果遇到必须抛的情况,要处理在析构过程中抛异常的情况,应该留接口给类用户在析构抛异常的时候处理好析构。这种做法很丑,没有优雅解决办法的根源是,析构函数的语义就假设析构过程一定是没问题的。

item9:不要在构造函数和析构函数里面调用虚函数,因为那个时候vptr指向基类的虚函数表。如果构造函数和析构函数是通过另外一个函数来调用虚函数,这种错误的做法不会被编译器检查出来。

item10:赋值操作符要返回*this引用,因为这是赋值运算符对于内置类型的语义。

item11:

Object& Object::operator=(const Object& rhs)
{
    Object *temp = p_obj;
    p_obj = new Object(*rhs.p_obj);
    delete temp;
    return *this;
}
Object& Object::operator=(const Object& rhs)
{
    Object temp(rhs);
    swap(temp);
    return *this;
}
Object& Object::operator=(const Object rhs)
{
    swap(rhs);
    return *this;
}

注意swap是自己定义的成员函数。上面这三种做法能够防止自己给自己赋值,因为delete放在后面,也可以防止new抛异常以后,p_obj指向空资源。

item12:记得在拷贝构造函数和赋值操作符里面调用 成员对象和基类 的拷贝构造函数和赋值操作符,不然编译器会给成员对象和基类调用默认拷贝构造函数而且不调用他们的赋值操作符。拷贝构造函数和赋值操作符有相同代码的时候,不应该让他们互相调用,而是将重复代码放在第三个函数里面。

item13:
用构造函数和析构函数的语义来管理资源,以防止多处return或者函数中间抛异常而引起资源泄漏的情况(RAII,Resources Acquisition Is Initialization)。
auto_ptr不能用于STL容器,因为如果拷贝装着auto_ptr的容器,原来的容器里面的auto_ptr全部被设成null。但shared_ptr就没问题。但两个智能指针都不能用于数组。

item14:对于管理资源的类,要好好考虑他被拷贝的情况。可以禁止拷贝,可以用引用技术。可以把资源也拷贝过去。可以改变所有权(像auto_ptr)。

item15:
有时候一些函数只能以裸指针作为参数。这个时候可以像auto_ptr和shared_ptr那样提供一个get方法,也可以定义隐式类型转换。
顺带一提,造智能指针的时候有应该重载指针的应有的api,这些api有 * -> ->*

item17:
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
当该statement以这样的顺序执行:1 new Widget. 2 运行priority() 3 把new出来的对象转换成shared_ptr然后调用shared_ptr的拷贝构造函数,而priority又抛异常,那new出来的资源就泄漏了。所以应该把new出来的资源交给智能指针的语句单独放在一行里面。

item18: 如果接口容易用错(例如传错参数),尝试用C++内置的类型检查让编译器来为我们检查这些错误(引入新类型,用const)。尽量让接口保持一致(命名一致,同名函数功能相似,重载操作符行为与内置的一致)。

item22:书中说把成员变量设成private。其实我觉得这不重要,关键还是接口应该设计得够简单而且不那么需要改来改去。

item23:不要把跟类有关的函数都塞进类里面。类应该紧凑,这样写类的时候脑子不会爆掉。

item24:像封装数学运算,由于要支持两种类型的二元运算,所以运算函数不能是成员函数,因为这样会限制实现二元运算的交换率。

item25:如果要定制std::swap,最好像STL容器那样先定义一个public的swap为成员函数,然后std命名空间下定义一个swap来调用这个成员函数swap。如果这个swap也需要模板参数,由于模板函数不支持partial specialization, 所以我们应该重载swap函数,而且要把非成员版本放在该类的命名空间下(因为不能在std里面加template)

template<typename T>
void swap(Widget<T> &a, Widget<T>&b) // 这里原来是 T &a, T & b
{
a.swap(b);
}

item26:变量应该在用的时候才定义。

item27:尽可能不用cast。即使用也尽可能用C++新的cast。注意不能通过下面的方式调用基类函数,因为cast会产生新的临时对象,函数调用不会在当前对象中起作用

static_cast<Base>(*this).memfun();

item28:对象方法返回一个指向对象内部成员的指针,引用,迭代器时,要小心外部可以通过这个对象方法来修改这些成员(可以通过返回const来解决),更要小心临时对象调用这个函数而让一个外部指针,引用,迭代其指向一个被析构对象的内部成员。

item29:非常精华和重要的有一节!
如果一个函数调用到一半抛异常后,Exception-safe有三个层次的保证:
1:程序仍然处于一个valid的状态。但是具体处于什么常态不清楚。
2:程序处于未调用该函数前的状态。
3:程序压根不抛异常。
如果资源在函数开头分配,在函数结束时释放,应该用RAII。如果要将某个指针指向一个新资源,析构原来的资源,可以像shared_ptr那样用reset。还可以用copy and swap,或者保证先分配好资源并且配置妥当,再释放原来的资源(这样即使分配资源或者配置状态失败的时候,不会改变原来的状态)。

struct PMImpl
{
std::shared_ptr<Image> bgImage;
int imageChanges;
};
class PrettyMenu
{
...
private:
Mutex mutex;
std::shared_ptr<PMImpl> pImpl;
};
void PrettyMenu::changeBackground(std::istream & imagSrc)
{
using std::swap;
Lock m1(&mutex);
std::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));
pNew->reset(new Image(imagSrc));
++pNew->imageChanges;
swap(pImpl, pNew);
}
基本的想法是,如果资源分配好,变量也配置好,就交换整个状态。

如果一个函数不是Exception-safe,那么它在抛异常以后,状态的不确定就导致整个程序的状态不确定。

item30:调用虚函数的函数不能是inline。

item31: "pimpl idiom"(把一个类分成两个,一个负责实现,一个通过提供指向实现类对象的private指针和调用实现类方法的public函数提供接口。为的是防止一个头文件修改导致n个文件要重新编译)的做法真的好丑。不过,当一个类仅仅用于组合其它类的话,尽量用指针确实是个不错的选择。
在声明函数时,即使对象以值传递的方式传给函数或者从函数返回,这个文件也不需要该类定义,只需要类声明。因为是函数声明而不是实现,不需要知道类的大小。
三种方式减少编译文件之前的依赖:
1 如果可以,在头文件中用 指针或者引用。
2 在头文件中进可能用类声明,而不是类定义。
3 将声明和定义类的放在不同的头文件里面。
可以使用工厂函数返回对象指针可以减少编译文件间的依赖。

item32:继承的语义是,子类拥有任何基类的行为。有时候会发现子类不应该拥有基类某个方法或者在完成一项任务的时候应该拥有与基类不一样的函数。这不是设计得好不好的问题,而是继承的语义本来就没那么通用。

item33:可以在public: 里面using Base::memfun; 来让被自己重载函数隐藏的基类同名函数重见天日。

item34:纯虚函数的语义是定义通用接口(子类必须自己定义行为)。虚函数的语义是定义通用接口还有默认行为。非虚函数的语义是子类不应该改变的行为。
纯虚函数可以定义,但是子类必须显示调用(Base:memfun())。这种用法可以用来提供一个通用接口和默认行为,但是使用默认行为的时候必须显示掉用,这样又可以提醒需要自定义行为的子类重新定义该方法。

item35:
1 基类不直接定义public的虚函数,而是把它设为private,而让一个public的非虚函数调用这个虚函数。好处是通过在public的非虚函数里面可以放一些设置配置调用环境的代码来保证子类在调用这个虚函数时处于正确的调用环境中。
2 将部分逻辑写在类外的一个函数里面,在构造函数中通过传入这个函数的方式,能让没一个类的对象有不同的行为。而且可以在运行期改变对象的行为。或者用boost的function和bind。
3 跟2差不多,只不过将类外的函数写进一个用类包裹的继承体系里面(喔,类也能这么灵活。不过相比与lambda和boost的function,这种做法就显得有点臃肿了)。

item36:不要重定义基类的非虚函数。

item37:不要在改变虚函数里面用默认参数。因为这里有个陷阱,一个基类指针在调用子类虚函数的时候会用基类的默认参数!

item39:private继承表达的是以什么类来实现(只重用实现,不重用接口)。通过将一个虚函数放进内部类里面,子类就不能修改这个虚函数(子类是可以override基类的private虚函数的)。private继承空基类的时候,会有empty base optimization(EBO), 这样编译器不会为空类插入一个char还有padding。private继承还可以让子类override基类虚函数还有调用基类protected的成员。

item40:从对象模型来看,要实现多重继承和虚拟继承的语义,坑实在是太多了。指针要转来转去,运行的成本很大。如果不在意效率,通过public继承接口和private继承实现确实很方便。

item41:类与模板都支持接口和多态(虽然我脑子里的模板多态指static polymorphism)。模板的implicit接口真的非常强大,特别是加上C++11的auto(好吧,这对动态语言来说再平常不过)。

item42:nested dependent name在默认情况下不会被编译器认为是类型,而是一个类中的成员,因此需要在类型前面加typename说明。但是他不能用于存在与继承列表和初始化列表的基类的nested dependent name。

item43:在继承一个模板基类的时候(同时子类也是模板类),由于这个模板基类可能存在specialization,对于不同的模板参数,基类的实现可能完全不一样!一个函数可以在general的实现有,在specialization的版本没有。如果子类调用了这个函数(直接使用memfun();),编译器会报错说没有这个函数(因为可能真的没有)。解决方法有三个:
1 把memefun(); 改成 this->memfun(); 这样编译器会假设有,如果没有,编译器是在模板被instantiated 的时候报错。
2 像item33那样 using Base<T>::memfun(); 他让编译器在instantiation之前就去搜索这个函数。
3 把memfun(); 改成Base<T>::memfun(); 注意这种做法会让虚函数失效。

item44:模板中与模板参数无关的代码随着各种版本instantiate,会让编译出来的程序膨胀。解决办法是可以把这些参数放进函数里面。是否会让程序变快变慢要看实际。

item45: 非常tricky

template<typename T>
class SmartPtr
{
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other):heldPtr(other.get())
{...}
T* get()const{ return heldPtr; }
private:
T *heldPtr;
};
这样,只有U版本的heldPtr能够隐式转换成T版本的heldPtr,编译才会通过,这样就可以通过内置的隐式类型转换,让自己构造的类也能隐式类型转换。这种做法可以用于赋值操作符,拷贝构造函数,也可以用于类型转换操作符。不过当T 和 U一样的时候,编译器不会造一个普通赋值操作符,拷贝构造函数!需要自己重新写一个。

item46:
当不用模板的时候(模板函数加上模板类),在调用参数的时候参数可以通过构造函数隐式类型转换来得到函数所需要的参数类型。但是在模板参数类型推导的时候,编译器不能让通过构造函数的隐式类型转换发生。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容