Effective C++学习第二天

1:确保对象被使用前已先被初始化,读取未初始化的值会造成不明确的行为,可能导致程序终止运行或者其他不可预期的现象;在C++中,当你使用C part of C++(C++中C语言部分的内容)且初始化可能导致运行期成本,那么就不会保证一定初始化,而non-C part of C++,系统却能保证被初始化。对于内置类型(C++定义算术类型(整型和浮点型)和空类型为内置类型),需要手动完成初始化;对于自定义类型,需要用构造函数完成相应的初始化工作;

2:区分赋值(assignment)操作和初始化(initialization)的行为,在实现相同功能的情况下尽可能的使用初始化的方式

      对象成员的初始化动作发生在进入构造函数之前,进入构造函数本体之前,成员变量自动调用自己的default构造函数完成默认初始化(不使用初始化列表的情况下),然后进入函数体执行对应的功能;

      针对上述情况,使用成员初始化列表(member initialization list)替换常规的赋值操作可以省去一个赋值操作的过程,直接实现以各个成员变量的实参拷贝构造给形参,初始化列表中应包含所有的成员变量;

      对于大多数类型而言,比起先调用default构造函数后调用copy assignment操作符,只调用一次copy构造函数是比较高效的,但对于内置类型,其初始化和赋值的成本相同,但为了一致性效果,都采用初值化列表的形式;

      如果成员变量是const或者references,那么就一定需要初值,而不能被赋值

      初始化列表的初始化顺序是按照成员变量的定义顺序进行初始化的,与初始化列表中变量如何排列无关;

    结论:初始化时,使用初始化列表可以使你的程序更加高效;当我们想要用default构造成员变量的时候,我们也可以指定成员初始列,只要指定初始化实参为空就行;

3:当classes中有多个构造函数时,每个构造函数有自己的成员初始列,那么这样就可能出现重复的初值列,这种情况下可以将初始值列中那些“赋值表现像初始化一样好”的成员变量,改用它们的赋值操作,并把这些赋值操作合并成一个函数(通常是private)来供所有的构造函数使用;

4:static对象,其寿命从被构造出来到程序结束为止。通常函数内的static对象称为local static对象,其他的static对象称为non-local static对象;

      问题:当某个编译单元内的某个非non-local static对象的初始化动作使用了另一个单元内的的某个non-local static对象,可能所用到的这个对象可能没有初始化,而且C++对这个初始化的顺序没有明确的定义;

     解决方法:采用singleton设计模式,将每个non-local static对象搬到自己的专属函数内(该对象在函数内被声明为static),然后然后函数返回一个引用指向这个static对象,然后使用者调用这些函数,而不直接去指涉这些对象。这个方法的好处在于:C++保证了在函数内的local static对象会在“这个函数调用期间”“首次遇上该对象定义式时”被初始化;代码如下:

class filesystem

{

public:std:size_t numberdisks() const;

};

extern filesystem tfs;//修改为filesystem&  tfs(){staticfilesystem fs;return fs;}

class directory

{

public:directory(params) {std::size_t disks=tfs.numberdisks();//修改为  std::size_t disks=tfs().numberdisks;

};

directory tempdir(params);//修改为directory& tempdir() {static directory td; return td;}

      修改之后,程序用户完全和以前一样使用,唯一不同的就是使用函数返回的是指向static对象的引用,而非static对象本身;

      对于singleton模式下的reference-returning函数比较简单,往往可以变成inline函数,但是对于多线程环境下会带来很大的麻烦;

      引申:编译单元是指产生单一目标文件的那些源码,基本上它是单一源码文件加上其所包含的头文件,平常写程序的一个文档就称为一个编译单元;

      结论:为了避免在对象初始化之前使用对象,需要做三件事情:1)手工初始化内置类型non-number对象;2)使用成员初始值列表对方对象的所有成分;3)在初始化次序不确定的情况下可以考虑singleton设计模式,也就是在处理跨编译单元的初始化次序时,用local static对象替换non-local static对象。

5:如果自己没有声明类中的函数,编译器会自动生成一个default构造函数、一个copy构造函数、一个copy assignment操作符和一个析构函数(non-virtual),并且这些函数都是public和inline的;

     编译器生成的copy构造函数和copy assignment操作符只是单纯地将来源对象的每一个non-static成员变量拷贝给目标对象;

     如果自己声明了一个构造函数,则编译器不再提供default构造函数;

     编译器生成的copy assignment操作符,行为和copy构造函数一样,当且仅当生成的代码合法且有意义时;否则编译器拒绝为class生成operator=,如成员变量中含有引用或者const变量时,则编译器不能为自动生成一个copy assignment操作符;

     编译器拒绝自动生成一个copy assignment构造函数的情况:1)内含reference成员变量;2)内含const变量;3)在base classes将copy assignment操作符声明为private;编译器拒绝为derive classes生成一个copy assignment操作符;

6:如果想要阻止某些函数被创建出来,声明这个函数,并将其权限设置为private,但是member函数和friend函数还是可以调用private函数;

         阻值member或者friend函数调用的方法,利用一个base class,将需要阻止的函数(private权限)放到base class内,我们所需要做的就是(private)继承base class;当我们的类要生成copy构造函数和copy assignment函数时,会调用base class类中的对应函数,这些调用会被编译器拒绝,因为函数的权限是private;

猜你喜欢

转载自blog.csdn.net/xx18030637774/article/details/80760486