CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
不看会后悔的Windows XP之经验谈 简单快捷DIY实用家庭影院
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  C/C++ >  C语言

初始化列表!!

楼主superwt2001(嘿嘿)2003-01-03 23:24:48 在 C/C++ / C语言 提问

在C++的类的构造函数中初始化数据时,有时必须使用初始化列表,举一例说明这种情形。 问题点数:20、回复次数:9Top

1 楼qhgary(Gary)回复于 2003-01-04 00:28:55 得分 5

当该类中有一个数据成员是常量,当然还有其他情况Top

2 楼qhgary(Gary)回复于 2003-01-04 00:37:36 得分 5

设想你有一个类成员,它本身是一个类或者结构,而且只有一个带一个参数的构造函数。  
   
  class   CMember   {  
   
  public:  
   
          CMember(int   x)   {   ...   }  
   
  };  
   
  因为Cmember有一个显式声明的构造函数,编译器不产生一个缺省构造函数(不带参数),所以没有一个整数就无法创建Cmember的一个实例。  
   
  CMember*   pm   =   new   CMember;                 //   Error!!  
   
  CMember*   pm   =   new   CMember(2);           //   OK  
   
  如果Cmember是另一个类的成员,你怎样初始化它呢?你必须使用成员初始化列表。  
   
  class   CMyClass   {  
   
          CMember   m_member;  
   
  public:  
   
          CMyClass();  
   
  };  
   
  //必须使用成员初始化列表  
   
  CMyClass::CMyClass()   :   m_member(2)  
   
  {  
   
  •••  
   
  }  
   
  Top

3 楼dochang(佣兵)回复于 2003-01-04 02:58:56 得分 0

实际上无论怎样都要做初始化表这一步,如果你不写初始化表,编译器会自动给你的父类,和类中成员进行缺省初始化,构造函数体内部不是初始化成员部分Top

4 楼chinajiji(菜鸟叽叽)回复于 2003-01-04 12:39:26 得分 5

class   B   {  
  public:  
          B(int   i)   :   m_i(i)   {};  
  private:  
  int   m_i;  
  };  
   
  class   D   :   public   B   {  
  private:  
      const   int   m_i;  
      const   string   &strRef;  
  public:  
          D(int   i,   const   string   &str)   :   B(i),   m_i(i),   strRef(str)   {};  
  };  
  copy   constructor   也有类似的问题.  
   
  qhgary(Shining)   的说法不一定正确:  
  他说:  
   
  //必须使用成员初始化列表  
   
  CMyClass::CMyClass()   :   m_member(2)  
   
  {  
   
  •••  
   
  }  
  ///////////////////////////////////  
  我可以不用初始化例表,也可以完成:  
   
  CMyClass::CMyClass()    
   
  {  
    m_member   =   CMember(2);//能执行,只要CMember的operator=语意正确即可;  
  }  
  如果   m_member   是const或引用,这样做就不行了.Top

5 楼chinajiji(菜鸟叽叽)回复于 2003-01-04 14:03:32 得分 0

更正:  
  我没看见qhgary(Shining)说过"CMember"没有default   constructor!  
  如果这样,就必须在constructor   list中初始化CMember了.Top

6 楼qhgary(Gary)回复于 2003-01-04 17:23:23 得分 3

to   chinajiji(菜鸟叽叽)    
  你这样做是十分危险的,虽然CMember有default   copy   constructor,可是你怎么知道CMember里面没有动态分配的空间?如果有就会出问题了。  
  所以不能采取你的做法。Top

7 楼web_spider(蓦然回首,那人却在、灯火阑珊处。)回复于 2003-01-04 17:26:33 得分 2

看看名家之言吧。Effective   c++(csdn上的朋友lostmouse译):  
   
  条款12:   尽量使用初始化而不要在构造函数里赋值  
   
  看这样一个模板,它生成的类使得一个名字和一个t类型的对象的指针关联起来。  
   
  template<class   t>  
  class   namedptr   {  
  public:  
      namedptr(const   string&   initname,   t   *initptr);  
      ...  
   
  private:  
      string   name;  
      t   *ptr;  
  };  
   
  (因为有指针成员的对象在进行拷贝和赋值操作时可能会引起指针混乱(见条款11),namedptr也必须实现这些函数(见条款2))  
   
  在写namedptr构造函数时,必须将参数值传给相应的数据成员。有两种方法来实现。第一种方法是使用成员初始化列表:  
   
  template<class   t>  
  namedptr<t>::namedptr(const   string&   initname,   t   *initptr     )  
  :   name(initname),   ptr(initptr)  
  {}  
   
  第二种方法是在构造函数体内赋值:  
   
  template<class   t>  
  namedptr<t>::namedptr(const   string&   initname,   t   *initptr)  
  {  
      name   =   initname;  
      ptr   =   initptr;  
  }  
   
  两种方法有重大的不同。  
   
  从纯实际应用的角度来看,有些情况下必须用初始化。特别是const和引用数据成员只能用初始化,不能被赋值。所以,如果想让namedptr<t>对象不能改变它的名字或指针成员,就必须遵循条款21的建议声明成员为const:  
   
  template<class   t>  
  class   namedptr   {  
  public:  
      namedptr(const   string&   initname,   t   *initptr);  
      ...  
   
  private:  
      const   string   name;  
      t   *   const   ptr;  
  };  
   
  这个类的定义要求使用一个成员初始化列表,因为const成员只能被初始化,不能被赋值。  
   
  如果namedptr<t>对象包含一个现有名字的引用,情况会非常不同。但还是要在构造函数的初始化列表里对引用进行初始化。还可以对名字同时声明const和引用,这样就生成了一个其名字成员在类外可以被修改而在内部是只读的对象。  
   
  template<class   t>  
  class   namedptr   {  
  public:  
      namedptr(const   string&   initname,   t   *initptr);  
      ...  
   
  private:  
      const   string&   name;                               //   必须通过成员初始化列表  
                                                                          //   进行初始化  
   
      t   *   const   ptr;                                         //   必须通过成员初始化列表  
                                                                          //   进行初始化  
  };  
   
  然而前面最初的类模板不包含const和引用成员。即使这样,用成员初始化列表还是比在构造函数里赋值要好。这次的原因在于效率。当使用成员初始化列表时,只有一个string成员函数被调用。而在构造函数里赋值时,将有两个被调用。为了理解为什么,请看在声明namedptr<t>对象时都发生了些什么。  
   
  对象的创建分两步:  
  1.   数据成员初始化。(参见条款13)  
  2.   执行被调用构造函数体内的动作。  
   
  (对有基类的对象来说,基类的成员初始化和构造函数体的执行发生在派生类的成员初始化和构造函数体的执行之前)  
   
  对namedptr类来说,这意味着string对象name的构造函数总是在程序执行到namedptr的构造函数体之前就已经被调用了。问题只在于:string的哪个构造函数会被调用?  
   
  这取决于namedptr类的成员初始化列表。如果没有为name指定初始化参数,string的缺省构造函数会被调用。当在namedptr的构造函数里对name执行赋值时,会对name调用operator=函数。这样总共有两次对string的成员函数的调用:一次是缺省构造函数,另一次是赋值。  
   
  相反,如果用一个成员初始化列表来指定name必须用initname来初始化,name就会通过拷贝构造函数以仅一个函数调用的代价被初始化。  
   
  即使是一个很简单的string类型,不必要的函数调用也会造成很高的代价。随着类越来越大,越来越复杂,它们的构造函数也越来越大而复杂,那么对象创建的代价也越来越高。养成尽可能使用成员初始化列表的习惯,不但可以满足const和引用成员初始化的要求,还可以大大减少低效地初始化数据成员的机会。  
   
  换句话说,通过成员初始化列表来进行初始化总是合法的,效率也决不低于在构造函数体内赋值,它只会更高效。另外,它简化了对类的维护(见条款m32),因为如果一个数据成员以后被修改成了必须使用成员初始化列表的某种数据类型,那么,什么也不用变。  
   
  但有一种情况下,对类的数据成员用赋值比用初始化更合理。这就是当有大量的固定类型的数据成员要在每个构造函数里以相同的方式初始化的时候。例如,这里有个类可以用来说明这种情形:  
   
  class   manydatambrs   {  
  public:  
      //   缺省构造函数  
      manydatambrs();  
   
      //   拷贝构造函数  
      manydatambrs(const   manydatambrs&   x);  
   
  private:  
      int   a,   b,   c,   d,   e,   f,   g,   h;  
      double   i,   j,   k,   l,   m;  
  };  
   
  假如想把所有的int初始化为1而所有的double初始化为0,那么用成员初始化列表就要这样写:  
   
  manydatambrs::manydatambrs()  
  :   a(1),   b(1),   c(1),   d(1),   e(1),   f(1),   g(1),   h(1),   i(0),  
      j(0),   k(0),   l(0),   m(0)  
  {   ...   }  
   
  manydatambrs::manydatambrs(const   manydatambrs&   x)  
  :   a(1),   b(1),   c(1),   d(1),   e(1),   f(1),   g(1),   h(1),   i(0),  
      j(0),   k(0),   l(0),   m(0)  
  {   ...   }  
   
  这不仅仅是一项讨厌而枯燥的工作,而且从短期来说它很容易出错,从长期来说很难维护。  
   
  然而你可以利用固定数据类型的(非const,   非引用)对象其初始化和赋值没有操作上的不同的特点,安全地将成员初始化列表用一个对普通的初始化函数的调用来代替。  
   
  class   manydatambrs   {  
  public:  
      //   缺省构造函数  
      manydatambrs();  
   
      //   拷贝构造函数  
      manydatambrs(const   manydatambrs&   x);  
   
  private:  
      int   a,   b,   c,   d,   e,   f,   g,   h;  
      double   i,   j,   k,   l,   m;  
   
      void   init();                 //   用于初始化数据成员  
                                               
  };  
   
  void   manydatambrs::init()  
  {  
      a   =   b   =   c   =   d   =   e   =   f   =   g   =   h   =   1;  
      i   =   j   =   k   =   l   =   m   =   0;  
  }  
   
  manydatambrs::manydatambrs()  
  {  
      init();  
   
      ...  
   
  }  
   
  manydatambrs::manydatambrs(const   manydatambrs&   x)  
  {  
      init();  
   
      ...  
   
  }  
   
  因为初始化函数只是类的一个实现细节,所以当然要把它声明为private成员。  
   
  请注意static类成员永远也不会在类的构造函数初始化。静态成员在程序运行的过程中只被初始化一次,所以每当类的对象创建时都去“初始化”它们没有任何意义。至少这会影响效率:既然是“初始化”,那为什么要去做多次?而且,静态类成员的初始化和非静态类成员有很大的不同,这专门有一个条款m47来说明。  
  Top

8 楼micropentium6(小笨|曾经的美好)回复于 2003-01-04 19:05:40 得分 0

继承类肯定要通过初始化列表调用基类构造函数;类中有类成员也要;有常量和引用成员变量时也必须使用初始化列表,还有什么?    
  Top

9 楼allen88680764(也不知为什么,爱上了编程,呵呵)回复于 2003-01-04 19:31:27 得分 0

我看见一个例子很有趣:  
   
  class   X{  
    int   i;  
  public:  
    X(int   I=0):i(I){}  
  };Top

相关问题

  • 用初始化列表初始化变量的小问题
  • 构造函数和初始化列表
  • vsflexgrid怎样初始化下拉列表?
  • 初始化器列表的问题!
  • 什么叫初始化参数列表?
  • 成员初始化列表中,应该都有哪些呀?
  • 如何用javascript初始化下拉列表?
  • Dialog,怎样使用构造函数初始化列表?
  • 请问要用什么函数来初始化列表框呢?
  • !!!!!!!!!!100分求解(struts下拉列表初始化数据)!!!!!!!!!!

关键词

  • c++
  • 函数
  • 数据
  • 指针
  • 拷贝
  • 执行
  • 初始化
  • 构造
  • manydatambrs
  • namedptr

得分解答快速导航

  • 帖主:superwt2001
  • qhgary
  • qhgary
  • chinajiji
  • qhgary
  • web_spider

相关链接

  • C/C++ Blog
  • C/C++类图书
  • C/C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号
世纪乐知(北京)网络技术有限公司 提供技术支持
Copyright © 2000-2008, CSDN.NET, All Rights Reserved
GongshangLogo