C++拷贝构造函数的几个细节

 

C++拷贝构造函数的几个细节(转自:http://grantren.javaeye.com/blog/43289)

一 拷贝构造函数是C++最基础的概念之一,大家自认为对拷贝构造函数了解么?请大家先回答一下三个问题:

1. 以下函数哪个是拷贝构造函数,为什么?

  1. X::X(const X&);   
  2. X::X(X);   
  3. X::X(X&, int a=1);   
  4. X::X(X&, int a=1, b=2);  

 2. 一个类中可以存在多于一个的拷贝构造函数吗?

3. 写出以下程序段的输出结果, 并说明为什么? 如果你都能回答无误的话,那么你已经对拷贝构造函数有了相当的了解。

  1. #include    
  2. #include    
  3.   
  4. struct X {   
  5.   template<typename T>   
  6.   X( T& ) { std::cout << "This is ctor." << std::endl; }   
  7.   
  8.   template<typename T>   
  9.     X& operator=( T& ) { std::cout << "This is ctor." << std::endl; }   
  10. };   
  11.   
  12. void main() {   
  13.   X a(5);   
  14.   X b(10.5);   
  15.   X c = a;   
  16.   c = b;   
  17. }  

 

解答如下:

1. 对于一个类X,如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数. 

  1. X::X(const X&);  //是拷贝构造函数   
  2. X::X(X&, int=1); //是拷贝构造函数  

 2.类中可以存在超过一个拷贝构造函数, 

  1. class X {      
  2. public:      
  3.   X(const X&);      
  4.   X(X&);            // OK   
  5. };  

注意,如果一个类中只存在一个参数为X&的拷贝构造函数,那么就不能使用const X或volatile X的对象实行拷贝初始化.

  1. class X {   
  2. public:   
  3.   X();   
  4.   X(X&);   
  5. };   
  6.     
  7. const X cx;   
  8. X x = cx;    // error   

如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数.
这个默认的参数可能为X::X(const X&)或X::X(X&),由编译器根据上下文决定选择哪一个.

默认拷贝构造函数的行为如下:
 默认的拷贝构造函数执行的顺序与其他用户定义的构造函数相同,执行先父类后子类的构造.
 拷贝构造函数对类中每一个数据成员执行成员拷贝(memberwise Copy)的动作.
 a)如果数据成员为某一个类的实例,那么调用此类的拷贝构造函数.
 b)如果数据成员是一个数组,对数组的每一个执行按位拷贝.
 c)如果数据成员是一个数量,如int,double,那么调用系统内建的赋值运算符对其进行赋值.

 

3.  拷贝构造函数不能由成员函数模版生成. 

  1. struct X {   
  2.     template<typename T>   
  3.     X( const T& );    // NOT copy ctor, T can't be X   
  4.   
  5.     template<typename T>   
  6.     operator=( const T& );  // NOT copy ass't, T can't be X   
  7. };   
  8.   

原因很简单, 成员函数模版并不改变语言的规则,而语言的规则说,如果程序需要一个拷贝构造函数而你没有声明它,那么编译器会为你自动生成一个. 所以成员函数模版并不会阻止编译器生成拷贝构造函数, 赋值运算符重载也遵循同样的规则.(参见Effective C++ 3edition, Item45)


二 针对上面作者的讨论,理解更深了,但是下面我还是会给出一个一般的标准的实现和注意事项:

#include <iostream>
#include <cstring>
using namespace std;

struct Test1
{
 Test1(){}
 Test1(int i){ id=i; }
 Test1 (const Test1 &test)
 {
  id=test.id;
 }
 Test1& operator=(const Test1& test)
 {
  if (this == &test)
  {
   return *this;
  }
  
  id=test.id;
  return *this;
 }
 int id;
};

class Test2
{
public:
 Test2(){ m_pChar=NULL; }
 Test2(char *pChar) { m_pChar=pChar; }
 
 Test2(int num)
 {
  m_pChar=new char[num];
  for (int i=0; i<num; ++i)
  {
   m_pChar[i]='a';
   m_pChar[num-1]='\0';
  }
 }
 Test2(const Test2& test)
 {
  char *pCharT=m_pChar;

  m_pChar=new char[strlen(test.m_pChar)];
  strcpy(m_pChar,test.m_pChar);

  if (!pCharT)
  {
   delete[] pCharT;//delete pChar 与该句的区别
  }
 }

 Test2& operator=(const Test2& test)
 {
  if (this == &test)
  {
   return *this;
  }
  char *pCharT=m_pChar;
  m_pChar=new char[strlen(test.m_pChar)];
  strcpy(m_pChar,test.m_pChar);

  if (!pCharT)
  {
   delete []pCharT;
  }
  return *this;
 }
private:
 char *m_pChar;
};

int main()
{
 const Test1 ts(1);//Test1()
 const Test1 *p_ts=&ts;
 const Test1 ts2(ts);//Test1(const Test1 &test)
 const Test1 ts3=ts;//Test1(const Test1 &test)
 Test1 ts4;
 ts4=ts;//Test1& operator = (const Test1& test)

 Test2 t(5);
 Test2 t2(t);
 Test2 t3=t2;
 Test2 t4;
 t4=t;
 return 0;
}

你可能感兴趣的:(C++拷贝构造函数的几个细节)