`
weihe6666
  • 浏览: 428303 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

C++ 构造函数

    博客分类:
  • C++
阅读更多
最近看C++方面的书,让我更深入的理解了C++的构造函数。什么情况下调用默认构造函数,什么情况下调用拷贝构造函数以及什么情况下使用显示的初始化列表,初始化列表的初始化顺序,以及显示的初始化列表和拷贝构造函数的区别,为什么拷贝构造函数和赋值构造函数同时出现?

1.默认构造函数

如果我们没有定义自己的构造函数,编译时会自动调用默认构造函数来初始化类的对象。那么既然可以调用默认的构造函数,为何还要自己定义构造函数?我的理解是:如果想初始化类的数据成员为某些特定的值时,这时就需要自己定义构造函数,否则不需要。

2.拷贝构造函数

既然有自己定义的构造函数,为何还要用拷贝构造函数。但是当类的数据成员包含指针,即需要动态的申请内存时,这是就需要自己定义拷贝构造函数。因为如果没有拷贝构造函数动态的为每一个对象申请内存,而只是用默认的拷贝构造函数来初始化类的对象时,所有的类的对象的指针数据成员所指向的是同一块地址,也就是不同的指针但是指向的却是同一块内存,而没有开辟自己的内存,这样在程序结束时调用析构函数就会出现一个内存块多次释放的错误。

比如下面的例子:

class Matrix { 
public: 
   Matrix( int row, int col ) 
         : _row( row ), _col( col ) 
   { 
           // constructor allocates a resource 
           // note: no error checking is shown 
           _pmat = new double[ row * col ]; 
   } 

   ~Matrix() 
   { 
           // destructor frees the resource 
           delete [] _pmat; 
   } 

   // ... 

private: 
   int     _row, _col; 
   double *_pmat; 
};

int main()
{
  Matrix a(4,6);
  Matrix b = a;
  return 0;
}


Matrix a(4,6);利用自定义的构造函数初始化对象a,并为double*_pmat;动态申请了内存。
Matrix b = a;由于programm没有定义自己的拷贝构造函数,编译器会使用默认的拷贝构造函数,int     _row, _col; 都可以恰当的被初始化,可是double *_pmat; 的初始化就不同了,只是b._pmat = a._pmat,这样a._pmat和b._pmat指向同一块内存。当然他们在使用的时候没有问题,但是当编译器调用析构函数~Matrix() 时就会出现问题,先是b.~Matrix() 这是已经把a对象申请的地址释放了,但当调用a.~Matrix() 时,就会出现问题。

拷贝构造函数如何定义:

类名::类名(const 类名 & 对象)
{
}

例如:
Matrix::Matrix( const Matrix &rhs ) 
    : _row( rhs._row ), _col( rhs._col ) 
{  // create a "deep copy" of the array addressed by rhs._pmat 
   int elem_cnt = _row * _col; 
   _pmat = new double[ elem_cnt ]; 

   for ( int ix = 0; ix < elem_cnt; ++ix ]   //动态申请内存
         _pmat[ ix ] = rhs._pmat[ ix ]; 
} 



3.拷贝赋值构造函数

为何要使用赋值构造函数,已经有了默认构造函数、自定义构造函数、拷贝构造函数,为何还要拷贝构造函数。类也是一种类型,类似于int char,为了兼容C中的赋值操作符,比如 int a = 2.;int b; b = a; 类的对象也可以直接赋值,这就是赋值构造函数的作用。

但是赋值构造函数也有默认的构造函数,为何不用默认的构造函数,这个原因和拷贝构造函数类似,也是当类的成员函数需要动态申请内存时就需要使用自定义的赋值构造函数。

赋值构造函数如何定义:
类名 & 类名::operator=(const 类名 & rhs)
{
}
Matrix& Matrix:: 
operator=( const Matrix &rhs ) 
{ 
   if ( this != &rhs )   //防止自符值
   { 
        _row = rhs._row; _col = rhs._col; 
        int elem_cnt = _row * _col; 

        delete [] _pmat; 
        _pmat = new double[ elem_cnt ]; 
        for ( int ix = 0; ix < elem_cnt; ++ix ] 
              _pmat[ ix ] = rhs._pmat[ ix ]; 
   } 

   return *this; 
}; 


4.构造函数初始化列表

为何要使用初始化列表,直接在构造函数体内初始化数据成员不也一样吗?

构造函数初始化列表也成为显示的初始化数据成员,它不需要隐士的调用拷贝构造函数直接初始化,效率会高些,而在构造函数体内初始化数据成员则会隐士调用拷贝构造函数,效率会低些。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics