Benjamin

静以修身,俭以养德,非澹薄无以明志,非宁静无以致远。
随笔 - 397, 文章 - 0, 评论 - 196, 引用 - 0
数据加载中……

C++基本概念之构造函数(constructor)(一)

1.功能:创建(build objects)对象,将一连串的随意的内存位变对象,也分配资源(memory, files, semaphores, sockets等),"ctor" 是构造函数(constructor)典型的缩写。
2.假定List是个类名,List x和 List x()的区别:前者声明了一个List对象,后者则是一个函数,返回List类型。
3.能否在一个构造函数中调用另一个构造函数?答案是否定的。
假设类Fro有两个构造函数Foo::Foo(char x)和Foo::Foo(char x,int y),那么下面的代码
class Foo {
 public:
   Foo(char x);
   Foo(char x, int y);
   ...
 };
 
 Foo::Foo(char x)
 {
   
...
   Foo(x, 0);  
// this line does NOT help initialize the this object!!to initialize a temporary(临时量)local object (not this), it immediately destructs  that temporary when control flows over,   
   
...
 }
也可以组合这两个构造函数,通过默认参数
class Foo {
 public:
   Foo(char x, int y=0);  // this line combines the two constructors
   
...
 };
如果没有默认参数可用,那么我可以共享公用代码在私有的Init函数中,如:
class Foo {
 public:
   Foo(char x);
   Foo(char x, int y);
   ...
 private:
   void init(char x, int y);
 };
 
 Foo::Foo(char x)
 {
   init(x, int(x) + 7);
   
...
 }
 
 Foo::Foo(char x, int y)
 {
   init(x, y);
   
...
 }
 
 void Foo::init(char x, int y)
 {
   
...
 }

不要尝试把它用在布局new(placemement new)中,有些人认为可以new(this) Foo(x, int(x)+7)在Foo::Foo(char)中,这是绝对错误的。它会影响对象的构建位(constructed bits)。
4.默认构造函数(default constructor )的参数可以使没有,也可以使默认的参数。如:
class Fred {
 public:
   Fred();   // Default constructor: can be called with no args
   
...
 }; 或
class Fred {
 public:
   Fred(int i=3, int j=5);   // Default constructor: can be called with no args
   
...
 };
5.建立对象数组时,哪个构造函数将被调用:
如果没有默认构造函数,那么创建对象数组将会出错。如:
class Fred {
 public:
   Fred(int i, int j);       assume there is no default constructor
   
...
 };
 
 int main()
 {
   Fred a[10];              
 ERROR: Fred doesn't have a default constructor
   Fred* p = new Fred[10];  
 ERROR: Fred doesn't have a default constructor
   
...
 }
如果用std::vector<Fred> ,则可以不用默认构造函数。如:
#include <vector>
 
 int main()
 {
   std::vector<Fred> a(10, Fred(5,7));   the 10 Fred objects in std::vector a will be initialized with Fred(5,7)
   
...
 }
也可以现实初始化数组:也可以用placement new来手工初始化数组元素
class Fred {
 public:
   Fred(int i, int j);       assume there is no default constructor
   
...
 };
 
 int main()
 {
   Fred a[10] = {
     Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7),  
// The 10 Fred objects are
     Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7)   
// initialized using Fred(5,7)
   };
   
...
 }

一般的我们用vector来替代数组。
6.构造函数中如何使用初始化列表(initialization lists)和赋值(assignment)
在构造中使用初始化列表比赋值更有效率,后者比前者多了一个临时变量,多了这个临时变量的创建和销毁的开销。但是在内置数据类型(int,float等)时,二者差别不大。
另一种情况是在构造中的成员对象会被以默认构造函数完整的构造,会分配一些缺省状态下的内存或是文件,这样如果在构造中如果使用表达式或复制失败,是没办法来释放资源或关闭文件的。
在下面情况下不易使用初始化列表:类有两个构造函数并且需要初始化这个数据成员按照不同的顺序,或是有两个数据成员自引用,或数据成员需要引用this对象,或者抛出一个异常之前要初始化这个this成员等。
7.构造函数可以使用this指针吗?可以,但是小心使用,甚至于在初始化列表中使用它。
可以使用的情况:构造函数的函数体(或构造函数所调用的函数)能可靠地访问基类中声明的数据成员和/或构造函数所属类里声明的数据成员。这是因为所有这些数据成员被保证在构造函数函数体开始执行时已经被完整的建立。
构造函数的函数体(或构造函数所调用的函数)不能向下调用被派生类重定义的虚函数。无论你如何调用虚成员函数:显式使用this指针(如,this->method()),隐式的使用this指针(如,method()),或甚至在this对象上调用其他函数来调用该虚成员函数,原因:在基类的构造函数执行期间,派生类对象还未产生。
下面的情况有时是可行的:如果传递 this 对象的任何一个数据成员给另一个数据成员的初始化程序,你必须确保该数据成员已经被初始化。他的优点是不依赖编译器,但是你必须知道一些语言规则(例如,基类子对象首先被初始化(如果有多重和/或虚继承,则查询这个次序!),然后类中定义的数据成员根据在类中声明的次序被初始化),如果不知道就不要使用这个this指针。
8.命名的构造函数法(Named Constructor Idiom):
作用就就是区分多个构造函数。
结构:把构造放到private或protected处,提供一个返回对象的public static 方法。每种不同的构造对象的方法都有一个这样的静态方法。例子:
class Point {
 public:
   Point(float x, float y);     // Rectangular coordinates
   Point(float r, float a);     
// Polar coordinates (radius and angle)
   
// ERROR: Overload is Ambiguous: Point::Point(float,float)
 };
 
 int main()
 {
   Point p = Point(5.7, 1.2);   
// Ambiguous: Which coordinate system?
   
...
 }
解决方法就是使用Named Constructor Idiom
#include <cmath>               // To get sin() and cos()
 
 class Point {
 public:
   static Point rectangular(float x, float y);      
// Rectangular coord's
   static Point polar(float radius, float angle);   
// Polar coordinates
   
// These static methods are the so-called "named constructors"
   
...
 private:
   Point(float x, float y);     
// Rectangular coordinates
   float x_, y_;
 };
 
 inline Point::Point(float x, float y)
   : x_(x), y_(y) { }
 
 inline Point Point::rectangular(float x, float y)
 { return Point(x, y); }
 
 inline Point Point::polar(float radius, float angle)
 { return Point(radius*cos(angle), radius*sin(angle)); }


int main()
 {
   Point p1 = Point::rectangular(5.7, 1.2);   // Obviously rectangular
   Point p2 = Point::polar(5.7, 1.2);         
// Obviously polar
   
...
 }

如果Point有派生类,构造就放在protected中。

posted on 2009-12-05 01:41 Benjamin 阅读(3096) 评论(0)  编辑 收藏 引用 所属分类: C/C++


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理