精华内容
下载资源
问答
  • 拷贝构造函数,默认拷贝构造函数 1.c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起 c++类的默认拷贝构造函数的弊端 c++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数。它们的特殊之处在于: (1)...

    拷贝构造函数,默认拷贝构造函数
    1.c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起

    1. c++类的默认拷贝构造函数的弊端
      c++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数。它们的特殊之处在于:
      (1)当类中没有定义任何构造函数时,编译器会默认提供一个无参构造函数且其函数体为空;
      (2)当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,进行成员变量之间的拷贝。(这个拷贝操作是浅拷贝)
      class TestCls{
      public:
      int a;
      int *p;

    public:
    TestCls() //无参构造函数
    {
    std::cout<<“TestCls()”<<std::endl;
    p = new int;
    }

    ~TestCls()     //析构函数
    {
        delete p;   
        std::cout<<"~TestCls()"<<std::endl;
    }
    

    };
    int main(void)
    {
    TestCls t;
    return 0;
    }
    编译运行确实不会出错:

    类在我们没有定义拷贝构造函数的时候,会默认定义默认拷贝构造函数,也就是说可以直接用同类型的类间可以相互赋值、初始化:
    int main(void)
    { TestCls t1; TestCls t2 = t1; //效果等同于TestCls t2(t1); return 0; }
    这样结果就会出错。

    原因就在于,默认的拷贝构造函数实现的是浅拷贝。
    2.深度拷贝和浅拷贝
    深度拷贝和浅拷贝在c语言中就经常遇到的了,在这里我简单描述。
    一般的赋值操作是深度拷贝:

    //深度拷贝
    int a = 5;
    int b = a;
    简单的指针指向,则是浅拷贝:
    //浅拷贝
    int a = 8;
    int *p;
    p = &a;

    char* str1 = “HelloWorld”;
    char* str2 = str1;
    将上面的浅拷贝改为深度拷贝后:
    //深度拷贝
    int a = 8;
    int *p = new int;
    *p = a;

    char* str1 = “HelloWorld”;
    int len = strlen(str1);
    char *str2 = new char[len];
    memcpy(str2, str1, len);
    能看得出深度拷贝和浅拷贝的差异。拷贝者和被拷贝者若是同一个地址,则为浅拷贝,反之为深拷贝。 
    以字符串拷贝为例,浅拷贝后,str1和str2同指向0x123456,不管哪一个指针,对该空间内容的修改都会影响另一个指针。

    深拷贝后,str1和str2指向不同的内存空间,各自的空间的内容一样。因为空间不同,所以不管哪一个指针,对该空间内容的修改都不会影响另一个指针。

    1. 解决默认拷贝构造函数的弊端
      类的默认拷贝构造函数只会用被拷贝类的成员的值为拷贝类简单初始化,也就是说二者的p指针指向的内存空间是一致的。以前面TestCls可以知道,编译器为我们默认定义的拷贝构造函数为:
      TestCls(const TestCls& testCls)
      { a = testCls.a; p = testCls.p; //两个类的p指针指向的地址一致。 }
      main函数将要退出时,拷贝类t2的析构函数先得到执行,它把自身p指向的堆空间释放了;接下来,t1的析构函数得到调用,被拷贝类t1的析构函数得到调用,它同样要去析构自身的p指向指向的堆空间,但是该空间和t2类中p指向的空间一样,造成重复释放,程序运行崩溃。
      解决办法就是自定义拷贝构造函数;
      class TestCls{
      public:
      int a;
      int *p;

    public:
    TestCls()
    {
    std::cout<<“TestCls()”<<std::endl;
    p = new int;
    }

    TestCls(const TestCls& testCls)
    {
        cout<<"TestCls(const TestCls& testCls)"<endl;
        a = testCls.a;
        //p = testCls.p;
        p = new int;
        *p = *(testCls.p);      //为拷贝类的p指针分配空间,实现深度拷贝
    }
    
    ~TestCls()
    {
        delete p;   
      cout<<"~TestCls()"<<endl;
    }
    

    };

    int main(void)
    {
    TestCls t1;
    TestCls t2 = t1;
    return 0;
    }

    展开全文
  • C++拷贝构造函数复制构造函数)

    千次阅读 2018-12-22 14:03:33
    一、拷贝构造函数的形式 复制构造函数是构造函数的一种特殊情况。因为类的对象包含各种成员变量,在发生拷贝时不能和普通对象一样来拷贝,所以我们需要使用拷贝构造函数来进行对象拷贝。拷贝构造函数只有一个参数,...

    一、拷贝构造函数的形式

    复制构造函数是构造函数的一种特殊情况。因为类的对象包含各种成员变量,在发生拷贝时不能和普通对象一样来拷贝,所以我们需要使用拷贝构造函数来进行对象拷贝。拷贝构造函数只有一个参数,参数类型是本类的引用。

    如果构造函数没有显式定义,那么编译器将会自动生成拷贝构造函数。大多数情况下,其作用是实现从源对象到目标对象逐个字节的复制,即使得目标对象的每个成员变量都变得和源对象相等。

    为什么只有一个参数?

    通常来说,要发生拷贝,需要两个参数。但是我们知道在类中存在一个this指针,所以其中一个参数就是this指针指向的对象,而这个对象被隐式的定义了,所以在拷贝构造函数中我们只需要给出用来初始化this指针指向对象的另一个对象。听起来是不是有点绕,简单来说,当我用一个对象去初始化另一个对象时,拷贝构造函数会发生调用,调用这个函数的对象作为this指针指向的对象,而用来初始化这个对象的对象就需要作为实参传递给拷贝构造函数,所以在拷贝构造函数中我们需要一个对象的形参来接收它。

    需要注意的是,这里所说的一个参数并不真的仅仅只局限于一个参数。对象拷贝的过程是一对一的过程,所以这里的参数指的是类所定义的对象。

    class Quick{
    public:
    	Quick(int rear=1, int imag=1)
    	{
    		_rear = rear;
    		_imag = imag;
    	}
    	Quick(const Quick& ret)
    	{
    		_rear = ret._rear;
    		_imag = ret._imag;
    		cout << "copy" << endl;
    	}
    		
    private:
    	int _rear;
    	int _imag;
    };
    int main()
    {
    	Quick s1;
    	Quick s2(s1);//调用拷贝构造函数对s2进行初始化
    	return 0;
    }

    对于一个类Quick, 如果一个构造函数的第一个参数是下列之一:

    1. Quick&
    2. const Quick&
    3. volatile Quick&
    4. const volatile Quick&

    且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.

    例如Quick(const Quick&,int m=3)也是拷贝构造函数

    为什么参数类型是本类的引用?

    当调用拷贝构造函数时,假设以值的方式进行传递,将类对象的值传递过去,首先会进行临时拷贝,因为类对象的拷贝要调用临时拷贝函数,又因为使用的是值传递,所以会再次发生临时拷贝,从而进行无限递归。所以为了避免这种情况发生,请使用类的引用方式。

    二、何时调用拷贝构造函数

    调用拷贝构造函数一共有三种情况:

    1.当用一个对象去初始化同类的另一个对象时,会引发拷贝构造函数被调用

    Quick s1;
    Quick s2(s1);

    这里需要注意的是

    Quick s1;
    Quick s2;
    s2=s1;

    上面第三条语句是赋值语句,不是初始化语句,因为左操作数已经是一个已经定义的变量,所以并不会调用拷贝构造函数。

    2.如果函数fun的参数是类Quick的对象,那么当fun被调用时,类Quick的拷贝构造函数将被调用

    int main()
    {
    	fun(Quick ret);
    	return 0;
    }

    3.如果函数的返冋值是类Quick的对象,则函数返冋时,类Quick的拷贝构造函数被调用

    Quick Fun() {
        Quick ret(2,3);
        return ret;
    }

    三、浅拷贝和深拷贝

    1.浅拷贝

    很多前拷贝时候我们在不显式定义拷贝构造函数的情况下,编译器会给我们自动产生一个拷贝构造函数,这个编译器生成默认的拷贝构造函数对传递对象给函数参数或者函数返回对象都能很好的完成,这个默认的拷贝构造也叫做浅拷贝。这个构造函数很简单,大多数情况下,其作用是实现从源对象到目标对象逐个字节的复制,即使得目标对象的每个成员变量都变得和源对象相等。

    那么为什么要自己显式定义拷贝构造函数呢?我们来看下面的代码:

    class Hyottoko {
    public:
    	Hyottoko()
    	{
    		m_count++;
    	}
    	
    	static int getCount()
    	{
    		return m_count;
    	}
    	~Hyottoko()
    	{
    		m_count--;
    	}
    private:
    	int *m_next;
    	int m_data;
    	static int m_count;
    };
    int Hyottoko::m_count = 0;
    
    int main()
    {
    	Hyottoko s1;//第一个对象
    	Hyottoko s2(s1);//第二个对象
    	cout << s2.getCount()<< endl;
    
    	system("pause");
    	return 0;
    }
    

    代码要解决的问题是:获取类定义的对象的个数

    在上面的代码中我们没有显式定义拷贝构造函数,使用编译器默认生成的拷贝构造函数,当我们在使用一个对象去初始化另一个对象时会对拷贝构造函数调用。对于调用默认生成的拷贝构造函数初始化的对象并没有对用于对象计数的count进行操作,所以此时虽然有两个对象,但count的值是1,同时当析构函数被调用时,count的值为-1。

    这种结果时拷贝构造函数对于静态数据成员没有做出相应的处理所造成的。

    如果我们在类中添加显式的拷贝构造函数就可以解决这种问题

    Hyottoko(const Hyottoko& data)
    	{
    		m_count++;
    	}

     

    2.深拷贝

    上面解决问题的拷贝构造函数是一种浅拷贝,那么深拷贝和浅拷贝区别在哪呢?

    我们先来看看下面的代码:

    class String {
    public:
    	String(char*str = NULL)
    	{
    		m_str = new char[strlen(str)+1];
    		strcpy(m_str, str);
    	}
    	String(String& str)
    	{
    		strcpy(m_str, str.m_str);
    	}
    	~String()
    	{
    		if (m_str)
    			delete[]m_str;
    	}
    private:
    	char *m_str;
    };
    int main()
    {
    	String s1("Hello");
    	String s2(s1);
    
    	system("pause");
    	return 0;
    }

    使用s1去初始化s2调用拷贝构造函数,我们的目的是将s1中的内容拷贝到s2的空间中,但实际上,使用浅拷贝的方式只是改变了s2的指向,让s2指向s1所指向的空间,这会使得调用析构函数时,同一空间被释放两次,造成错误;而且原来s2指向的空间没有被释放,造成内存泄露。为了解决这种问题我们需要进行深拷贝,修改类中的拷贝构造函数,修改如下:

    String(String& str)
    	{
    		m_str = new char[strlen(str.m_str)+1];
    		strcpy(m_str, str.m_str);
    	}

    也就是在拷贝对象前,先进行空间分配。

    四、归纳总结

    初始化对象的函数有两种,一个是构造函数,一个是拷贝构造函数(浅拷贝、深拷贝)。以不同的方式来初始化一个对象时,需要明确调用函数是那个,再根据需求来确定使用哪种构造函数。在重载赋值运算符时也需要注意拷贝这个问题。

    展开全文
  • 在c++中既存在默认构造函数,又存在默认拷贝构造函数(即复制构造函数,以下均以拷贝构造函数称呼);例如以下申明一个Person类Class CPerson { private : int age; int height; char sex; public: CPerson ...

    在c++中既存在默认构造函数,又存在默认拷贝构造函数(即复制构造函数,以下均以拷贝构造函数称呼);

    例如以下申明一个Person类

    Class CPerson
    {
       private :
               int age;
               int height;
               char sex;
      public:
              CPerson (int age,int height,char sex);
              
                 
    }
    CPerson::CPerson(int age,int height,char sex)
    {
       age=age;
       height=height;
       sex=sex;
       CPerson * son;
    }
    
       
    

    以下实例化一个父亲的对象

    CPerson father1(40,180,'男');//这会他还没有儿子
    //此时如果要再实例化一个父亲对象father2,并且跟father1一模一样,这里就用到了拷贝构造函数。
    CPerson father2(father1);
    //另一种能达到上面这种拷贝效果的格式
    //CPerson father2=father1;
    
     
    

    以上这中拷贝方法的实现实际上是调用了了程序默认的拷贝构造函数
    CPerson (const CPerson& p)//加const 是保证不能通过p来改变实参的数据成员
    {
       age=p.age;
       height=p.height;
       sex=p.sex;
    }
    实际上就是利用了father1的引用(引用就是一种特殊的指针);
    但是如果这时father1现在有儿子了(孩子他妈是谁并不重要);
    father1.son=new CPerson(5,100,‘男’);
    这会咱们依然将father1拷贝给father2,这时候就出现拷贝构造函数的两个重要概念:浅拷贝深拷贝   ;

    1.默认的拷贝构造函数是浅拷贝,形式如下:
     CPerson (const CPerson& p)//加const 是保证不能通过p来改变实参的数据成员
    {
    age=p.age;
    height=p.height;
    sex=p.sex;
       son=p.son;//这里就是浅拷贝,是址传递,father1和father2指向同一个儿子的内存地址
    }
      在浅拷贝中就出现了另一个问题,既然是址传递,那么只要father1或者father2中任意一个son的数据成员改变,另一个人的son的数据成员同样改变

    2. 深拷贝问题;首先你要明确深拷贝不是程序默认的,哎,你如果自己不写,那么就不会有深拷贝;
     具体代码:
    CPerson (const CPerson& p)//加const 是保证不能通过p来改变实参的数据成员
    {
    age=p.age;
    height=p.height;
    sex=p.sex;
    son=new CPerson(p.son->age,p.son->height,p.son->sex);//这里就是深拷贝,是值传递,father1和father2现在有相同特征的son.但是他们的son是各自的son,自己的son在内存上有自己的地址               }    
        关于深拷贝,这里就出现了不同于浅拷贝的地方;因为深拷贝之后的两个father有各自自己的son,只是两个son长得一模一样罢了;这时如果你改变任意一个father的son的数据成员,另一个son是不会受到影响的;

    注意:浅拷贝一般不要自己写函数。深拷贝一般就要自己动手了。

    另一个用到拷贝构造函数的例子
          函数的参数为对象         CPerson fffffff(CPerson person2);
            这种情况下就又会用到拷贝复制函数;
            首先调用函数时fffffff(person1);
            这是在我们看不见的程序内部到底发生了什么的?
            首先实参person1将要复制给person2(这时候就调用的拷贝构造函数),你要知道你利用了拷贝构造函数实例话一个对象后,在这个对象失效时,又必须调用析构函数,将其清除内存;而这个函数返回一个CPerson类,在你返回时,你看不见之下程序又一次调用拷贝构造函数,一来一去,两次拷贝,一次析构;      函数的多次调用对我们程序性能是不好的,会让程序运行缓慢,那么解决的办法的?
         
           将 CPerson fffffff(CPerson& person2);或者CPerson fffffff(CPerson* person2);
          对应的函数调用形式为 fffffff( person1);和 fffffff(&person1);
       这会咱们就是址传递了,管你呢?反正指向的同一个地址,就不存在多次调用拷贝构造函数和析构函数的问题了;

    对了,处于一种好的习惯 如果你不打算改变实参的话,那么就要引用,声明函数时 CPerson fffffff(const  CPerson& person2)如果你打算改变实参就用指针,CPerson ffffff(CPerson *person2);

    以上均为个人学习认识,若有知识点上和书写上的错误,欢迎各位大佬指出;
     介于CSDN新手,,,不会排版,各位大佬见谅。

    展开全文
  • 对于我来说,在写代码的时候能用得上复制构造函数的机会并不多,不过这并不说明复制构造函数没什么用,其实复制构造函数能解决一些我们常常会忽略的问题。  为了说明复制构造函数作用,我先说说我们在编程时会...
    也许很多C++的初学者都知道什么是构造函数,但是对复制构造函数(copy constructor)却还很陌生。对于我来说,在写代码的时候能用得上复制构造函数的机会并不多,不过这并不说明复制构造函数没什么用,其实复制构造函数能解决一些我们常常会忽略的问题。
           为了说明复制构造函数作用,我先说说我们在编程时会遇到的一些问题。对于C++中的函数,我们应该很熟悉了,因为平常经常使用;对于类的对象,我们也很熟悉,因为我们也经常写各种各样的类,使用各种各样的对象;对于指针的操作,我们也不陌生吧?嗯,如果你还不了解上面三个概念的话,我想这篇文章不太适合你,不过看看也无碍^_^。我们经常使用函数,传递过各种各样的参数给函数,不过把对象(注意是对象,而不是对象的指针或对象的引用)当作参数传给函数的情况我们应该比较少遇见吧,而且这个对象的构造函数还涉及到一些内存分配的操作。嗯,这样会有什么问题呢?
           把参数传递给函数有三种方法,一种是值传递,一种是传地址,还有一种是传引用。前者与后两者不同的地方在于:当使用值传递的时候,会在函数里面生成传递参数的一个副本,这个副本的内容是按位从原始参数那里复制过来的,两者的内容是相同的。当原始参数是一个类的对象时,它也会产生一个对象的副本,不过在这里要注意。一般对象产生时都会触发构造函数的执行,但是在产生对象的副本时却不会这样,这时执行的是对象的复制构造函数。为什么会这样?嗯,一般的构造函数都是会完成一些成员属性初始化的工作,在对象传递给某一函数之前,对象的一些属性可能已经被改变了,如果在产生对象副本的时候再执行对象的构造函数,那么这个对象的属性又再恢复到原始状态,这并不是我们想要的。所以在产生对象副本的时候,构造函数不会被执行,被执行的是一个默认的构造函数。当函数执行完毕要返回的时候,对象副本会执行析构函数,如果你的析构函数是空的话,就不会发生什么问题,但一般的析构函数都是要完成一些清理工作,如释放指针所指向的内存空间。这时候问题就可能要出现了。假如你在构造函数里面为一个指针变量分配了内存,在析构函数里面释放分配给这个指针所指向的内存空间,那么在把对象传递给函数至函数结束返回这一过程会发生什么事情呢?首先有一个对象的副本产生了,这个副本也有一个指针,它和原始对象的指针是指向同块内存空间的。函数返回时,对象的析构函数被执行了,即释放了对象副本里面指针所指向的内存空间,但是这个内存空间对原始对象还是有用的啊,就程序本身而言,这是一个严重的错误。然而错误还没结束,当原始对象也被销毁的时候,析构函数再次执行,对同一块系统动态分配的内存空间释放两次是一个未知的操作,将会产生严重的错误。
           上面说的就是我们会遇到的问题。解决问题的方法是什么呢?首先我们想到的是不要以传值的方式来传递参数,我们可以用传地址或传引用。没错,这样的确可以避免上面的情况,而且在允许的情况下,传地址或传引用是最好的方法,但这并不适合所有的情况,有时我们不希望在函数里面的一些操作会影响到函数外部的变量。那要怎么办呢?可以利用复制构造函数来解决这一问题。复制构造函数就是在产生对象副本的时候执行的,我们可以定义自己的复制构造函数。在复制构造函数里面我们申请一个新的内存空间来保存构造函数里面的那个指针所指向的内容。这样在执行对象副本的析构函数时,释放的就是复制构造函数里面所申请的那个内存空间。
           除了将对象传递给函数时会存在以上问题,还有一种情况也会存在以上问题,就是当函数返回对象时,会产生一个临时对象,这个临时对象和对象的副本性质差不多。
    拷贝构造函数,经常被称作X(X&),是一种特殊的构造函数,他由编译器调用来完成一些基于同一类的其他对象的构件及初始化。它的唯一的一个参数(对象的引用)是不可变的(因为是const型的)。这个函数经常用在函数调用期间于用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。 
    在C++中,下面三种对象需要拷贝的情况。因此,拷贝构造函数将会被调用。 
    1). 一个对象以值传递的方式传入函数体 
    2). 一个对象以值传递的方式从函数返回 
    3). 一个对象需要通过另外一个对象进行初始化 
    以上的情况需要拷贝构造函数的调用。如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作赋共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。 
    拷贝构造函数不可以改变它所引用的对象,其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环。 
    除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。换句话说,你从函数返回得到的只是对象的一份拷贝。但是同样的,拷贝构造函数被正确的调用了,你不必担心。 
    如果在类中没有显式的声明一个拷贝构造函数,那么,编译器会私下里为你制定一个函数来进行对象之间的位拷贝(bitwise copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。许多作者都会提及这个默认的拷贝构造函数。注意到这个隐式的拷贝构造函数和显式声明的拷贝构造函数的不同在于对于成员的关联方式。显式声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数除非另外一个构造函数在类初始化或者在构造列表的时候被调用。 
    拷贝构造函数是程序更加有效率,因为它不用再构造一个对象的时候改变构造函数的参数列表。设计拷贝构造函数是一个良好的风格,即使是编译系统提供的帮助你申请内存默认拷贝构造函数。事实上,默认拷贝构造函数可以应付许多情况。
    附另外一篇关于复制构造函数的文章:

    对一个简单变量的初始化方法是用一个常量或变量初始化另一个变量,例如:
      int m = 80;
      int n = m;
      我们已经会用构造函数初始化对象,那么我们能不能象简单变量的初始化一样,直接用一个对象来初始化另一个对象呢?答案是肯定的。我们以前面定义的Point类为例:
      Point pt1(15, 25);
      Point pt2 = pt1;
    后一个语句也可以写成:
      Point pt2( pt1);
    它是用pt1初始化pt2,此时,pt2各个成员的值与pt1各个成员的值相同,也就是说,pt1各个成员的值被复制到pt2相应的成员当中。在这个初始化过程当中,实际上调用了一个复制构造函数。当我们没有显式定义一个复制构造函数时,编译器会隐式定义一个缺省的复制构造函数,它是一个内联的、公有的成员,它具有下面的原型形式:
      Point:: Point (const Point &);
    可见,复制构造函数与构造函数的不同之处在于形参,前者的形参是Point对象的引用,其功能是将一个对象的每一个成员复制到另一个对象对应的成员当中。
      虽然没有必要,我们也可以为Point类显式定义一个复制构造函数:
      Point:: Point (const Point &pt)
      {
       xVal=pt. xVal;
       yVal=pt. yVal;
      } 
      如果一个类中有指针成员,使用缺省的复制构造函数初始化对象就会出现问题。为了说明存在的问题,我们假定对象A与对象B是相同的类,有一个指针成员,指向对象C。当用对象B初始化对象A时,缺省的复制构造函数将B中每一个成员的值复制到A的对应的成员当中,但并没有复制对象C。也就是说,对象A和对象B中的指针成员均指向对象C,实际上,我们希望对象C也被复制,得到C的对象副本D。否则,当对象A和B销毁时,会对对象C的内存区重复释放,而导致错误。为了使对象C也被复制,就必须显式定义复制构造函数。下面我们以string类为例说明,如何定义这个复制构造函数。

    例10-11
      class String
    {
     public:
      String(); //构造函数
      String(const String &s); //复制构造函数
      ~String(); //析构函数

      // 接口函数
      void set(char const *data);
      char const *get(void);

     private:
      char *str; //数据成员ptr指向分配的字符串
    };

    String ::String(const String &s)
    {
     str = new char[strlen(s.str) + 1];
     strcpy(str, s.str);
    }

    我们也常用无名对象初始化另一个对象,例如:
      Point pt = Point(10, 20);
      类名直接调用构造函数就生成了一个无名对象,上式用左边的无名对象初始化右边的pt对象。
      构造函数被调用通常发生在以下三种情况,第一种情况就是我们上面看到的:用一个对象初始化另一个对象时;第二种情况是当对象作函数参数,实参传给形参时;第三种情况是程序运行过程中创建其它临时对象时。下面我们再举一个例子,就第二种情况和第三种情况进行说明:
      Point foo(Point pt) 
      { 
       … 
       return pt;
      }
      void main()
      {
       Point pt1 = Point(10, 20);
       Point pt2;
       …
       pt2=foo(pt);
       …
      }
      在main函数中调用foo函数时,实参pt传给形参pt,将实参pt复制给形参pt,要调用复制构造函数,当函数foo返回时,要创建一个pt的临时对象,此时也要调用复制构造函数。

    缺省的复制构造函数
      在类的定义中,如果没有显式定义复制构造函数,C++编译器会自动地定义一个缺省的复制构造函数。下面是使用复制构造函数的一个例子:

    例10-12
      #include <iostream.h>
    #include <string.h>
    class withCC
    {
     public:
     withCC(){}
     withCC(const withCC&)
     {
      cout<<"withCC(withCC&)"<<endl;
     }
    };

    class woCC
    {
     enum{bsz = 100};
     char buf[bsz];
    public:
     woCC(const char* msg = 0)
     {
      memset(buf, 0, bsz);
      if(msg) strncpy(buf, msg, bsz);
     }
     void print(const char* msg = 0)const
     {
      if(msg) cout<<msg<<":";
      cout<<buf<<endl;
     }
    };

    class composite
    {
     withCC WITHCC;
     woCC WOCC;
    public:
     composite() : WOCC("composite()"){}
     void print(const char* msg = 0)
     {
      WOCC.print(msg);
     }
    };

    void main()
    {
     composite c;
     c.print("contents of c");
     cout<<"calling composite copy-constructor"<<endl;
     composite c2 = c;
     c2.print("contents of c2");
    }

      类withCC有一个复制构造函数,类woCC和类composite都没有显式定义复制构造函数。如果在类中没有显式定义复制构造函数,则编译器将自动地创建一个缺省的构造函数。不过在这种情况下,这个构造函数什么也不作。
      类composite既含有withCC类的成员对象又含有woCC类的成员对象,它使用无参的构造函数创建withCC类的对象WITHCC(注意内嵌的对象WOCC的初始化方法)。
      在main()函数中,语句:
      composite c2 = c;
    通过对象C初始化对象c2,缺省的复制构造函数被调用。
      最好的方法是创建自己的复制构造函数而不要指望编译器创建,这样就能保证程序在我们自己的控制之下。

    展开全文
  • 编译器永远不会把模板构造函数视为构造函数,即使客户没有自己定义拷贝构造函数,编译器也会生成一个默认的拷贝构造函数,这种情况同样存在于拷贝赋值函数和模板拷贝赋值函数。请看下面的例子:...
  • 拷贝构造函数复制构造函数从属于类,不属于对象,所以不能static,所以在拷贝构造函数中,可以对象.成员名访问。 cat(const cat& t) : catAge(t.catAge) { cout << "cat copy" << endl; } ...
  • 构造函数与拷贝构造函数

    千次阅读 多人点赞 2019-04-03 09:07:15
    拷贝构造函数和构造函数不能分开说,他们都是初始化对象的一种方法。但是我们这里用构造函数辅助说明拷贝构造函数,主要说说拷贝构造函数的声明,用途和使用注意事项。 众所周知,构造函数是一个初始化类对象的函数...
  • 复制构造函数与拷贝构造函数

    千次阅读 2014-09-16 11:39:03
    对于我来说,在写代码的时候能用得上复制构造函数的机会并不多,不过这并不说明复制构造函数没什么用,其实复制构造函数能解决一些我们常常会忽略的问题。  为了说明复制构造函数作用,我先说
  • 拷贝构造函数和重载赋值运算符设为private来禁止拷贝
  • 当对一个已知对象进行拷贝时,编译系统会自动调用一种构造函数 —— 拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。 #include <iostream> using namespace std; class Student {...
  • 1.什么是拷贝构造函数: CA(const CA& C)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型...
  • C++复制构造函数和拷贝构造函数

    千次阅读 2014-03-04 14:53:03
    拷贝构造函数参数的特点 赋值构造函数要申请内存。就像一般的构造函数一样。 而赋值操作,是已经申请好了内存。只是赋值。 对于一个类X,如果一个构造函数的第一个参数是下列之一: a) X& b) const X& c)...
  • C++拷贝构造函数详解

    万次阅读 多人点赞 2011-02-23 13:39:00
    什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a = 100; int b = a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。 下面看一个类对象...
  • 拷贝构造函数是对象被创建时调用,赋值函数只能被已经存在了的对象调用 1、拷贝构造函数的使用 1) 一个对象以值传递的方式传入函数体; 2) 一个对象以值传递的方式从函数返回; 3) 一个对象需要通过另外一个对象...
  • 在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。 但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个...
  • 注意:如果类中没有说明拷贝构造函数,则系统自动生成一个缺省复制构造函数,作为该类的公有成员 下面有一个String的例子,说明拷贝构造函数的使用: #ifndef _STRING_H_ #define _STRING_H_ class String { p
  • 什么时候需要拷贝构造函数和赋值构造函数? 每个C++类只有一个析构函数,但可以有多个构造函数和多个赋值函数。其中构造函数包括默认构造函数(无参,或参数全有默认值),拷贝构造函数。在编程时,如果程序员不...
  • C++ 拷贝构造函数 赋值构造函数 拷贝构造函数和赋值构造函数的异同
  • 什么是复制构造函数复制构造函数是一种特殊的构造函数,具有一般构造函数的所有特性 ,它的形参是本类的对象的引用,比如(类名 对象名)。它的作用是使用一个已经存在的对象(此对象由复制构造函数的参数决定...
  • 拷贝构造函数

    2016-11-16 10:21:05
    拷贝构造函数拷贝构造函数 一什么是拷贝构造函数 ...所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 98,439
精华内容 39,375
关键字:

复制拷贝构造函数