精华内容
下载资源
问答
  • 类的赋值构造函数和复制构造函数

    千次阅读 2016-10-31 22:41:49
    C++的初学者经常会对复制构造函数一知半解,我曾经对复制构造函数和赋值函数就很是迷茫。闲来无事,整理一下,一个对象的赋值构造函数和赋值构造函数。整体的说一下,复制构造函数和赋值构造函数的相同点是: 赋值...

    C++的初学者经常会对复制构造函数一知半解,我曾经对复制构造函数和赋值函数就很是迷茫。闲来无事,整理一下,一个对象的复制构造函数和赋值构造函数。整体的说一下,复制构造函数和赋值构造函数的相同点是: 赋值运算符和复制构造函数都是用已存在的B对象来创建另一个对象A;
    最大的不同在于:赋值构造函数处理两个已有对象,即赋值前B应该是存在的;复制构造函数是生成一个全新的对象,即调用复制构造函数之前A不存在。

    - 类

    首先,介绍一下类:类是数据以及用于操纵该数据的方法(函数)的集合,是逻辑上相关函数与数据的封装。它是对所要处理问题的抽象描述,它把数据(事物的属性)和函数(事物的行为/操纵)封装为一个整体。
    类的定义格式如下:

    class 类名{
    private//私有数据和函数
    protect:
    //保护数据和函数
    public//公有数据和函数
    }

    C++中面向对象的思想是对对象的属性(成员变量)进行操作,应该通过对象的方法(成员函数)来进行,对象的方法是对象和外部的接口。
    这里顺便说一句成员函数的定义通常的格式是:

    范围值类型 类名::函数名(参数表)
    {
    //函数体
    }

    很多基础比较差的程序员,“::”符号不知道是什么意思(虽然知道也没什么用),只知道怎么用!运算符“::”成为作用域解析运算符,它指出该函数是属于哪一类的成员函数。当然也可以在类的定义中直接定义函数。所以有的时候也不用“::”符号.

    对象即类的实例。创建类的对象可以常用的两种方法:
    1、直接创建对象,比如CObject类的一个对象 CObject object;(注意这里的object是一个临时对象)
    2、采用动态创建类的对象的方法,当然遍历同样也可以动态创建。

    - 复制构造函数

    上面说到了类,也说到了对象的创建,那么同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制是完全可行的。这个复制的过程只需要把数据成员复制过来即可,而函数成员是可以共用的,同一个类的任何对象都可以共用这个类的函数成员。
    在建立对象时可以用同一类的另一个对象来初始化该对象,这时所用的构造函数成为复制构造函数。像X::X(X&),这种形式,且只有一个参数——同类的对象,采用的是引用的方式。不能用X::X(X)这种形式的构造函数,如果把一个真实的类的对象作为参数传递到复制构造函数,会引起无穷的递归。那很多人有不解,平常我创建类的对象的时候也没有写复制构造函数呀,为什么也没有报错呢?原因是如果没有定义的话,那么编译器生成默认复制构造函数;如果定义了地自己的复制构造函数,则默认的复制构造函数就不存在了。(这一点与析构函数不同,即使自己定义了析构函数,默认的析构函数仍然存在)
    复制构造函数在以下3中情况下会被调用:

    1. 当用一个对象去初始化同类的另一个对象时。
    CObject o2(o1);
    //
    CObject o2=o1;
    1. 如果某函数有一个形参是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用。
    void Fun(CObject object)
    {
        object.a=1;
    }
    CObject object;
    Fun(object);
    //CObject 的复制构造函数被调用,生成形参,在内存新建一个局部对象,并把实参复制到新的对象中。
    1. 如果函数的返回值是类CObject的对象,则函数返回时,CObject的复制构造函数被调用。理由也是建立一个临时对象,再返回调用者。
    CObject Fun()
    {
        CObject object;
        return object; //在此处调用了CObject(object)
    }
    int main()
    {
        CObject object; //此处是一个局部对象,也是一个临时对象
        object = Fun(); 
        return 0;
    }

    有人可能会问,为什么不直接用要返回的局部对象呢?因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存。所以编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓返回对象,实际上就是调用复制构造函数把该对象的值复制到临时对象中。

    -赋值构造函数

    首先看一段代码:

    int main()
    {
        CObject theObjectOne;
        theObjectOne.Init(100);
    
        CObject theObjectTwo;
        theObjectTwo.Init(200);
    
        theObjectTwo = theObjectOne;
        //此操作是对象赋值操作,更深一步的操作时将被复制对象theObjectTwo对象原有的内容清除,并用theObjectOne对象里的内容填充。
        return 0;
    }

    “=”就是讲一个对象里的内容到另一个对象中去,这其中涉及到对象原有内容的丢弃和新内容的复制。由于对象内容包含指针,将造成不良的后果:指针的值被丢弃了(指针内容丢失了也就是说指针地址丢失了,但是改地址中所存储的内容没有丢失,但指针指向的内容并未释放。指针的值被复制了,但是指针所指内容并未复制,这就要出大事儿了)。所以如果类的对象中有指针的话,此处的“=”绝对不能是我们平常理解的简简单单的等于号,这个“=”必须重载,而这个“=”的重载函数就是我们要讨论的赋值构造函数。

    我们再来理解两个概念:值拷贝和位拷贝,位拷贝拷贝的是地址;值拷贝拷贝的是内容。理解了这两个概念我们再看一段代码:
    定义一个string类,但是不实现其他的成员函数

    Class String
    {
    public:
               String(const char *ch = NULL); //默认构造函数
               String(const String &str); //复制构造函数
               ~String();
               String &operator=(const String &str);//赋值构造函数
     privat:
                char *m_data;
    }
    
    int main()
    {
        String strA;
        strA.m_data=L"Windows";
        String strB;
        strB.m_data=L"Linux";
    
        strB.m_data = strA.m_data;
    }

    如果“=”未重写赋值构造函数的话,将strA赋给strB;则编译器会默认进行位拷贝,即strB.m_data = strA.m_data;
    则strA.m_data和strB.m_data都指向了同一块区域,虽然strA.m_data指向的内容会改变为“Linux”,但是会出现这样的问题:
    (1)strB.m_data原来指向的内存区域未释放,造成内存泄露。
    (2)strB.m_data和strA.m_data指向同一块区域,任何一方改变都会影响另一方。
    (3)当对象被析构时,strA.m_data被释放两次。

    如果“=”重写了复制构造函数后,strB.m_data = strA.m_data;进行的是值拷贝,会将strA.m_data的内容赋给strB.m_data,strB.m_data还是指向原来的内存区域,但是其内容改变。

    所以在我理解起来,赋值构造函数其实就是对“=”的重载。缺省的赋值构造函数采用的是“位拷贝”而非“值拷贝”的方式实现。如果类中含有指针变量,那么赋值构造函数如果不重载的话肯定会出错。理解到这里,我们再回过头看一下复制构造函数,其实也是一样的道理,缺省的复制构造函数采用的也是“位拷贝”的方式实现,所以如果类中含有指针变量的话我们也需要重写复制构造函数。
    总而言之一句话,什么把你弄的这么头疼来看这篇文章,归根到底就是——指针。

    最后附上一段不错的理解复制构造函数和赋值构造函数的代码:

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class String  
    {
        public:
            String(const char *str);
            String(const String &other);
            String & operator=(const String &other);
            ~String(void); 
        private:
            char *m_data;
    };
    
    String::String(const char *str)
    {
        cout << "自定义构造函数" << endl;
        if (str == NULL)
        {
            m_data = new char[1];
            *m_data = '\0';
        }
        else
        {
            int length = strlen(str);
            m_data = new char[length + 1];
            strcpy(m_data, str);
        }
    }
    
    String::String(const String &other)
    {
        cout << "自定义复制构造函数" << endl;
        int length = strlen(other.m_data);
        m_data = new char[length + 1];
        strcpy(m_data, other.m_data);
    }
    
    String & String::operator=(const String &other)
    {
        cout << "自定义赋值函数" << endl; 
    
        if (this == &other)
        {
            return *this;
        }
        else
        {
            delete [] m_data;
            int length = strlen(other.m_data);
            m_data = new char[length + 1];
            strcpy(m_data, other.m_data);
            return *this;
        }
    }
    
    String::~String(void)
    {
        cout << "自定义析构函数" << endl; 
        delete [] m_data;
    }
    int main()
    {
        cout << "a(\"abc\")" << endl;
        String a("abc");
    
        cout << "b(\"cde\")" << endl;
        String b("cde");
    
        cout << " d = a" << endl;
        String d = a;
    
        cout << "c(b)" << endl;
        String c(b);
    
        cout << "c = a" << endl;
        c = a;
    
        cout << endl;

    执行结果

    a(“abc”)
    执行自定义构造函数
    b(“ced”)
    执行自定义构造函数
    d=a
    执行自定义复制构造函数
    c(b)
    执行自定义复制构造函数
    c=a
    执行自定义赋值函数

    执行自定义析构函数
    执行自定义析构函数
    执行自定义析构函数
    执行自定义析构函数

    说明几点

    1. 赋值函数中,上来比较 this == &other 是很必要的,因为防止自复制,这是很危险的,因为下面有delete []m_data,如果提前把m_data给释放了,指针已成野指针,再赋值就错了

    2. 赋值函数中,接着要释放掉m_data,否则就没机会了(下边又有新指向了)

    3. 拷贝构造函数是对象被创建时调用,赋值函数只能被已经存在了的对象调用

      注意:String a(“hello”); String b(“world”); 调用自定义构造函数

           String c=a;调用拷贝构造函数,因为c一开始不存在,最好写成String c(a);
      
    展开全文
  • 一看标题,这个有点过,甚至有点哗众取宠性质,因为从C++的语言角度,其实构造函数及复制构造函数是并不存在虚函数形式的,原因是为何,先把C++ Primer中的原话引述上来:……只有析构函数应定义为虚函数,构造函数...

      一看标题,这个有点过,甚至有点哗众取宠性质,因为从C++的语言角度,其实构造函数及复制构造函数是并不存在虚函数形式的,原因是为何,先把C++ Primer中的原话引述上来:……只有析构函数应定义为虚函数,构造函数不能定义为虚函数。构造函数是在对象完全构造之前运行的,在构造函数运行的时候,对象的动态类型还不完整。

            甚至有人一看见这文章标题,就要骂我,构造函数本来不允许虚,而且这么久还没见过虚构造函数,而且就如上边提到Lippman的权威著作也写明。更甚至者,网上有人对于虚复制构造函数这一词的反应:“我学了 10 年 C++,还没听说过有“虚复制构造函数”。”笔者看了立刻第一反应:一个不愿意看书又自大兼口没遮拦、误导新手的糊涂虫!

            有某些情况是需要用到虚复制构造函数等等,例如,想象一下,如果有一项功能,程序为一个基类+N个派生类,而基类充当的是一个抽象接口类的角色,而到各派生类才会依据数据来建立各自的对象,这个时候虚拟构造函数或虚复制构造函数就有用处了,就如More Effective C++  条款M25的例子,一个新闻类(一个接口基类),有图片(派生类)、文字等不同东西(不同的派生类),平时不用时存储在磁盘上,不同的东西当然放的地方有不同了,构造函数就是利用istream读取,所以,当要对不同类型的新闻内容操作时,就会建立不同的对象,这样就需要根据不同内容建立不同对象,这时,虚复制构造函数就产生用处了(当然,某部分人要违反面向对象封装这种设计哲学,又不顾效率的话,有接口不用,特意直接分别用派生类来构建对象的这个情况,那么我想说:“C++真的不适合你。”)。

            C++不允许直接对构造函数声明为虚啊,包括复制构造函数,那么如何实现呢?其实是有方法的,笔者在两本书中也看见过虚复制构造函数的实现方法,其一是C++大师Scott Meyers写的More Effective C++ 的条款 M25,就正正是讨论这个问题!而另外一本则是一本一个美国花旗银行前开发部副总写的C++初学者教程,名字是《21天学通C++》(在GOOGLE上输入虚复制构造函数就会有人说在看这个书,看到这部分不明何解,之后还有SB回复上边第二段的蓝色字部分。如果一个没编程基本的人,要说21天能完全掌握C++就有点扯,但老实说,那本书的教学内容广度和深度及对学习者编程习惯的强调要比国内相当多的C++教程好得多),扯远了,下边说回来,既然C++语法中没有直接方法实现,但又有需要用到的场合,那么如何实现呢?其实好简单,通过一个虚函数方法建议一个新的对象,如下边代码所示:

    class A
    {
    public:
    	A(int);
    	A(A const &rhs);
    
    	~A();
    
    	/* 如果基类只是一个接口类,此行还可以直接改为virtual  A* clone() =0;具体行8、为于具体类中再定义 */
    
    	virtual  A* clone() {  return  new A (*this)  }   
    private: 
    };
    
    class B:public A
    {
    public:
    	B(int);
    	B(B const &rhs);
    
    	virtual B* clone(){  return  new  B(*this)  } 
    private:
    };

     上边代码第8和第16行就是一个虚复制构造函数机制,关键就是那个clone方法中的功能语句: return   new   B (*this),这句的意思是:建立一个某个类的对象(此例中就是B类),并且,在新建这个对象时,将*this作参数传给复制构造函数,因为每定义一个对象对,C++就会依据类型调用相应的构造函数或者复制构造函数,而 new   B (*this)这种形式,正好就是复制构造B(B const &rhs);的形式,如果看不明白,可以看下边例子:其实 return    new   B (*this)这个语句,就类似于:X *p=new X;   X*q=new X(*p);当然,作用不一样,但原理相似,例如:

    红线的一句在新建对象时就会调用复制构造函数了!

            而虚复制构造函数则是利用虚函数的性质(能覆盖基类的特质),执行一个创建某个类的对象的方法,由于创建某个类的对象时,会执行某一个类的构造函数或复制构造函数,如果该类是一个接口类的派生类,则和虚函数实现达到目的了,而就算是一般类的派生类,也会在执行基类的复制构造后自动执行派生类的复制构造,这样同样达到目的。

            虚构造函数可以如上边提到的虚复制构造函数一样处理,其实本文提到的虚复制构造函数,我个人觉得可以说成是“利用虚函数及新建对象的机制实现构造函数及虚构造函数的多态性。”只不过,因为其能够实现虚复制构造函数这一种性质,所以人们就将其说成虚复制构造函数、虚构造函数。

            说了一晚的虚复制构造函数,下边就说说虚构造函数了,其实差别不大,只要将virtual B* clone(){  return new  B(*this)  }这句改成构造函数的形式,在本例中就是virtual B* clone(){  return  new  B()  }。

            有了虚构造函数嘛,如何用呢,当然是用指针来调用了,函数的类型也是一个返回值为指针的类型嘛,而且对虚函数起作用的通常也是指针。

            具体调用格式嘛:TYPE *指针名=new 派生类;    指针->clone();(TYPE要是基类的,否则搞这么多东西后做些违反设计哲学的东西无意义)。

            值得注意的地方:

            由于这样的过程替代了 X *x=new X;这个过程,所以在使用后注意记得释放指针,万分注意,当然,这个可以在析构中实现,当然,析构可以直接是虚的,所以没什么难度。

             文章就到此,本文只为记录一下个人所了解的情况,至于某些说自己学了多少年C++,没听过虚构造函数的人如果强行要辩论说虚构造、虚复制构造没可能、没用的话,先去看清楚More Effective C++的M25条款,还有意见就去找顶级C++大师Scott Meyers反映,本人得知该方法某程度上也是从那位大量的杰作中得知的,到目前为止亦相信该实现方法的正确性,要是你能辩倒Scott Meyers的话,可能下一版More Effective C++的感谢名单中会有你的大名。

    转载自:http://blog.csdn.net/dicky3651/article/details/5421662


    展开全文
  • 1.构造函数和复制构造函数相关的初始化 最近由于项目比较闲,自己自学了一些C++的知识,也在博客上总结一下。 首先理解“初始化”和“赋值”,一个新对象创建时,才会有初始化操作,初始化操作出现在构造函数中;而...

    1.构造函数和复制构造函数相关的初始化

    最近由于项目比较闲,自己自学了一些C++的知识,也在博客上总结一下。

    首先理解“初始化”和“赋值”,一个新对象创建时,才会有初始化操作,初始化操作出现在构造函数中;而赋值是用来修改一个已经存在的对象的值,出现在operator=操作函数中。

    1.1复制构造函数

    用同一个类产生的另外一个对象的值来为将要被创建的对象进行初始化,执行这样的初始化操作的构造函数也被称为复制构造函数,它的格式如下:
      class Complex 
      {
      public:
      	Complex(double, double);//普通构造函数
      	Complex(const Complex&);//复制构造函数
      };
    如果我们没有在类中声明一个复制构造函数,编译器也会为我们合成一个,这个缺省的复制构造函数会将新对象中的每个数据成员初始化为源对象中相应成员的值。

    1.1.1缺省的复制构造函数的行为是否符合我们的会符合我们的要求

      普通的:)
      class Complex
      {
      private:
      	double real_d;
      	double imag_d;
      public:
      	Complex(double r, double i):real_d(r), imag_d(i){}
      //....
      };

    如上这种没有明确声明的复制构造函数,它的行为就是复制两个数据成员,这符合我们预期的操作,是ok

      (特别的:)
      class String 
      {
      private:
      	char* data;
      public:
      	String(const char* cp = "");
      	~String() {delete [] data;}	
      };
      String::String(const char* cp):data(new char[strlen(cp)+1])
      {
      	strcpy(data, cp);
      }

    如上这种缺省的复制构造函数就不符合我们的预期了,应为如果有多个String对象,它会指向同一块内存,如果释放一个String对象,其它的也会被释放所以我们必须明确的声明复制构造函数:

      class String 
      {
      private:
      	char* data;
      public:
      	String(const char* cp = "");
      	String(const String&);
      	~String() {delete [] data;}	
      };
      String::String(const String& s):data(new char[strlen(s.data)+1])
      {
      	strcpy(data, s.data);
      }

    这样就能确保每个String都会拥有一份数据的私有拷贝

    对于缺省的复制构造函数是否能够正常工作,并没有一个通用的规则。从经验中得到的方法就是:对于包含指针的类要特别注意,如果被指向的对象是“属于”该类产生的对象,那么缺省的复制构造函数就有可能是错误的,因为它只是简单的复制了指针,而不是指针所指向的对象。复制构造函数在编码过程中是不可被忽略的!

    1.2类成员的初始化

    当类中的某个数据成员本身也是一个类对象时,我们应该避免用赋值操作来为该成员进行初始化,如下:

      class Employee 
      {
      private:
      	String name;
      public:
      	Employee(const String&);
      };
      
      Employee::Employee(const String& n)
      {
      	name = n;//效率不高
      }

    这样做虽能得到正确结果,但效率并不高,因为一个新Employee对象被创建时,成员name先将会被String的缺省构造函数所初始化,而在Employee的构造函数中又会被初始化一次,所以这样做效率不高。我们可以把它合并成一步如下:

      class String 
      {
      public:
      String();
      	String(const String&);
      };
      class Employee 
      {
      private:
      	String name;
      public:
      	Employee(const String&);
      };
      Employee::Employee(const String& n):name(n){}

        我们用String::String(n),这样编译器就只会用一次函数调用来初始化namne这个成员,在效率上会有30%的提升。同样,那些内建类型的成员也可以用这种初始化列表的方式来进行初始化,这样做会使代码的可读性更高。

    1.3成员初始化的顺序

        C++中规定:一个类中的成员的初始化顺序和它在类中被声明的顺序(而不是构造函数定义的顺序)必须是一致的。忽略这种顺序在一些情况下不会有影响,但在某些场合下,就会导致问题,比如:某个成员的初始化过程中使用了另外成员的值,那么“另外成员”的初始化就必须在“某个成员”的前面了。一般我们最好在头文件把这种顺序记录下来,这样会给我们带来便利。

    1.4类成员的引用

        如果在类中某个非静态数据成员是一个引用的话,因为所有的引用都必须被明确的初始化,所以我们必须在该类的每个构造函数中都使用初始化语法:

      Class Payroll_entry
      {
      Private:
      Employee& emp;
      Public:
      Payroll_entry(Employee&);
      };
      Payroll_entry::Payroll_entry(Employee& e){}//编译错误:“emp”必须被初始化

        所以我们在设计类的时候,为什么要将emp声明为Employee&,而不是Employee*呢?因为声明为引用会有下面两方面的限制:

    1.我们必须在创建emp的时候就必须初始化

    2.一旦绑定初始化后,emp就能不改变









    展开全文
  • 派生类构造函数格式如下表示: <派生类构造函数名>(<参数表>) : <成员初始化表> {  <派生类构造函数的函数体> } 其中,<派生类构造函数名>同该派生类的类名。<成员初始化表>中包含如下的初始化项: ...

    ◆由于构造函数不能被继承,因此,派生类的构造函数中除了对派生类中数据成员进行初始化外,还必须通过调用直接基类的构造函数来对基类中数据成员初 始化,一般地将,对派生类中数据成员初始化放在该派生类构造函数的函数体内,而调用基类构造函数的基类中数据成员初始化放在该构造函数的成员初始化表中。 派生类构造函数的格式如下表示:
    <派生类构造函数名>(<参数表>) : <成员初始化表>
    {
      <派生类构造函数的函数体>
    }
    其中,<派生类构造函数名>同该派生类的类名。<成员初始化表>中包含如下的初始化项:
    ①基类的构造函数,用来给基类中数据成员初始化;
    ②子对象的类的构造函数,用来给派生类中子对象的数据成员初始化;
    ③派生类中常成员的初始化。
    <派生类构造函数的函数体>用来给派生类中的数据成员初始化。
    派生类构造函数的调用顺序如下:
    ①基类构造函数;
    ②子对象的构造函数;
    ③成员初始化表中其他初始化项;
    ④派生类构造函数的函数体。

    派生类中析构函数
    由于析构函数也不能继承,因此派生类的析构函数中将调用直接基类的析构函数。执行派生类析构函数的顺序正好与指向派生类的构造函数的顺序相反。先调用派生类的析构函数,再调用子对象类的析构函数,最后调用直接基类的析构函数。


    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A(int x=0)
    	{
    		a=x;
    		cout<<"A Constructor"<<endl;
    	}
    	A(A&t)
    	{
    		cout<<"A COPY"<<endl;
    	}
    	~A()
    	{
    		cout<<"A Destructor"<<endl;
    	}
    	void display()
    	{
    		cout<<a<<endl;
    	}
    private:
    	int a;
    };
    class B:public A
    {
    public:
    	B(int i):A(i)//B(A i):aa(i)
    	{
    	    cout<<"B Constructor"<<endl;
    	}
    	~B()
    	{
            cout<<"B Destructor"<<endl;
    	}
    private:
    	A aa;
    };
    int main()
    {
    
    	
    	B b(33);
    	b.display();
       return 0;
    }
    

    结果:

    A Constructor

    A Constructor

    B Constructor

    33

    B Destructor

    A Destructor

    A Destructor

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A(int x=0)
    	{
    		a=x;
    		cout<<"A Constructor"<<endl;
    	}
    	A(A&t)
    	{
    		cout<<"A COPY"<<endl;
    	}
    	~A()
    	{
    		cout<<"A Destructor"<<endl;
    	}
    	void display()
    	{
    		cout<<a<<endl;
    	}
    private:
    	int a;
    };
    class B:public A
    {
    public:
    	B(int i):aa(i)
    	{
    	    cout<<"B Constructor"<<endl;
    	}
    	~B()
    	{
            cout<<"B Destructor"<<endl;
    	}
    private:
    	A aa;
    };
    int main()
    {
    
    	
    	B b(33);
       
       return 0;
    }
    

    结果:

    A Constructor

    A Constructor

    B Constructor

    B Destructor

    A Destructor

    A Destructor

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A(int x=0)
    	{
    		a=x;
    		cout<<"A Constructor"<<endl;
    	}
    	A(A&t)
    	{
    		cout<<"A COPY"<<endl;
    	}
    	~A()
    	{
    		cout<<"A Destructor"<<endl;
    	}
    	void display()
    	{
    		cout<<a<<endl;
    	}
    private:
    	int a;
    };
    class B:public A
    {
    public:
    	B(A i):aa(i)
    	{
    	    cout<<"B Constructor"<<endl;
    	}
    	~B()
    	{
            cout<<"B Destructor"<<endl;
    	}
    private:
    	A aa;
    };
    int main()
    {
    	
    	
    	B b(33);
    	
       
       return 0;
    }
    

    结果:

    A Constructor

    A Constructor

    A COPY

    B Constructor

    A Destructor

    B Destructor

    A Destructor

    A Destructor

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A(int x=0)
    	{
    		a=x;
    		cout<<"A Constructor"<<endl;
    	}
    	A(A&t)
    	{
    		cout<<"A COPY"<<endl;
    	}
    	~A()
    	{
    		cout<<"A Destructor"<<endl;
    	}
    	void display()
    	{
    		cout<<a<<endl;
    	}
    private:
    	int a;
    };
    class B:public A
    {
    public:
    	B(A &i):aa(i)//B(A i):aa(i)
    	{
    	    cout<<"B Constructor"<<endl;
    	}
    	~B()
    	{
            cout<<"B Destructor"<<endl;
    	}
    private:
    	A aa;
    };
    int main()
    {
    	
    	A a(3);
    	
    	B b(a);
    	b.display();
       
       return 0;
    }

    结果:

    A Constructor

    A Constructor

    A COPY

    B Constructor

    0

    B Destructor

    A Destructor

    A Destructor

    A Destructor

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A(int x=0)
    	{
    		a=x;
    		cout<<"A Constructor"<<endl;
    	}
    	A(A&t)
    	{
    		cout<<"A COPY"<<endl;
    	}
    	~A()
    	{
    		cout<<"A Destructor"<<endl;
    	}
    	void display()
    	{
    		cout<<a<<endl;
    	}
    private:
    	int a;
    };
    class B:public A
    {
    public:
    	B(A i):aa(i)//B(A i):aa(i)
    	{
    	    cout<<"B Constructor"<<endl;
    	}
    	~B()
    	{
            cout<<"B Destructor"<<endl;
    	}
    private:
    	A aa;
    };
    int main()
    {
    	
    	A a(3);
    	
    	B b(a);
    	b.display();
       
       return 0;
    }

    结果:

    A Constructor

    A COPY

    A Constructor

    A COPY

    B Constructor

    A Destructor

    0

    B Destructor

    A Destructor

    A Destructor

    A Destructor

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A(int x=0)
    	{
    		a=x;
    		cout<<"A Constructor"<<endl;
    	}
    	A(A&t)
    	{
    		cout<<"A COPY"<<endl;
    	}
    	~A()
    	{
    		cout<<"A Destructor"<<endl;
    	}
    	void display()
    	{
    		cout<<a<<endl;
    	}
    private:
    	int a;
    };
    class B:public A
    {
    public:
    	B(){cout<<"B Constructor"<<endl;}
    	~B()
    	{
            cout<<"B Destructor"<<endl;
    	}
    	
    private:
    	A aa;
    };
    int main()
    {	
    	B b; 
       return 0;
    }

    结果:

    A Constructor

    A Constructor

    B Constructor

    B Destructor

    A Destructor

    A Destructor














    展开全文
  • 在CSDN论坛中有朋友问到这样一个问题:自定义类mystring中,定义了该类的复制构造函数 mystring::mystring(mystring&another) { if(str!=NULL) delete[]str; str = new char[strlen(another.str)+1]; strcpy(str...
  • 关于复制构造函数

    2007-05-08 15:47:00
    今天在上课时听王石锟说起“复制构造函数”这个概念。 如果一个类包含有指向另一个对象的指针数据成员,应为该类提供复制构造函数,通常还要提供一个重载的赋值运算符,以确保对象正确赋值。 为什么要这样做呢,如果...
  • 近期在学C++的过程中,...一、什么是复制构造函数  一种特殊的重载构造函数,他的形式很固定:[类名]::[构造函数名](const [类名] &amp;[参数]); (参数是本类型的一个引用变量)  实际代码:MyString::...
  • Tips: 软件工程知识 ...可以阻止对象的复制,要达到此目的,只需把这个类重载的赋值运算符和复制构造函数声明为private。 1. #include #include using namespace std; class A{ public: A(in
  • 有些看着是理所当然的东西有时候往往被我们忽略其深层次的意义,比如复制构造函数的形参被设计为引用类型,这是为什么捏?   众所周知,c++中类没有给出复制构造函数的话,编译器会自动补上一个,然而对于深拷贝来...
  • C++引用与复制构造函数

    千次阅读 2017-02-07 20:18:38
    引用 ◆ 1、什么是引用? 引用又称别名(alias),是一种非常...引用主要用于函数之间的数据传递。 引用定义的格式为:  类型 &引用变量名=已定义过的变量名; 例如:  double number ;  double &newnum=num
  • Qt中为什么函数可以直接返回...注:本文涉及到的主要知识点为:C++的复制构造函数的作用 一、在C++中返回参数的基础知识 在C++中,函数返回类型有,基本数据类型,结构体,对象,指针。下面举例几个正确使用C++...
  • 这张图摘来酷壳(陈浩大神的博客),感觉他总结的这个图比较经典,至少我是深有感触,从一开始不顾任何规则乱写,到四处碰壁开始明白c++需要编程规则。...首先解释下什么是复制控制,复制构造函数、赋值操
  • C++ 拷贝构造函数和赋值构造函数

    万次阅读 多人点赞 2019-04-06 16:43:35
    在C++中复制控制是一个比较重要的话题,主要包括复制构造函数、重载赋值操作符、析构函数这三部分,这三个函数是一致的,如果类需要析构函数,则它也需要复制操作符 和 复制构造函数,这个规则被称为 C++的“三法则...
  • 格式如下:复制构造 IntCell::IntCell(const IntCell &rhs) ...{} 赋值重载 const IntCell& IntCell::operator=(const IntCell& rhs)...{if(this!=&rhs)storedValue=rhs.storedValue;return *this;}那么...
  • 构造函数

    千次阅读 2019-06-02 21:32:29
    构造函数 一、定义:每个类都分别定义了它的对象被初始化的方式。类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数就叫构造函数。(构造函数的任务就是初始化类对象的数据成员,无论何时只要类的...
  • 拷贝构造函数和赋值函数

    千次阅读 2014-08-21 10:25:37
    在C++中复制控制是一个比较重要的话题,主要包括复制构造函数、重载赋值操作符、析构函数这三部分,这三个函数是一致的,如果类需要析构函数,则它也需要复制操作符 和 复制构造函数,这个规则被称为 C++的“三法则...
  • 拷贝构造函数: TestClass(const TestClass&); // 形式1 TestClass(TestClass&); // 形式2 TestClass(TestClass); // 形式3 形式1是最常见的构造函数声明,这也是最好的书写方式。 ...
  • 一丶构造函数 构造函数的作用是在对象被创建时利用特定的值构造对象,对对象进行初始化,即对数据成员进行初始化,构造函数也是类的成员函数,它具有一些特性: 1.构造函数的名字和类的名字相同 2.构造函数可以...
  • 拷贝构造函数

    2013-10-08 18:10:14
    拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的参数(对象的引用)是不可变的(const类型)。此函数经常用在函数调用时用户定义类型的值传递及返回。...
  • 声明和定义构造函数 构造函数:专门用于构造新对象、将值赋给它们的数据成员。 由于Stock对象提供3个值,因此应为构造函数提供3个参数。 Stock(const string &co, long n = 0, double pr = 0.0) 第一个参数:...
  • c++ 拷贝构造函数

    2014-02-24 16:46:03
    拷贝构造函数(深拷贝vs浅拷贝) 类对象之间的初始化是由类的拷贝构造函数完成的。它是一种特殊的构造函数,它的作用是用一个...一、拷贝构造函数定义格式 类名::拷贝构造函数名(类名& 引用名) 例如: Tdate ::
  •  构造函数的形参是本类对象的引用而不是本类的对象是为了防止引起拷贝构造函数无休止地递归调用。 一、缺省拷贝构造函数。 class A{} A a(3,2); A b = a ;//调用拷贝构造函数 b = a//非初始化操作,为赋值操作...
  • 构造函数小结

    2014-07-30 08:52:00
    类中构造函数的作用是对对象初始化,它是一种特殊的成员函数,不需要用户来调用它,而是在建立对象时自动执行。构造函数的名字必须与类名相同,它不具有任何类型,不返回任何值。构造函数的功能由用户定义,用户根据...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 113,764
精华内容 45,505
关键字:

复制构造函数格式