精华内容
下载资源
问答
  • 以下几种情况会调用拷贝构造函数 1. 以一个对象初始化另一个对象 2. 函数以某个对象为参数 3. 函数返回某个对象 4. 初始化序列式容器的元素 2. void test2() { Test t1(1,2); Test t2; t2=t1;//调用的不是拷贝构造...
  • 拷贝构造函数 都是在什么情况下调用?... 什么时候调用拷贝构造函数 ** */ #include using namespace std; //日期类 class Date{ public: int year,month,day; Date(){//无参构造 cout 日期类的构造函数" <<



    拷贝构造函数 都是在什么情况下调用???


    /*
     **
         什么时候调用拷贝构造函数
     **
     */
    
    #include <iostream>
    using namespace std;
    
    
    //日期类
    class Date{
        
    public:
        int year,month,day;
        Date(){//无参构造
            cout << "日期类的构造函数" << endl;
        }
        
        ~Date(){
            cout << "日期的析构函数" << endl;
        }
       
    #pragma 拷贝构造函数里面的参数类型必须是引用
        Date (const Date& d3){
            this->year = d3.year;
            this->month = d3.month;
            this->day = d3.day;
            cout << "拷贝构造函数Date (Date& d3)" << endl;
            
        }
        
    };
    
    
    void fa(Date d){//在这个参数可以看出会创建一个新的临时变量d对象,相当于Date d = d;后面的d是传进来的,也就是通过相同类型对象来创建对象,所以这里会调用拷贝构造函数。
        
    }
    
    //主函数
    int main(){
        Date d;
        cout << "===== 1 =====" << endl;
        
        fa(d);
        
        cout << "===== 2 =====" << endl;
        return 0;
    }


    运行结果:

    日期类的构造函数
    ===== 1 =====
    拷贝构造函数Date (Date& d3)   //fa函数进栈
    日期的析构函数                 //弹栈
    ===== 2 =====
    日期的析构函数
    Program ended with exit code: 0

    再写一个参数是引用类型的函数 fb 如下:

    void fb(Date &d){ //这里是给传进的参数创建别名(引用)不会创建新的对象
        
    }

    修改主函数如下:

    /主函数
    int main(){
        Date d;
        cout << "===== 1 =====" << endl;
        
        fa(d);
        
        cout << "===== 2 =====" << endl;
        
        fb(d);
        cout << "===== 3 =====" << endl;
        return 0;
    }

    运行结果可以看出在2和3之间并没有创建新的对象


    日期类的构造函数
    ===== 1 =====
    拷贝构造函数Date (Date& d3)
    日期的析构函数
    ===== 2 =====
    ===== 3 =====
    日期的析构函数
    Program ended with exit code: 0


    再创建一个参数类型是指针类型的函数 fc 如下:

    void fc(Date *d){ //传进来的参数是指针类型
        //这里创建了一个指针变量放在栈里面了,但是没有创建新的对象,指针指向的是传进来的对象的地址
    }

    主函数如下:

    //主函数
    int main(){
        Date d;
        cout << "===== 1 =====" << endl;
        
        fa(d);
        
        cout << "===== 2 =====" << endl;
        
        fb(d);
        cout << "===== 3 =====" << endl;
        fc(&d);
        cout << "===== 4 =====" << endl;
        return 0;
    }

    运行的结果如下:

    日期类的构造函数
    ===== 1 =====
    拷贝构造函数Date (Date& d3)
    日期的析构函数
    ===== 2 =====
    ===== 3 =====
    ===== 4 =====
    日期的析构函数
    从运行结果3和4之间也可以知道并没有创建新的对象。


    再来,创建一个返回值类型是Date ,参数是引用类型的函数 fd 如下:

    Date fd(Date &r){
        return r;
    }

    主函数中:

    //主函数
    int main(){
        Date d;
        cout << "===== 1 =====" << endl;
        fa(d);
        cout << "===== 2 =====" << endl;
        fb(d);
        cout << "===== 3 =====" << endl;
        fc(&d);
        cout << "===== 4 =====" << endl;
        fd(d);
        cout << "===== 5 =====" << endl;
        
        return 0;
    }

    运行结果如下:

    日期类的构造函数
    ===== 1 =====
    拷贝构造函数Date (Date& d3)
    日期的析构函数
    ===== 2 =====
    ===== 3 =====
    ===== 4 =====
    拷贝构造函数Date (Date& d3)
    日期的析构函数
    ===== 5 =====
    日期的析构函数
    Program ended with exit code: 0


    从运行结果可知在4和5之间调用了拷贝构造函数创建对象了。那fd这个函数哪里创建对象了呢?  参数是引用类型,只是起别名,所以并没有创建对象。是返回值这里创建了,返回值类型是Date类型,相当于通过引用 Date = r ;创建了一个匿名的临时对象,所以这里会调用拷贝构造函数。


    如果main主函数中修改如下,结果会如何呢?

        cout << "===== 4 =====" << endl;
        Date d2 = fd(d);                      
        cout << "===== 5 =====" << endl;
        

    理论上fd(d); 这里有一个拷贝构造函数和一个析构函数  还有这一步Date d2 = fd(d);  这里应该还有一个拷贝构造函数


    运行结果如下:


    日期类的构造函数
    ===== 1 =====
    拷贝构造函数Date (Date& d3)
    日期的析构函数
    ===== 2 =====
    ===== 3 =====
    ===== 4 =====
    拷贝构造函数Date (Date& d3)
    ===== 5 =====
    日期的析构函数
    日期的析构函数
    Program ended with exit code: 0

    为什么运行结果和我们分析的理论不一样呢??45之间为什么不是2个拷贝构造函数和1个析构函数呢?(其中一个拷贝构造函数和析构函数是fd的返回值临时变量的)

    那是因为编译器做的优化,它把那个临时变量的拷贝构造函数和析构函数相抵消了,所以这里也就只显示了 Date d2 = ...这个的拷贝构造函数。



    再写一个返回值是引用类型  参数是引用类型的函数 fe:

    Date& fe(Date &r){
        return r;
    }

    主函数中添加如下代码:
        <p class="p1"><span class="s1">    cout</span><span class="s2"> << </span><span class="s3">"===== 5 ====="</span><span class="s2"> << </span><span class="s4">endl</span><span class="s2">;</span></p><p class="p2"><span class="s5">    </span><span class="s6">fe</span><span class="s5">(d);</span><span class="s7">//</span><span class="s3">参数</span><span class="s7"> </span><span class="s3">返回值都是引用类型的函数</span></p><p class="p1"><span class="s2">    </span><span class="s1">cout</span><span class="s2"> << </span><span class="s3">"===== 6 ====="</span><span class="s2"> << </span><span class="s4">endl</span><span class="s2">;</span></p><p class="p3"><span class="s3">    </span><span class="s8">Date</span><span class="s3"> d3 = </span><span class="s9">fe</span><span class="s3">(d);</span></p><p class="p1"><span class="s2">    </span><span class="s1">cout</span><span class="s2"> << </span><span class="s3">"===== 7 ====="</span><span class="s2"> << </span><span class="s4">endl</span><span class="s2">;</span></p><p class="p1"><span class="s1">    Date</span><span class="s2"> &d4 = </span><span class="s3">fe</span><span class="s2">(d);  //创建一个引用变量接收函数返回的</span></p><p class="p2"><span class="s4">    </span><span class="s5">cout</span><span class="s4"> << </span><span class="s2">"===== 8 ====="</span><span class="s4"> << </span><span class="s6">endl</span><span class="s4">;</span></p>
        

    运行结果如下:
    日期类的构造函数
    ===== 1 =====
    拷贝构造函数Date (Date& d3)
    日期的析构函数
    ===== 2 =====
    ===== 3 =====
    ===== 4 =====
    拷贝构造函数Date (Date& d3)
    ===== 5 =====
    ===== 6 =====
    拷贝构造函数Date (Date& d3)
    <p class="p1"><span class="s1">===== 7 =====</span></p><p class="p1"><span class="s1">===== 8 =====</span></p><p class="p2"><span class="s1">日期的析构函数</span></p><p class="p2"><span class="s1">日期的析构函数</span></p><p class="p2"><span class="s1">日期的析构函数</span></p><p class="p3"><span class="s1">Program ended with exit code: 0</span></p>

    从运行结果可知56之间的只是调用函数fe  ,该函数的返回值和参数都是引用类型,并没有创建新的对象。
       而在67之间因为通过返回值创建了对象 Date d3 =......这里调用了拷贝构造函数。
       而78之间因为创建一个引用类型的变量来接收函数返回的,也就是相当于起别名,而并没有创建新对象,所以并没有调用拷贝构造函数




    再来,在main函数当中添加如下代码:
        cout << "===== 8 =====" << endl;
        Date d5[2] = {d,d2};              //数组里面有2个元素,分别用d和d2来赋值
        <span class="s1" style="font-family: Arial, Helvetica, sans-serif;">cout</span><span class="s2" style="font-family: Arial, Helvetica, sans-serif;"> << </span><span class="s3" style="font-family: Arial, Helvetica, sans-serif;">"===== 9 ====="</span><span class="s2" style="font-family: Arial, Helvetica, sans-serif;"> << </span><span class="s4" style="font-family: Arial, Helvetica, sans-serif;">endl</span><span class="s2" style="font-family: Arial, Helvetica, sans-serif;">;</span><p class="p2"><span class="s3">    </span><span class="s5">Date</span><span class="s3"> d6[</span><span class="s6">5</span><span class="s3">];</span></p><p class="p1"><span class="s2">    </span><span class="s1">cout</span><span class="s2"> << </span><span class="s3">"===== 10 ====="</span><span class="s2"> << </span><span class="s4">endl</span><span class="s2">;</span></p>
    运行结果如下:

    <p class="p1"><span class="s1"><span style="font-size:18px;">日期类的构造函数</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 1 =====</span></span></p><p class="p2"><span style="font-size:18px;"><span class="s2">拷贝构造函数</span><span class="s1">Date (Date& d3)</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 2 =====</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 3 =====</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 4 =====</span></span></p><p class="p2"><span style="font-size:18px;"><span class="s2">拷贝构造函数</span><span class="s1">Date (Date& d3)</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 5 =====</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 6 =====</span></span></p><p class="p2"><span style="font-size:18px;"><span class="s2">拷贝构造函数</span><span class="s1">Date (Date& d3)</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 7 =====</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 8 =====</span></span></p><p class="p2"><span style="font-size:18px;"><span class="s2">拷贝构造函数</span><span class="s1">Date (Date& d3)</span></span></p><p class="p2"><span style="font-size:18px;"><span class="s2">拷贝构造函数</span><span class="s1">Date (Date& d3)</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 9 =====</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期类的构造函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期类的构造函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期类的构造函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期类的构造函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期类的构造函数</span></span></p><p class="p2"><span class="s1"><span style="font-size:18px;">===== 10 =====</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p1"><span class="s1"><span style="font-size:18px;">日期的析构函数</span></span></p><p class="p3"><span class="s1"><span style="font-size:18px;">Program ended with exit code: 0</span></span></p>

    在结果89之间看到有2个拷贝构造函数。因为是通过同类型对象来创建对象的,所以会调用2个拷贝构造函数。
    在9和10 之间会看到5个构造函数,这里不是拷贝构造而是直接创建对象,所以是构造函数。



    如果再在main函数里面添加如下的代码:
    <span style="font-size:24px;">    </span><span style="font-size:18px;"><span class="s1" style="font-family: Arial, Helvetica, sans-serif;">cout</span><span class="s2" style="font-family: Arial, Helvetica, sans-serif;"> << </span><span class="s3" style="font-family: Arial, Helvetica, sans-serif;">"===== 10 ====="</span><span class="s2" style="font-family: Arial, Helvetica, sans-serif;"> << </span><span class="s4" style="font-family: Arial, Helvetica, sans-serif;">endl</span><span class="s2" style="font-family: Arial, Helvetica, sans-serif;">;</span></span><p class="p2"><span style="font-size:18px;"><span class="s3">    d = d2;  </span><span class="s5">//</span><span class="s6">赋值操作</span></span></p><p class="p1"><span style="font-size:18px;"><span class="s2">    </span><span class="s1">cout</span><span class="s2"> << </span><span class="s3">"===== 11 ====="</span><span class="s2"> << </span><span class="s4">endl</span><span class="s2">;</span></span></p><p class="p2"><span style="font-size:18px;"><span class="s3">    </span><span class="s7">Date</span><span class="s3"> *d7 = </span><span class="s8">new</span><span class="s3"> </span><span class="s7">Date</span><span class="s3">;</span></span></p><p class="p2"><span style="font-size:18px;"><span class="s3"></span></span></p><p class="p1"><span style="font-size:18px;"><span class="s1">    delete</span><span class="s2"> d7;</span></span></p><p></p><p class="p1"><span style="font-size:18px;"><span class="s2">    </span><span class="s1">cout</span><span class="s2"> << </span><span class="s3">"===== 12 ====="</span><span class="s2"> << </span><span class="s4">endl</span><span class="s2">;</span></span></p>
    运行结果:
    <p class="p1"><span class="s1">===== 10 =====</span></p><p class="p1"><span class="s1">===== 11 =====</span></p><p class="p2"><span class="s1">日期类的构造函数</span></p><p class="p2"><span class="s1"></span></p><p class="p1"><span class="s1">日期的析构函数</span></p><p></p><p class="p1"><span class="s1">===== 12 =====</span></p>

    运行结果10 和 11之间什么都没有打印,也就是并没有创建新的对象。
    而11和12之间只有一个构造函数,Date *d7是指针,指向一个对象,在创建这个对象的时候就调用了构造函数。

    如果把11和12当中 delete d7注释掉  然后通过d7来创建对象,main函数中增加如下代码呢:
       cout << "===== 12 =====" << endl;
        Date *d8 = new Date(*d7);
        delete d8;
        cout << "===== 13 =====" << endl;
    运行结果如下:
    <p class="p1"><span class="s1">===== 12 =====</span></p><p class="p1"><span class="s2">拷贝构造函数</span><span class="s1">Date (Date& d3)</span></p><p class="p2"><span class="s1">日期的析构函数</span></p><p class="p1"><span class="s1">===== 13 =====</span></p>

    在12和13之间是通过*d7得到同类型的对象然后创建对象,所以这里会调用拷贝构造函数。



    要搞明白:
    什么时候调用构造函数 ?   什么时候调用拷贝构造函数 ?  什么时候什么都不调用??





















    展开全文
  • 1、拷贝构造函数是用一个已存在的对象去初始化另一个需要新建的对象,即用已经存在的对象初始化新建对象。 2、拷贝构造函数的写法类名 (const 类名 &amp;amp;要拷贝的类名) 例如: X是类的名称; X...

    1、拷贝构造函数是用一个已存在的对象去初始化另一个需要新建的对象,即用已经存在的对象初始化新建对象。
    2、拷贝构造函数的写法类名 (const 类名 &要拷贝的类名)
    例如:
    X是类的名称;

    X(const X &p)
    {
    a_=p.a_,b_=p.b_;//这里必须要有
    }
    

    2、使用拷贝构造函数的情形
    (1)

    X (const X &p){}
    int main()
    {X p2=p1;}
    

    (2)

    X (const X &p){}
    Line(Point &p):p1(p){}//这里调用了拷贝构造函数,前面没有调用拷贝构造函数,但是后面p1(p)这里调用了拷贝构造函数;
    int main()
    {
    Point p2(p1);//这里会调用拷贝构造函数;
    }
    

    (3)以对象来作为参数并且必须是传值参数

    X (const X &p){}
    void f(X p){}//这里是传值参数,所以调用拷贝构造函数;
    void f(X &p){}//不调用拷贝构造函数,因为都不生成新对象;
    void f(X *p){}//不调用拷贝构造函数,因为都不生成新对象;
    int main()
    {
    
    }
    

    (4)返回类对象时会调用拷贝构造函数

    X f1()
    {
        X p1(2,2);//对类的局部变量,p1在函数结束时释放,
        return p1;//不会调用拷贝构造函数
    }
    改法一:
    X f1()
    {
        statio X p1(1,2);
        return p1;
    }
    改法二:
    X p1(1,2);//全局变量;
    {
        return p1;
    }
    改法三:
    {
        X *p=new X(1,2);
        return *p;
    }
    综上以上三总方法是只要别释放掉即可;
    

    3、拷贝构造函数说明
    (1)拷贝构造函数与一般构造函数相同,与类同名,没有返回类型,可以重载。
    (2)把一个对象复制给一个已经定义好的对象时,调用的是赋值运算符函数。

    X p;
    X p1;
    p1=p;//这个时候因为P已经定义好了,所以调用的是赋值运算符函数;
    

    (3)第一个参数必须是自身类型的引用,其余参数必须有默认值。

    展开全文
  • class Person { public: Person(const Person& p); //拷贝构造函数 Person& operator=(const Person&... //赋值运算符重载 ...何时调用 ...拷贝构造函数使用传入对象的值生成一个新的对象的实.
    class Person
    {
    public:
    
        Person(const Person& p);    //拷贝构造函数
    
        Person& operator=(const Person& p);   //赋值运算符重载
    
    private:
        int age;
        string name;
    };

    何时调用

    拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象;但是其结果却有些不同,

    拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例

    这种区别从两者的名字也可以很轻易的分辨出来,拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给一个已经存在的实例

    调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。

     

    拷贝构造函数调用时机主要有以下场景:

    • 对象作为函数的参数,以值传递的方式传给函数。 
    • 对象作为函数的返回值,以值的方式从函数返回
    • 使用一个对象给另一个对象初始化

     

    范例

    #include <iostream>
    using namespace std;
     
    //定义一个Point类
    class Point{
    public:
    	Point(int xx=0,int yy=0):x(xx),y(yy){}     //构造函数
    	~Point(){ };                              //析构函数
        Point(const Point &p);                //拷贝构造函数 的声明
    	int getX()const{return x;}
    	int getY()const {return y;}
    private:
    	int x,y;//私有成员
     
    };
    
    //拷贝构造函数 的定义
    Point::Point(const Point &p)   
    {
    	x = p.x;
    	y = p.y;
    	cout << "Calling the copy constructor" <<endl;
     
    }
    
    //形参作为Point类对象的函数
    void fun1(Point p)
    {
    	cout<< p.getX()<<endl;
     
    }
     
    //返回类的对象
    Point fun2()
    {
    	Point a(1,2);
    	return a;
    }
     
    int main()
    {
    	Point a(4);    //第一个对象A,该过程利用了重载,后面的y默认为0
    	
    	Point b(a);      //此时调用拷贝构造函数;情况1,用a初始化b,第一次调用拷贝构造函数
    	cout << b.getX()<<endl; 
     
    	fun1(b);  //此时调用拷贝构造函数;类的对象在函数中为实参,第二次调用拷贝构造函数
     
    	b = fun2();//此时调用拷贝构造函数;函数返回值为类的对象,第三次调用拷贝构造函数
    	cout << b.getX()<<endl; 
    	return 0;
    }

     

    为了更好地理解拷贝构造函数   
     1、为什么要有拷贝构造函数,它跟构造函数有什么区别?
           答:拷贝构造函数其实也是构造函数,只不过它的参数是const 的类自身的对象的引用。如果类里面没有指针成员(该指针成员指向动态申请的空间),是没有必要编写拷贝构造函数的 。     我们知道,如果有一个类CObj,它已经产生了一个对象ObjA,现在又用CObj去创建ObjB,如果程序中使用语句ObjB = ObjA; 也就是说直接使用ObjA的数据给ObjB赋值。这对于一般的类,没有任何问题,但是如果CObj里面有个char * pStr的成员,用来存放动态申请的字符串的地址,在ObjA中使用new 方法动态申请了内存并让ObjA.pStr指向该申请的空间,在OjbB = OjbA之后,ObjA.pStr和ObjB.pStr将同时指向那片空间,这样到导致了谁也不知道到底该由谁来负责释放那块空间,很有可能导致同一块内存被释放两次。     使用拷贝构造函数,先申请ObjA.pStr所指向的空间大小的空间,然后将空间内容拷贝过来,这样就不会同时指向同一块内存,各自有各自申请的内存,各自负责释放各自申请的内存,从而解决了刚才的问题。所以这里的“拷贝”拷贝的是动态申请的空间的内容,而不是类本身的数据。另外注意到,拷贝构造函数的参数是对象的引用,而不是对象的指针。至于为什么要用引用,不能够用指针暂时还没有搞明白,等搞明白了再说。    
    2、为什么要对=赋值操作符进行重载?
        答:接上面的例子,用户在使用语句ObjB = ObjA的时候,或许ObjB的pStr已经指向了动态申请的空间,如果直接简单将其指向的地址覆盖,就会导致内存泄露,所以需要对=赋值操作符进行重载,在重载函数中判断pStr如果已经指向了动态申请的空间,就先将其释放。    
    3、拷贝构造函数和=赋值操作符重载的关系。
        答:从原文的例子中可以看出,=赋值操作符重载比拷贝构造函数做得要多,它除了完成拷贝构造函数所完成的拷贝动态申请的内存的数据之外,还释放了原本自己申请的内存空间。所以原文最后给出的拷贝构造函数的实现可以使用=赋值操作符的重载来完成。    
    4、拷贝构造函数何时被调用?
        a.对象的直接赋值也会调用拷贝构造函数  ;
        b.函数参数传递只要是按值传递也调用拷贝构造函数;
        c.函数返回只要是按值返回也调用拷贝构造函数。

     

    展开全文
  • 当以拷贝的方式初始化对象时会调用拷贝构造函数。这里有两个关键点,分别是「以拷贝的方式」和「初始化对象」。 初始化对象 初始化对象是指,为对象分配内存后第一次向内存中填充数据,这个过程会调用构造函数。对象...

    当以拷贝的方式初始化对象时会调用拷贝构造函数。这里有两个关键点,分别是「以拷贝的方式」和「初始化对象」。
    初始化对象
    初始化对象是指,为对象分配内存后第一次向内存中填充数据,这个过程会调用构造函数。对象被创建后必须立即被初始化,换句话说,只要创建对象,就会调用构造函数。
    初始化和赋值的区别
    初始化和赋值都是将数据写入内存中,并且从表面上看起来,初始化在很多时候都是以赋值的方式来实现的,所以很容易混淆。请看下面的例子:

    int a = 100;  //以赋值的方式初始化
    a = 200;  //赋值
    a = 300;  //赋值
    int b;  //默认初始化
    b = 29;  //赋值
    b = 39;  //赋值
    

    在定义的同时进行赋值叫做初始化(Initialization),定义完成以后再赋值(不管在定义的时候有没有赋值)就叫做赋值(Assignment)。初始化只能有一次,赋值可以有多次。

    对于基本类型的数据,我们很少会区分「初始化」和「赋值」这两个概念,即使将它们混淆,也不会出现什么错误。但是对于类,它们的区别就非常重要了,因为初始化时会调用构造函数(以拷贝的方式初始化时会调用拷贝构造函数),而赋值时会调用重载过的赋值运算符。请看下面的例子:

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Student{
    public:
        Student(string name = "", int age = 0, float score = 0.0f);  //普通构造函数
        Student(const Student &stu);  //拷贝构造函数
    public:
        Student & operator=(const Student &stu);  //重载=运算符
        void display();
    private:
        string m_name;
        int m_age;
        float m_score;
    };
    
    Student::Student(string name, int age, float score): m_name(name), m_age(age), m_score(score){ }
    
    //拷贝构造函数
    Student::Student(const Student &stu){
        this->m_name = stu.m_name;
        this->m_age = stu.m_age;
        this->m_score = stu.m_score;
        cout<<"Copy constructor was called."<<endl;
    }
    
    //重载=运算符
    Student & Student::operator=(const Student &stu){
        this->m_name = stu.m_name;
        this->m_age = stu.m_age;
        this->m_score = stu.m_score;
        cout<<"operator=() was called."<<endl;
       
        return *this;
    }
    
    void Student::display(){
        cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
    }
    
    int main(){
        //stu1、stu2、stu3都会调用普通构造函数Student(string name, int age, float score)
        Student stu1("小明", 16, 90.5);
        Student stu2("王城", 17, 89.0);
        Student stu3("陈晗", 18, 98.0);
       
        Student stu4 = stu1;  //调用拷贝构造函数Student(const Student &stu)
        stu4 = stu2;  //调用operator=()
        stu4 = stu3;  //调用operator=()
       
        Student stu5;  //调用普通构造函数Student()
        stu5 = stu1;  //调用operator=()
        stu5 = stu2;  //调用operator=()
       
        return 0;
    }
    

    运行结果:

    Copy constructor was called.
    operator=() was called.
    operator=() was called.
    operator=() was called.
    operator=() was called.
    

    以拷贝的方式初始化对象
    初始化对象时会调用构造函数,不同的初始化方式会调用不同的构造函数:
    如果用传递进来的实参初始化对象,那么会调用普通的构造函数,我们不妨将此称为普通初始化;
    如果用其它对象(现有对象)的数据来初始化对象,那么会调用拷贝构造函数,这就是以拷贝的方式初始化。

    在实际编程中,具体有哪些情况是以拷贝的方式来初始化对象呢?
    1) 将其它对象作为实参
    以上面的 Student 类为例,我们可以这样来创建一个新的对象:

    Student stu1("小明", 16, 90.5);  //普通初始化
    Student stu2(stu1);  //以拷贝的方式初始化
    

    即使我们不在类中显式地定义拷贝构造函数,这种初始化方式也是有效的,因为编译器会生成默认的拷贝构造函数。
    2) 在创建对象的同时赋值
    接着使用 Student 类,请看下面的例子:

    Student stu1("小明", 16, 90.5);  //普通初始化
    Student stu2 = stu1;  //以拷贝的方式初始化
    

    这是最常见的一种以拷贝的方式初始化对象的情况,非常容易理解,我们也已经多次使用。
    3) 函数的形参为类类型
    如果函数的形参为类类型(也就是一个对象),那么调用函数时要将另外一个对象作为实参传递进来赋值给形参,这也是以拷贝的方式初始化形参对象。请看下面的代码:

    void func(Student s){
        //TODO:
    }
    Student stu("小明", 16, 90.5);  //普通初始化
    func(stu);  //以拷贝的方式初始化
    

    func() 函数有一个 Student 类型的形参 s,将实参 stu 传递给形参 s 就是以拷贝的方式初始化的过程。

    函数是一段可以重复使用的代码,只有等到真正调用函数时才会为局部数据(形参和局部变量)在栈上分配内存。对于上面的 func(),虽然它的形参 s 是一个对象,但在定义函数时 s 对象并没有被创建,只有等到调用函数时才会真正地创建 s 对象,并在栈上为它分配内存。而创建 s 对象,就是以拷贝的方式进行的,它等价于下面的代码:
    Student s = stu;

    4) 函数返回值为类类型
    当函数的返回值为类类型时,return 语句会返回一个对象,不过为了防止局部对象被销毁,也为了防止通过返回值修改原来的局部对象,编译器并不会直接返回这个对象,而是根据这个对象先创建出一个临时对象(匿名对象),再将这个临时对象返回。而创建临时对象的过程,就是以拷贝的方式进行的,会调用拷贝构造函数。

    下面的代码演示了返回一个对象的情形:

    Student func(){
        Student s("小明", 16, 90.5);
        return s;
    }
    Student stu = func();
    

    理论上讲,运行代码后会调用两次拷贝构造函数,一次是返回 s 对象时,另外一次是创建 stu 对象时。

    展开全文
  • 假设Person是一个类,复制构造函数调用会在以下几种情况下发生: 1、对象在创建时使用其他的对象初始化 Person p(q); //此时复制构造函数被用来创建实例p Person p = q; //此时复制构造函数被用来在定义...
  • "调用拷贝构造函数" << endl; } void operator=(CopyTest& cp) { cout << "调用了拷贝幅值函数" << endl; } }; CopyTest show(CopyTest mmm) { return mmm; } 我们写下以下的测试程序:...
  • 何时会调用拷贝构造函数

    千次阅读 2020-08-25 22:23:38
    以下情况都会调用拷贝构造函数:①程序中需要新建立一个对象,并用另一个同类的对象对它初始化,如前面介绍的那样。② 当函数的参数为类的对象时。在调用函数时需要将实参对象完整地传递给形参,也就是需要建立一个...
  • 什么情况下会调用拷贝构造函数?

    千次阅读 2019-01-03 11:38:59
    调用拷贝构造函数的情形: 1)用类的一个对象去初始化另一个对象的时候   2)当函数的参数是类的对象时,就是值传递的时候,如果是引用传递则不会调用   3)当函数的返回值是类的对象或者引用的时候  ...
  • 什么时候用拷贝构造函数,和赋值构造函数:...以下情况都会调用拷贝构造函数:  一个对象以值传递的方式传入函数体  一个对象以值传递的方式从函数返回  一个对象需要通过另外一个对象进行初始化。   如果在类中没
  • 1.类对象赋值 void test1() { Test t1(1,2); Test t2(t1); }2.void test2() ...//调用的不是拷贝构造函数,调用的是=号操作符,也能够完成将t1的值给t2,但是不是调用t2的拷贝构造函数 }3.void func(Test t)
  • 拷贝构造函数什么时候调用? 1当用类的一个对象初始化该类的另一个对象时.例如: C/C++ code int main() { point A(1,2); point B(A);//用对象A初始化对象B,拷贝构造函数被调用. } 2 如果...
  • 这是因为,在值传递的方式传递给一个函数的时候,会调用拷贝构造函数生成函数的实参。 如果拷贝构造函数的参数仍然是以值的方式,就会无限循环的调用下去,直到函数的栈溢出。 (不用引用,编译不能通过)   ...
  • 时候需要自己定义拷贝构造函数,以避免浅拷贝问题。 在什么情况下需要用户自己定义拷贝构造函数: 一般情况下,当类中成员有指针变量、类中有动态内存分配时常常需要用户自己定义拷贝构造函数。 &nbsp; 在...

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 325
精华内容 130
关键字:

什么时候调用拷贝构造函数