精华内容
下载资源
问答
  • C++ 拷贝构造函数 赋值构造函数 拷贝构造函数和赋值构造函数的异同

    C++ 拷贝构造函数 赋值构造函数
    拷贝构造函数和赋值构造函数的异同
    由于并非所有的对象都会使用拷贝构造函数和赋值函数,程序员可能对这两个函数有些轻视。请先记住以下的警告,在阅读正文时就会多心:如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。以类String 的两个对象a,b 为例,假设a.m_data 的内容为“hello”,b.m_data 的内容为“world”。现将a 赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data = a.m_data。这将造成三个错误:一是b.m_data 原有的内存没被释放,造成内存泄露;二是b.m_data 和a.m_data 指向同一块内存,a 或b 任何一方变动都会影响另一方;三是在对象被析构时,m_data 被释放了两次。拷贝构造函数和赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。以下程序中,第三个语句和第四个语句很相似,你分得清楚哪个调用了拷贝构造函数,哪个调用了赋值函数吗?
      

       String a(“hello”);
      String b(“world”);
      String c = a; // 调用了拷贝构造函数,最好写成 c(a);
      c = b; // 调用了赋值函数
      本例中第三个语句的风格较差,宜改写成String c(a) 以区别于第四个语句。
      类String 的拷贝构造函数与赋值函数
      // 拷贝构造函数
      String::String(const String &other)
      {
      // 允许操作other 的私有成员m_data
      int length = strlen(other.m_data);
      m_data = new char[length+1];
      strcpy(m_data, other.m_data);
      }
      // 赋值函数
      String & String::operator =(const String &other)
      {
      // (1) 检查自赋值
      if(this == &other)
      return *this;
      // (2) 释放原有的内存资源
      delete [] m_data;
      // (3)分配新的内存资源,并复制内容
      int length = strlen(other.m_data);
      m_data = new char[length+1];
      strcpy(m_data, other.m_data);
      // (4)返回本对象的引用
      return *this;
      }

      类String 拷贝构造函数与普通构造函数的区别是:在函数入口处无需与NULL 进行比较,这是因为“引用”不可能是NULL,而“指针”可以为NULL。类String 的赋值函数比构造函数复杂得多,分四步实现:
      (1)第一步,检查自赋值。你可能会认为多此一举,难道有人会愚蠢到写出 a = a 这样的自赋值语句!的确不会。但是间接的自赋值仍有可能出现,例如
      

    // 内容自赋值
      b = a;
      …
      c = b;
      …
      a = c;
      // 地址自赋值
      b = &a;
      …
      a = *b;

      也许有人会说:“即使出现自赋值,我也可以不理睬,大不了化点时间让对象复制自己而已,反正不会出错!”他真的说错了。看看第二步的delete,自杀后还能复制自己吗?所以,如果发现自赋值,应该马上终止函数。注意不要将检查自赋值的if 语句
      if(this == &other)
      错写成为
      if( *this == other)
      (2)第二步,用delete 释放原有的内存资源。如果现在不释放,以后就没机会了,将造成内存泄露。
      (3)第三步,分配新的内存资源,并复制字符串。注意函数strlen 返回的是有效字符串长度,不包含结束符‘\0’。函数strcpy 则连‘\0’一起复制。
      (4)第四步,返回本对象的引用,目的是为了实现象 a = b = c 这样的链式表达。注意不要将 return *this 错写成 return this 。那么能否写成return other 呢?效果不是一样吗?不可以!因为我们不知道参数other 的生命期。有可能other 是个临时对象,在赋值结束后它马上消失,那么return other 返回的将是垃圾。
      偷懒的办法处理拷贝构造函数与赋值函数
      如果我们实在不想编写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的缺省函数,怎么办?
      偷懒的办法是:只需将拷贝构造函数和赋值函数声明为私有函数,不用编写代码。
      例如:
      

    class A
      { …
      private:
      A(const A &a); // 私有的拷贝构造函数
      A & operator =(const A &a); // 私有的赋值函数
      };

      如果有人试图编写如下程序:
      A b(a); // 调用了私有的拷贝构造函数
      b = a; // 调用了私有的赋值函数
      编译器将指出错误,因为外界不可以操作A 的私有函数。

    一、
    拷贝构造,是一个的对象来初始化一边内存区域,这边内存区域就是你的新对象的内存区域赋值运算,对于一个已经被初始化的对象来进行operator=操作

    class    A;     
    A   a;  
    A   b=a;    //拷贝构造函数调用  
    //或  
    A   b(a);    //拷贝构造函数调用  
    ///////////////////////////////////     
    A   a;  
    A   b;  
    b =a;    //赋值运算符调用   

    你只需要记住,在C++语言里,
    String s2(s1);
    String s3 = s1;
    只是语法形式的不同,意义是一样的,都是定义加初始化,都调用拷贝构造函数。

    二、
    一般来说是在数据成员包含指针对象的时候,应付两种不同的处理需求的 一种是复制指针对象,一种是引用指针对象 copy大多数情况下是复制,=则是引用对象的
    例子:

     class    A  
    {  
        int    nLen;  
        char    *    pData;  
    }  

    显然
    A a, b;
    a=b的时候,对于pData数据存在两种需求
    第一种copy
    a.pData = new char [nLen];
    memcpy(a.pData, b.pData, nLen);
    另外一种(引用方式):
    a.pData = b.pData

    通过对比就可以看到,他们是不同的
    往往把第一种用copy使用,第二种用=实现
    你只要记住拷贝构造函数是用于类中指针,对象间的COPY

    三、
    和拷贝构造函数的实现不一样
    拷贝构造函数首先是一个构造函数,它调用的时候产生一个对象,是通过参数传进来的那个对象来初始化,产生的对象。
    operator=();是把一个对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。

    还要注意的是拷贝构造函数是构造函数,不返回值

    而赋值函数需要返回一个对象自身的引用,以便赋值之后的操作

    你肯定知道这个:

    int a, b;   
    b = 7;   
    Func(a = b);    //    把i赋值后传给函数Func(int)   
    

    同理:

     CMyClass obj1, obj2;   
      obj1.Initialize();       
      Func2(obj1 = obj2);    //如果没有返回引用,是不能把值传给Func2的   
    
           注:   
    
     CMyClass & CMyClass::operator = (CMyClass & other)   
     {   
        if(this == &other)   
            return *this;   
            //    赋值操作...   
            return *this   
    }

    ==================================================================================
    赋值运算符和复制构造函数都是用已存在的B对象来创建另一个对象A。不同之处在于:赋值运算符处理两个已有对象,即赋值前B应该是存在的;复制构造函数是生成一个全新的对象,即调用复制构造函数之前A不存在。
      CTemp a(b); //复制构造函数,C++风格的初始化
      CTemp a=b; //仍然是复制构造函数,不过这种风格只是为了与C兼容,与上面的效果一样,在这之前a不存在,或者说还未构造好。
      CTemp a;
      a=b; //赋值运算符
      在这之前a已经通过默认构造函数构造完成。
      实例总结:
      重点:包含动态分配成员的类 应提供拷贝构造函数,并重载”=”赋值操作符。
      以下讨论中将用到的例子:
      

    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指向堆中分配的一段内存空间。
      一、拷贝构造函数
      调用拷贝构造函数1
      

    int main(int argc, char* argv[])
    {
      CExample theObjone;
      theObjone.Init(40);
      //现在需要另一个对象,需要将他初始化称对象一的状态
      CExample theObjtwo=theObjone;//拷贝构造函数
      ...
    }

      语句”CExample theObjtwo=theObjone;”用theObjone初始化theObjtwo。
      其完成方式是内存拷贝,复制所有成员的值。
      完成后,theObjtwo.pBuffer==theObjone.pBuffer。
      即它们将指向同样的地方(地址空间),指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用了。这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。
      所以需要采用必要的手段来避免此类情况。
      回顾以下此语句的具体过程:通过拷贝构造函数(系统默认的)创建新对象theObjtwo,并没有调用theObjtwo的构造函数(vs2005试验过)。
      可以在自定义的拷贝构造函数中添加输出的语句测试。
      注意:
      对于含有在自由空间分配的成员时,要使用深度复制,不应使用浅复制。
      调用拷贝构造函数2
      当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调同拷贝构造函数。
      例如
      

    BOOL testfunc(CExample obj);
    testfunc(theObjone); //对象直接作为参数。
    BOOL testfunc(CExample obj)
    {
      //针对obj的操作实际上是针对复制后的临时拷贝进行的
    }

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

    CTest func()
    {
      CTest theTest;
      return theTest
    }

      二、赋值符的重载
      下面的代码与上例相似
      

    int main(int argc, char* argv[])
    {
      CExample theObjone;
      theObjone.Init(40);
      CExample theObjthree;
      theObjthree.Init(60);
      //现在需要一个对象赋值操作,被赋值对象的原内容被清除,并用右边对象的内容填充。
      theObjthree=theObjone;
      return 0;
    }

      也用到了”=”号,但与”一、”中的例子并不同,”一、”的例子中,”=”在对象声明语句中,表示初始化。更多时候,这种初始化也可用括号表示。
      例如 CExample theObjone(theObjtwo);
      而本例子中,”=”表示赋值操作。将对象theObjone的内容复制到对象theObjthree;,这其中涉及到对象theObjthree原有内容的丢弃,新内容的复制。
      但”=”的缺省操作只是将成员变量的值相应复制。旧的值被自然丢弃。
      由于对象内包含指针,将造成不良后果:为了避免内存泄露,指针成员将释放指针所指向的空间,以便接受新的指针值,这正是由赋值运算符的特征所决定的。但如果是”x=x”即自己给自己赋值,会出现什么情况呢?x将释放分配给自己的内存,然后,从赋值运算符右边指向的内存中复制值时,发现值不见了。
      因此,包含动态分配成员的类除提供拷贝构造函数外,还应该考虑重载”=”赋值操作符号。
      类定义变为:
     

     class CExample
     {
      ...
      CExample(const CExample&); //拷贝构造函数
      CExample& operator = (const CExample&); //赋值符重载
      ...
     };
      //赋值操作符重载
     CExample & CExample::operator = (const CExample& RightSides)
     {
      nSize=RightSides.nSize; //复制常规成员
      char *temp=new char[nSize]; //复制指针指向的内容
      memcpy(temp, RightSides.pBuffer, nSize*sizeof(char));
      delete []pBuffer; //删除原指针指向内容 (将删除操作放在后面,避免X=X特殊情况下,内容的丢失)
      pBuffer=temp; //建立新指向
      return *this
     }

      三、拷贝构造函数使用赋值运算符重载的代码。
      

    CExample::CExample(const CExample& RightSides)
    {
      pBuffer=NULL;
      *this=RightSides //调用重载后的"="
    }
    展开全文
  • 一、拷贝构造函数 功能:使用一个已经存在的对象来初始化一个新的同一类型的对象 声明:只有一个参数并且参数为该类对象的引用 注意:如果类中没有说明拷贝构造函数,则系统自动生成一个缺省复制构造函数,作为该类...
    一、拷贝构造函数

    功能:使用一个已经存在的对象来初始化一个新的同一类型的对象
    声明:只有一个参数并且参数为该类对象的引用
    注意:如果类中没有说明拷贝构造函数,则系统自动生成一个缺省复制构造函数,作为该类的公有成员
    下面有一个String的例子,说明拷贝构造函数的使用:
    #ifndef _STRING_H_
    #define _STRING_H_
    class String
    {
    public:
            String(char* str="");
            ~String();
            String(const String& other);
            operator=(const String& other);
    	void Display();
    private:
            char* AllocAndCpy(char* str);
            char* str_;};
    #endif // _STRING_H_
    
    cpp文件:
    #include "String.h"
    #include <cstring>
    #include <iostream>
    using namespace std;
    
    String::String(char* str/* = */)
    {
            str_ = AllocAndCpy(str);
    }
    
    String::~String()
    {
    	delete[] str_;
    }
    
    String::String(const String& other)
    {
    	str_ = AllocAndCpy(other.str_);
    }
    
    String& String::operator =(const String &other)
    {
    	if (this == &other)
    		return *this;
    	delete[] str_;
    	str_ = AllocAndCpy(other.str_);
    	return *this;
    }
    
    char* String::AllocAndCpy(char* str)
    {
    	int len = strlen(str) + 1;
    	char* tmp = new char[len];
    	memset(tmp, 0, len);
    	strcpy(tmp, str);
    	return tmp;
    }
    
    void String::Display()
    {
    	cout<<str_<<endl;
    }

    二、拷贝构造函数调用的几种情况
    • 函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。理所当然也调用拷贝构造函数。如:String a;String b(a);
    • 函数的返回值是类对象,函数执行完成返回调用者时使用。理由也是要建立一个临时对象中,再返回调用者。为什么不直接用要返回的局部对象呢?因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓return对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。
    例子如下:
    String func(String a)
    {
            a = "bbb";
            return a;
    }
    int main()
    {
            String s1("aaaa");
            String s2(s1);        // 初始化调用拷贝构造函数,我们提供了深拷贝构造
            String s3 = s2; //上面是等价的初始化操作,我们提供深拷贝
            String s4 = func(s3);//这个语句调用两次构造函数1、用实参s3初始化形参a 2、用返回值初始化临时对象时
            s4.Display();
            return 0;
    }
    三、深拷贝和浅拷贝
          上面的例子中,我们做的都是深拷贝,因为我们提供自己的构造函数,并在构造函数里分配内存。如果我们没有提供自己的构造函数,系统默认会为我们生成自己的构造函数,这个构造函数是浅拷贝的,对于不涉及到显式内存分配的类来说,默认的构造函数是没有问题的,但是对于有显式内容分配的类,比如我们这个String例子,就会出现问题。为什么会出现问题呢?我们来看一下它们的内存模型:
    1.浅拷贝


    图画的比较挫,将就着看吧。由图可以看出,s1和s2指向的是同一块内存区域,在s1,s2出了作用域之后,会调用各自的构造函数,造成内存的重复释放,引发程序错误。
    2.例子里实现的深拷贝


    深拷贝的时候会创建对象自己的内存空间,再把原来对象的内容拷贝进去,这样拷贝和被拷贝的就是两个不同的对象。
    四、拷贝构造函数和"="操作符的区别
          上面的例子中我们已经实现了拷贝构造函数和=操作符,我们在这里说下两者的区别。首先拷贝构造函数用于对象的初始化,在这之前被初始化对象没有任何内容。但是调用=操作符的时候,左边的对象可能已经有值了,我们不能像构造函数那样直接将对象里的指针直接指向新分配的空间,这样指针原来指向的内存空间就没有指向,造成内存泄露。我们必须先把原来的空间释放,在调用拷贝构造函数。还有一种例外的情况,就是=操作符两边是相同的对象,此时我们直接返回对象本身即可。例子如下:
    int main()
    {
    	String s1("aaaa");
    	String s2(s1);	// 初始化调用拷贝构造函数
    	String s3 = s2; //上面是等价的初始化操作
    	String s4 = func(s3);
    	String s5;
    	s5.Display();
    	s5 = s2;			// 调用等号运算符
    	s5 = s5;			// 系统提供的默认等号运算符实施的是浅拷贝 s3.str_ = s2.str_;
    						// s3.operator=(s2);
    	return 0;
    }
    五、禁止对象拷贝
          要让对象是独一无二的,我们要禁止拷贝,方法是将拷贝构造函数与=运算符声明为私有,并且不提供它们的实现。也可以写一个类noncopy,私有化拷贝构造函数和=运算符。然后让我们需要禁止拷贝的类继承类noncopy即可。
    #include <iostream>
    using namespace  std;
    class Noncopy
    {
    public:
    	Noncopy(){}
    	~Noncopy(){}
    private:
    	Noncopy(const Noncopy &a);
    	const Noncopy& operator=( const Noncopy& );
    };
    
    class Student : public Noncopy
    {
    public:
    	void setAge(int age) { age_ = age; }
    	int getAge() { return age_; }
    private:
    	int age_;
    };
    int main()
    {
    	Student a;
    	a.setAge(21);
    	cout<<a.getAge()<<endl;
    	Student b(a);//出错,不能拷贝
    	return 0;
    }



    展开全文
  • 拷贝构造函数

    2016-11-16 10:21:05
    拷贝构造函数拷贝构造函数 一什么是拷贝构造函数 1 浅拷贝 2 拷贝构造函数 二在哪里使用到拷贝函数 1 作为参数传递给函数 2 对象需要通过另外一个对象进行初始化 3 从函数返回对象 一、什么是拷贝构造函数?1.1 浅...

    拷贝构造函数

    一、什么是拷贝构造函数?

    1.1 浅拷贝

    先来了解点预备知识,浅拷贝。所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了
    

    1.2 拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象

    如果没有构造函数,会自动定义,如果有指针变量且带有动态内存分配就必须自己写一个。

        classname(const classname &obj)
        {
        }

    二、在哪里使用到拷贝函数?

    1. 作为参数传递给函数
    2. 对象需要通过另外一个对象进行初始化
    3. 从函数返回对象

    2.1 作为参数传递给函数

    void display(ClassName obj)
    {
       cout << "Length of line : " << obj.getLength() <<endl;
    }

    注解:当对象作为参数传递过来的时候,先产生一个temp对象,调用拷贝构造函数,当作用域结束的时候(就是函数结束),调析构。

    2.2 对象需要通过另外一个对象进行初始化

       Line line1(10);
       Line line2 = line1; // 这里也调用了拷贝构造函数

    2.3 从函数返回对象

    //全局函数  
    CExample g_Fun()  
    {  
     CExample temp(0);  
     return temp;  
    }  

    (1). 先会产生一个临时变量,就叫XXXX吧。
    (2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
    (3). 在函数执行到最后先析构temp局部变量。
    (4). 等g_Fun()执行完后再析构掉XXXX对象。

    • 特别注意: 当我使用gcc进行编译的时候发现,返回对象自动调用复制拷贝函数的机制没有执行,经过查阅资料发现是由于gcc具有返回值优化,我们要看见结果只需要在编译时候加入参数:-fon-elide-constructors
        g++ -fon-elide-constructors main.cpp
    展开全文
  • C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。 构造函数就是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存。拷贝构造函数是一种特殊的构造函数,用...

    C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。下面就详细比较下三者之间的区别以及它们的具体实现

    1.构造函数

    构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存。(构造函数的命名必须和类名完全相同)

    首先说一下一个C++的空类,编译器会加入哪些默认的成员函数

    ·默认构造函数和拷贝构造函数

    ·析构函数

    ·赋值函数(赋值运算符)

    ·取值函数

    **即使程序没定义任何成员,编译器也会插入以上的函数!

    注意:构造函数可以被重载,可以多个,可以带参数;

    析构函数只有一个,不能被重载,不带参数

     

    而默认构造函数没有参数,它什么也不做。当没有重载无参构造函数时,

      A a就是通过默认构造函数来创建一个对象

    下面代码为构造函数重载的实现

    <span style="font-size:14px;">class A
    {
    int m_i;
    Public:
      A() 
    {
     Cout<<”无参构造函数”<<endl;
    }
    A(int i):m_i(i) {}  //初始化列表
    }</span>

    2.拷贝构造函数


    拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。

    当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建一个对象

    A a;

    A b(a);

    A b=a;  都是拷贝构造函数来创建对象b

    强调:这里b对象是不存在的,是用a 对象来构造和初始化b的!!


    先说下什么时候拷贝构造函数会被调用:

    在C++中,3种对象需要复制,此时拷贝构造函数会被调用

    1)一个对象以值传递的方式传入函数体

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

    3)一个对象需要通过另一个对象进行初始化


    什么时候编译器会生成默认的拷贝构造函数:

    1)如果用户没有自定义拷贝构造函数,并且在代码中使用到了拷贝构造函数,编译器就会生成默认的拷贝构造函数。但如果用户定义了拷贝构造函数,编译器就不在生成。

    2)如果用户定义了一个构造函数,但不是拷贝构造函数,而此时代码中又用到了拷贝构造函数,那编译器也会生成默认的拷贝构造函数。

     

    因为系统提供的默认拷贝构造函数工作方式是内存拷贝,也就是浅拷贝。如果对象中用到了需要手动释放的对象,则会出现问题,这时就要手动重载拷贝构造函数,实现深拷贝。

    下面说说深拷贝与浅拷贝:

    浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用,两个对象不独立,删除空间存在)

    深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。


    拷贝构造函数重载声明如下:

    A (const A&other)

    下面为拷贝构造函数的实现:

    <span style="font-size:14px;">class A
    {
      int m_i
      A(const A& other):m_i(other.m_i)
    {
      Cout<<”拷贝构造函数”<<endl;
    }
    }</span>


    3.赋值函数

    当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。

    当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作

    A a;

    A b;

    b=a; 

    强调:这里a,b对象是已经存在的,是用a 对象来赋值给b的!!


    赋值运算的重载声明如下:

     A& operator = (const A& other)


    通常大家会对拷贝构造函数和赋值函数混淆,这儿仔细比较两者的区别:

    1)拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。

    <span style="font-size:14px;">class  A;
    A a;
    A b=a;   //调用拷贝构造函数(b不存在)
    A c(a) ;   //调用拷贝构造函数
    
    /****/
    
    class  A;
    A a;
    A b;   
    b = a ;   //调用赋值函数(b存在)</span>

    2)一般来说在数据成员包含指针对象的时候,需要考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象

    3)实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检察一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。(这些要点会在下面的String实现代码中体现)

     

    !!!如果不想写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的缺省函数,最简单的办法是将拷贝构造函数和赋值函数声明为私有函数,不用编写代码。如:

    <span style="font-size:14px;">class A
    {
     private:
     A(const A& a); //私有拷贝构造函数
     A& operate=(const A& a); //私有赋值函数
    }</span>

    如果程序这样写就会出错:

    <span style="font-size:14px;">A a;
    A b(a); //调用了私有拷贝构造函数,编译出错
    
    A b;
    b=a; //调用了私有赋值函数,编译出错</span>

    所以如果类定义中有指针或引用变量或对象,为了避免潜在错误,最好重载拷贝构造函数和赋值函数。


    下面以string类的实现为例,完整的写了普通构造函数,拷贝构造函数,赋值函数的实现。String类的基本实现见我另一篇博文。

    <span style="font-size:14px;">String::String(const char* str)    //普通构造函数
    
    {
    
     cout<<construct<<endl;
    
     if(str==NULL)        //如果str 为NULL,就存一个空字符串“”
    
    {
     m_string=new char[1];
     *m_string ='\0';
    }
    
     else
    
    {
    
      m_string= new char[strlen(str)+1] ;   //分配空间
      strcpy(m_string,str);
    
    }
    
    }
    
     
    String::String(const String&other)   //拷贝构造函数
    
    {
     cout<<"copy construct"<<endl;
     m_string=new char[strlen(other.m_string)+1]; //分配空间并拷贝
     strcpy(m_string,other.m_string);
    }
    
    String & String::operator=(const String& other) //赋值运算符
    {
     cout<<"operator =funtion"<<endl ;
     if(this==&other) //如果对象和other是用一个对象,直接返回本身
     {
      return *this;
     }
     delete []m_string; //先释放原来的内存
     m_string= new char[strlen(other.m_string)+1];
     strcpy(m_string,other.m_string);
     return * this;
    }</span>

     

    一句话记住三者:对象不存在,且没用别的对象来初始化,就是调用了构造函数;

                    对象不存在,且用别的对象来初始化,就是拷贝构造函数(上面说了三种用它的情况!)

                     对象存在,用别的对象来给它赋值,就是赋值函数。

    以上为本人结合很多资料和图书整理出来的,将核心的点都系统的理出来,全自己按条理写的,现在大家对普通构造函数,拷贝构造函数,赋值函数的区别和实现应该都清楚了。

     

    展开全文
  • 拷贝构造函数详解

    2021-02-02 15:04:44
    拷贝构造函数 拷贝构造函数是构造函数的一种, 也称复制构造函数, 只有一个参数, 参数类型是该类的引用. [拷贝构造函数的参数可以是 const 引用,也可以是非 const 引用。 一般使用前者,这样既能以常量对象(初始...
  • C 拷贝构造函数

    2019-05-09 02:14:46
    总结: 拷贝构造函数被调用的场景: 1. A = B 2. Rect r; Rect r1(r);...浅拷贝只拷贝栈上的变量,深拷贝同时要拷贝堆上面的内存。...拷贝构造函数,编译器会默认给你添加的, 所以可以直接Rect r...
  • c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起

    万次阅读 多人点赞 2017-07-24 19:44:10
    1. c++类的默认拷贝构造函数的弊端c++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数。它们的特殊之处在于: (1)当类中没有定义任何构造函数时,编译器会默认提供一个无参构造函数且其函数体为空;...
  • c++拷贝构造函数

    千次阅读 多人点赞 2019-06-28 16:22:40
    什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a = 100; int b = a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。 下面看一个类对象...
  • C++类的构造函数学习笔记 构造函数 拷贝赋值运算符 移动构造函数 移动赋值运算符 析构函数
  • 这里我们用类String 来介绍这两个函数: ...试想一下,假如形参是该类的一个实例,由于是传值参数,我们把形参复制到实参会调用拷贝构造函数,如果允许拷贝构造函数传值,就会在拷贝构造函数内调用拷贝构...
  • 1,为什么拷贝构造函数传入的必须是引用? 如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值... 需要澄清的是,传指针其实也是传值,如果上面的拷贝构造函数写...
  • C++拷贝构造函数总结

    2016-03-30 17:55:41
    拷贝构造函数,又称为复制构造函数。拷贝构造函数是一种特殊的构造函数。函数的名称必须和类名称一致,它仅含一个本类型的引用变量的参数。一般情况下会加const限制。(因为拷贝构造函数通常是复制对象成员,而不...
  • http://blog.chinaunix.net/uid-25808509-id-354211.html ...以上两篇文章讲了拷贝构造函数与赋值构造函数 看以下代码: #include using namespace std; class A { public:
  • 拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。 作用就是用来复制对象,在使用这个对象的实例来初始化这个对象的一个新的实例。类中可以存在多个拷贝...
  • 拷贝构造函数 拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此...
  • 昨天晚上在看智能指针的时候,我发现自己连一个拷贝构造函数和赋值构造函数都不出来,自己就尝试了一个版本,结果发现错误百出,对于拷贝构造函数和赋值构造函数的理解仅仅停留在理论的方面,而不知其中太多的...
  • C++拷贝构造函数、构造函数和析构函数

    万次阅读 多人点赞 2018-08-30 22:09:15
    一、拷贝构造函数 转载自:http://www.cnblogs.com/BlueTzar/articles/1223313.html 1、类对象的拷贝  对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=88; int b=a;   而类对象与普通...
  • 拷贝构造函数和赋值构造函数的区别  这两种函数什么时候用呢?  1、当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。  2、一个对象以值传递的...
  • 一,拷贝构造函数 拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(const X& x)...
  • C++ 拷贝构造函数和重载赋值函数

    千次阅读 2016-03-24 15:05:10
    拷贝构造函数和重载赋值=的函数可以有效防止在浅复制过程中可能对于同一片内存释放两次的问题。 然而拷贝函数和重载复制=的函数很容易混淆。拷贝构造函数是在对象创建时调用的,而赋值函数只能被已经存在的对象调用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 141,414
精华内容 56,565
关键字:

拷贝构造函数怎么写