C++primer-第14章-类的初始化,赋值及析构

来源:互联网 发布:张北云计算 编辑:程序博客网 时间:2024/06/08 12:11

(1)显式初始化表有两个主要缺点:
   ---它只能被应用在所有数据成员都是公有类的对象上(即初始化表不支持使用数据封装和抽象数据类型)
   ---它要求程序员干涉,增加了意外和错误的可能性.
 当然它也有优点:
   ---在某些应用中用常量初始化大型数据结构比较有效.
   ---显式初始化可以在装载时刻完成,从而节省了构造函数启动的开销,尤其是全局对象.
(2)类的构造函数不能指定返回类型,甚至void也不行
(3)调用构造函数的形式:
 ---- Account acct1("Anna Press");           //推荐使用的方式.
 ---- Account acct2 = Account("Anna Press");
 ---- Account acct3 = "Anna Press";         //当有两个以上的实参时,不能使用这种方式
 ---- Account *pacct = new Account("Anna Press");

(4)只有当没有构造函数或声明了缺省构造函数时,我们才能不指定实参集来定义类.
(5)如果类声明了一个或者多个构造函数,类对象就不能被定义为不调用任何构造函数的实例.尤其是,如果一个类声明了包含多个参数的构造函数,但没有声明缺省构造函数,则每个类
   对象的定义都必须提供所需的实参.
(6)成员初始化表只能在构造函数定义中被指定,而不是在其声明中.
(7)构造函数不能用const或volatile关键字声明,但是这并不意味const和volatile类对象不能用构造函数初始化,而是说,被应用到类对象上的适当的构造函数与对象是const,非const或volatile
   无关.只有当构造函数执行完毕.类对象已经初始化的时候,该类对象的常量性才被建立起来.一旦析构函数被调用,常量性就消失.
(8)缺省情况下,单参数构造函数(或者有多个参数,除了第一个参数外,其他都有缺省实参)被用作转换操作符.
   void print(const Account &acct);

   int main()
   {
         //....
        print("opps");
  //....
   }
   则print()的调用理,Account构造函数被编译器隐式地应用,以便把一个文字字符串转换成一个Account对象,尽管这种转化在这种情况下并不合适.
   explicit修饰符通知编译器不要提供隐式转换.
(9)缺省构造函数是指不需要用户指定实参就能够被调用的构造函数,这并不意味着它不能接受实参,只意味着构造函数的每个实参都有一个缺省值与之关联.
(10)Account acct;
   ---定义了缺省构造函数,它被应用到acct上.
   ---定义了缺省构造函数,但不是公有的.acct的定义被标记为编译时刻错误:main()没有访问权限.
   ---没有定义缺省构造函数,但是定义了一个或者多个要求实参的构造函数.acct的定义被标记为编译时刻错误:实参太少
   ---没有定义缺省构造函数,也没有定义其他构造函数.该定义是合法的.acct没有被初始化,没有调用任何构造函数.
(11)构造函数的可访问性由其声明所在的访问区来决定.我们可以通过把相关的构造函数放在非公有访问区内,从而限制或显式禁止某写形式的对象创建动作.
(12)用一个类初始化另一个对象被缺省按成员初始化.在概念上讲,一个类对象向该类的另一个对象做拷贝是通过依次拷贝每个非静态数据成员来实现的.
    如果定义了拷贝构造函数,则在用一个类对象初始化该类另一个对象时,它就会被调用.
(13)拷贝构造函数有一个指向类对象引用作为形式参数.
(14)Account acct1;
    Account acct2(acct1);
    编译器判断是否为Account类声明了一个显式的拷贝构造函数.
    ---如果声明了拷贝构造函数,并且可以访问,则调用它;
    ---如果声明了拷贝构造函数,但是不可访问,则acct2的定义就是一个编译时刻错误.
    ---如果没有声明拷贝构造函数的实例,则执行缺省的按成员初始化.
如果我们后来引用或去掉了一个拷贝构造函数的声明,则用户程序无需改变,但是需要重新编译.
(15)析构函数是一个特殊的由用户定义的成员函数,该类的对象离开了它的域,或者delete表达式应用到一个该类的对象的指针上时,析构函数会自动被调用.
(16)析构函数不放回任何值,也没有任何参数,所以它也不能被重载.
(17)析构函数不会复位数据成员的值,尽管这样做也没错,但是没有必要这样做,因为与这些成员相关联的存储区将被归还.
(18)并不是所有时候析构函数都是必需的.如果一个类的数据成员是按值存储的,则无需析构函数.析构函数主要用于放弃在类对象的构造函数或生命期内获得的资源,如释放
    互斥锁或删除由操作符new分配的内存.同时,析构函数不局限在放弃资源上,一般地,析构函数可以执行"类设计者希望在最后一次使用对象之后执行的任何操作".
(19)(P579)析显式的析构调用部分
(20)定义一个含有16个Account对象的数组,且每个元素依次用Account缺省构造函数初始化,则Account pooh_pals[] ={"Piglet","Eeyore","Tigger"};
  如果希望指定多个实参,则需要使用完整的构造函数语法.例如:
  Account pooh_pals[] = {
                         Account("Piglet",1000.0),
    Account("Eeyore",1000.0),
    Account("Tigger",1000.0)
                 };
(21)如果希望通过new表达式分配数组,则类必须提供一个缺省的构造函数或不提供构造函数.
(22)(P583)堆数组的初始化
(23)类对象的vector,vector< pointer > vec(5);
元素的初始化过程:
   ---创建一个底层次类类型的临时变量,在其上应用该类的缺省构造函数;
   ---在vector的每个元素上依次应用拷贝构造函数,用临时类对象的拷贝初始化每个类对象;
   ---删除临时类对象
尽管最终的结果等同于Point pa[5];但是初始化vector代价比较大:临时对象的构造函数和析构,以及拷贝构造函数往往比缺省构造函数计算上更复杂.
(24)构造函数的执行过程被分为两个阶段:隐式或显式初始化阶段,以及一般的计算阶段.计算阶段由构造函数体内的所有语句构成.在计算阶段,数据成员的设置被认为是赋值,而不是
    初始化,没有清楚地认识到这个区别是程序错误和低效的常见源泉.
(25)对于类对象,在初始化和赋值之间的区别是巨大.成员类对象应该总是在成员初始化表中被初始化,而不是在构造函数体内被赋值.
(26)对于非类成员的初始化或赋值,除了两个例外,两者在结果和性能上都是等价的.即,更受欢迎的实现是用成员初始化表.
    两个例外是:
     ----任何类型的const和引用数据成员,const和引用数据成员也必须是在成员初始化表中被初始化.否则会产生编译时刻错误.
     当构造函数体开始执行时,所有const和引用的初始化必须都已经发生.
(27)每个成员在成员初始化表中只能出现一次,初始化的顺序不是由名字在初始化表中的顺序决定的,而是由成员在类中被声明的顺序决定的.
(28)"用一个类对象初始化该类的另一个对象"发生在下列程序情况下:
  ---用一个类对象显式地初始化另一个类对象
  ---把一个类对象作为实参传递给一个函数
  ---非空顺序容器类型的定义
  ---把一个类对象插入到一个容器类型中.
(29)当按成员初始化时,如果一个类的数据成员是一个指向堆内存的指针,并且这块内存将由该类的析构函数删除,则产生严重的错误.解决办法就是提供拷贝构造函数,另外的替代方案是:
    ---把拷贝构造函数声明为私有的,这可以防止按成员初始化发生在程序的任何一个地方.
    ---通过有意不提供一个定义,可以防止在类的成员函数和友元中出现按成员初始化.但是,不提供定义,任何试图调用拷贝构造函数的动作虽然在编译系统中是合法的,但是会产生链接错误.
(39)Account的成员_name是string类类型对象.
    //这里不太对
    inline Account::Account(const Account &rhs)
    {
 _name = rhs._name;
        _balance = rhs._balance;
        _acct_nmbr = get_unique_acct_nmbr();
    }
   该实现不完全正确是因为没有区分开初始化和赋值.结果,调用的不是string拷贝构造函数,而是在隐式初始化阶段调用了缺省的string构造函数,并且在构造函数体内调用了string拷贝赋值操作符.
  修正为:
   inline Account::Account(const Account &rhs)
      :_name( rhs._name )
    {
        _balance = rhs._balance;
        _acct_nmbr = get_unique_acct_nmbr();
    }
(40)按成员赋值所处理的是,用一个类对象向该类的另一个对象的赋值操作.其机制基本上与缺省的按成员初始化相同,但是它利用了一个隐式的拷贝构造函数来取代拷贝构造函数.
(41)一般来说,按成员初始化对于一个类不适合,则缺省的按成员赋值也不适合.
(42)当一个类对象赋值给该类的另一个对象时:
   newAccount = oldAccount;
  下面几个步骤:
  ---检查该类,判断它是否提供了一个显式的拷贝构造赋值操作符;
  ---如果是,则检查访问权限,判断是否在这个程序部分它可以被调用;
  ---如果不能被调用,则会产生一个编译时刻错误.否则,调用它执行赋值操作.
  ---如果该类没有提供显式的拷贝赋值操作符,则执行缺省按成员赋值.
  ---在缺省按成员赋值下,每个内置类型或复合类型的数据成员被赋值给相应的成员.
  ---对于每个类成员对象,递归执行1到6步,直到所有内置或复合类型的数据成员都被赋值.
(43)(P600)的效率问题. 

原创粉丝点击