c++ 拷贝构造函数

来源:互联网 发布:走廊室内设计知乎 编辑:程序博客网 时间:2024/05/24 05:16

    拷贝构造函数: 是一种特殊的构造函数,它由编译器来调用来完成一些基于同一类的其他对象的构建及初始化。
    什么时候要调用拷贝构造函数(复制构造函数)?
       1)  一个对象作为函数参数,以值传递的方式传入函数体.

       2)  一个对象作为函数返回值,以值传递的方式从函数返回。

       3)  一个对象用于给另外一个对象进行初始化(常称为复制初始化)。

             如果在1),2)两种情况下不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。

 

   拷贝构造函数的格式为:

         类名 (const 类名& 对象名);参数是常量对象的引用,由于拷贝构造函数的目的是成员复制,不应该修改原对象,所以建议使用const 关键字.

 

    拷贝构造函数例子:

       复制初始化

            class    CExample

            {

             public:

                    CExample( )

                    {

                         pBuffer  = NULL;

                         nSize = 0;

                    }

                   

                    ~ CExample ( )

                    {

                         delete  [ ] pBuffer;

                    }

                    

                    void  init(int n)

                     {

                         pBuffer =  new  char[n];

                         nSize = n;

                     }

              private:

                     char * pBuffer ;  //类的对象中包含指针,指向动态分配的内存资源。

                     int   nSize;

              }

              这个类的主要特点是包含指向其他资源的指针,pBuffer指向堆中动态分配的一段内存空间.

             int   main(int argc,char*argv[ ])

             {

                   CExample  theObjone;

                   theObjone.init(40);

                  

                   //现在需要另一个对象,并将它初始化为theObjone;

                   CExample  theObjtwo  =  theObjone;

                   ..........................

            }

            语句“CExample theObjtwo = theObjone”,用theObjone初始化theObjtwo.

            回顾一下此语句的具体过程:首先建立对象theObjtwo,并调用其构造函数,然后成员被复制初始化.

            其完成方式是内存拷贝,复制所有成员的值,完成后,theObjtwo.pBuffer = theObjone.pBuffer.

             即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象公用了,这样不符合要求,对象之间不独立了,并为空间的删除带来隐患.

            所以c++语法中除了提供缺省形式的构造函数外,还规范了另一种特殊的构造函数:拷贝构造函数,上面的语句中,如果类中定义了拷贝构造函数,在对象复制初始化时,调用的将是拷贝构造函数,而不是缺省构造函数,在拷贝构造函数中,可以根据传入的变量,复制指针所指向的资源.

           

            提供了拷贝构造函数后的CExample类定义:

            class   CExample

            {

             public:

                     CExample( )

                     {

                              pBuffer  = NULL;

                              nSize  =  0;

                     }

                     ~CExample( )

                     {

                              delete  [ ] pBuffer;

                     }

                     CExample(const CExample & );   //拷贝构造函数。

                     void init(int n)

                     {

                             pBuffer = new char[n];

                             nSize = n;

                    }

             private:

                    char * pBuffer;   //类的对象中包含指针,指向动态分配的内存资源.

                    int  nSize;

            }

 

            CExample::CExample(const CExample  &RightSides)    //拷贝构造函数的定义

            {

                    nSize =  RightSides.nSize;    //复制常规成员

                   pBuffer = new  char [nSize];  //复制指针指向的内容。

                   memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));

             }

             这样,定义新对象,并用已有对象初始化新对象时,CExample(const CExample& RightSides)将被调用,而已有对象用别名RightSides传给构造函数,以用来作复制。

 

    对象按值传递

         当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调用拷贝构造函数.例如:

          BOOL  testfunc(CExample  obj);

          testfunc(theObjone);

          BOOL  testfunc(CExample obj)

          {

                  //针对obj的操作实际上是针对复制后的临时拷贝进行的。

          }

         当函数中的局部对象作为返回值返回给函数调用者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用。

          CTest  func( )

          {

                  CTest  theTest;

                  return  theTest;

          }

          总结:当某对象是按值传递时(无论是作为函数参数,还是作为函数返回值),编译器都会先建立一个此对象的临时拷贝,而在建立该临时拷贝时就会调用类的拷贝构造函数.

 

    3 赋值操作符的重载

         重载的必要性

          int  main(int argc,char *argv[ ])

          {

               CExample   theObjone;

               theObjone.init(40);

               CExample  theObjthree;

               theObjthree.init(60);

               //现在需要一个对象赋值操作符,被赋值对象的原内容被清除,并用右边对象的内容填充。

              theObjthree = theObjone;

              return 0;

          }

          这里也用到了“=”号,但与“复制初始化”中的例子并不同,“复制初始化”的例子中,“=”在对象声明

语句中,表示初始化.更多时候,这种初始化也可用圆括号表示,例如:CExample  theObjthree(theObjone);。

          而本例中,"="表示赋值操作,将对象theObjone的内容复制到theObjthree,这其中涉及到对象theObjthree原有内容的丢失,新内容的复制.

          但“=”的缺省操作只是将成员变量的值相应复制,由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放,指针的值被复制了,但指针所指内容并未被复制.

          因此,包含动态分配成员的类除提供拷贝构造函数外,还应该考虑重载"="赋值操作符号.

          重载的示例:

              class   CExample

              {

              public:

                    CExample()

                    {

                            pBuffer = NULL;

                            nSize = 0;

                    }

                    ~CExample()

                    {

                            delete pBuffer;

                    }

                    CExample(const CExample &); //拷贝构造函数

                     CExample & operator = (const CExample &);  //赋值符重载

                      void  init(int n)

                      {

                              pBuffer = new char[n];

                              nSize = n;

                       }

              private:

                       char *pBuffer;//类的对象中包含指针,指向动态分配的内存资源.

                        int nSize;

              }

              //赋值操作符重载

              CExample  &CExample::oprator = (const CExample &RightSides)

              {

                     if(this==&RightSides) //如果自己给自己赋值则直接返回

                     {

                             return *this;

                     }

                     nSize = RightSides.nSize; //复制常规成员。

                     char *temp = new char[nSize]; //复制指针指向的内容.

                     memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));

                     delete [ ]pBuffer;  //删除原指针指向内容

                     pBuffer = temp;  //建立新指向.

                     return *this;

             }