• C++:对象的赋值和复制


    3.6.1 对象赋值语句

    如同基本类型赋值语句一样,同类型的对象之间也可以进行赋值,即一个对象的值可以赋给
    另一个对象。这里所指的对象的赋值是指对其中的数据成员赋值,而不对成员函数赋值。
    例如:A和B是同一类的两个对象,那么下述对象赋值语句
    B=A;
    就能把对象A的数据成员的值逐位复制给对象B

    //例3.24 对象赋值语句示例
    #include<iostream>
    using namespace std;
    class Myclass{
     public:
           void set(int i,int j)
           {
            a = i;
            b = j;
           }
           void show()
           {
            cout<<a<<" "<<b<<endl;
           }
     private:
           int a,b;       
    }; 
    int main()
    {
     Myclass o1,o2;
     o1.set(20,5);
     o2 = o1;          //将对象o1的值赋给对象o2 
     o1.show();
     o2.show();
     return 0;
    }
    /*
     该程序中,语句:
       o2 = o1;
     等价于语句:
       o2.a = o1.a;
       o2.b = o1.b;   
     因此,运行此程序将显示:
       20 5
       20 5
      说明:
      1、在使用对象赋值语句进行对象赋值时,两个对象的类型必须相同,如对象的类型不同,
         编译时将出错。
      2、两个对象之间的赋值,仅仅使这些对象中数据成员相同,而两个对象仍是分离的。例如
         本例对象后,再调用o1.set()设置o1的值,不会影响o2的值。
      3、对象赋值是通过默认赋值运算符函数实现的
      4、将一个对象的值赋给另一个对象时,多数情况下都是成功的,但当类中存在指针时,可能
         会产生错误。              
    */

    3.6.2 拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。拷贝构造函数的作用
    是,在建立一个新对象时,使用一个已经存在的对象去初始化这个新对象。
    例如: Point p2(p1);
    其作用是:在建立新对象p2时,用已经存在的对象p1去初始化对象p2,在这个过程中就要
    调用拷贝构造函数。

    拷贝构造函数具有以下特点:
    (1)因为该函数也是一种构造函数,所以其函数名与类名相同,并且该函数也没有返回值
    类型。
    (2)该函数只有一个参数,并且是同类对象的引用
    (3)每一个类都必须有一个拷贝构造函数。程序员可以自定义拷贝构造函数,用于按照需要
    初始化新对象。如果程序员没有定义类的拷贝构造函数,系统就会自动生成产生一个默认的拷贝
    构造函数,用于复制出数据成员值完全相同的新对象。

    1. 自定义拷贝构造函数

    一般格式: 类名::类名(const 类名 &对象名)
    {
           //拷贝构造函数的函数体
    }

    下面是一个用户自定义的拷贝构造函数:
    class Point{
                   public:
                 Point(int a,int b) //构造函数
                        {
                             x = a;
                             y = b;
             }
                       Point(const Point &p) //拷贝构造函数
                       {
                            x = 2*p.x;
                            y = 2*p.y;
                        }
                   .....
                  private:
                           int x;
                           int y;
    };
    假如p1为类Point的一个对象,则下述语句可以在建立新对象p2时调用拷贝构造函数初始化p2;
    Point p2(p1);

    //例3.25 自定义拷贝构造函数的使用。

    #include<iostream>
    using namespace std;
    class Point{
           public:
                 Point(int a,int b)  //普通构造函数 
                 {
                   x = a;
                   y = b;
                 }
                 Point::Point(const Point &p) //自定义的拷贝构造函数 
                 {
                   x = 2*p.x;
                   y = 2*p.y;
                 }
                 void print()
                 {
                  cout<<x<<" "<<y<<endl; 
                 }
           private:
                   int x;
                   int y;         
    };
    int main()
    {
     Point p1(30,40);       //定义对象p1,调用了普通的构造函数
     Point p2(p1);          //调用拷贝构造函数,用对象p1初始化对象p2 
     p1.print();
     p2.print();
     return 0; 
    } 
    /*  本例中定义对象p2时,调用了自定义拷贝构造函数。程序运行结果如下:
            30 40 
            60 80
        
        调用拷贝构造函数的一般形式为:
        类名 对象2(对象1);
        
        上面的这种拷贝构造函数的方法称为“代入法”。除了用代入法调用拷贝构造函数外,
    还可以采用"赋值法"调用拷贝构造函数,与基本类型的变量初始化类似.这种调用方的一般格式为:
        类名 对象2=对象1;
        当然,这种方法可以在一个语句中进行多个对象的复制。如
        Point p2=p1,p3=p1; 
        
    如将例3.25主函数main改为如下形式:
    int main()
    {
     Point p1(10,20);
     Point p2=p1;  //以赋值法调用拷贝构造函数 
     p1.print();
     p2.print();
     return 0; 
    }     

    2. 默认的拷贝构造函数(程序员没有定义,系统会自动生成)

    // 例3.26 默认的拷贝构造函数

    #include<iostream>
    using namespace std;
    class Point{
     public:
            Point(int a,int b) //普通的构造函数 
            {
             x = a;
             y = b;
            }
            
    //        Point::Point(const Point &p)//不用写,系统会默认存在(需要用时直接调用) 
    //        {
    //         x = p.x;
    //         y = p.y;
    //        }
        
            void print()
            {
             cout<<x<<" "<<y<<endl;
            } 
     private:
            int x;
            int y;
    };
    int main()
    {
            Point p1(30,40);  //调用普通构造函数 
            Point p2(p1);     //用代入法调用默认的拷贝构造函数,用对象p1初始化对象p2 
            Point p3=p1;      //用赋值法调用默认的拷贝构造函数,用对象p1初始化对象p3
            p1.print();
            p2.print();
            p3.print();
            return 0;          
    } 
    
    /*
       调用拷贝构造函数的方法有两种:代入法、赋值法
       
       代入法:Point p2(p1)
    
       赋值法:Point p2=p1 
    
    */

    3.调用拷贝构造函数的三种情况

    (1)当用类的一个对象去初始化另一个对象时,拷贝构造函数将会被调用,
    如例3.26主函数中的下属语句
    Point p2(p1); //用代入法调用默认的拷贝构造函数,用对象p1初始化对象p2
    Point p3=p1; //用赋值法调用默认的拷贝构造函数,用对象p1初始化对象p3
    (2)当函数的形参是类的对象,在调用函数进行形参和实参结合时,拷贝构造函数将会被调用
    例如:

    void fun1(Point p) //形参是类Point的对象p
    {
       p.print();
    }
    int main()
    {
       point p1(10,20);
       fun1(p1); //调用函数fun1时,实参p1是类Point的对象
       //将调用拷贝构造函数,初始化形参对象p
       return 0;
    }

    理解:在main函数内,执行语句“fun1(p1)”,便是这种情况。在调用这个函数时,对象p1是实参
    用它来初始化被调用函数的形参p时,需要调用拷贝构造函数。这时,如果类Point中有自定义
    的拷贝构造函数时,就调用拷贝的构造函数,否则就调用系统自动生成的默认拷贝构造函数

    (3)当函数的 返回值是类的对象,在函数调用完毕将返回值(对象)带回调用处时。此时将会
    调用拷贝构造函数,将此对象赋值给一个临时对象并传到该函数的调用处。

    例如: Point fun2() //函数fun2()的返回值类型是Point类类型
    {
        Point p1(10,30); //定义类Point的对象p1
        return p1; // 函数的返回值是Point类的对象
    }
    int main()
    {
       Point p2; //定义类Point的对象p1
       p2=fun2(); //函数执行完成,返回调用者时,调用拷贝构造函数(赋值法)
       return 0;
    }

    理解:
    由于对象p1是函数fun2中定义的,在调用函数fun2结束时,p1的生命周期结束了,因此在
    函数fun2结束之前,执行语句"return p1"时,将会调用拷贝构造函数将p1的值复制到一个
    临时对象中,这个临时对象是系统在主程序中临时创建的。函数运行结束时,p1对象消失,
    但是临时对象将会通过语句"p2=fun2()"将它的值赋给对象p2,执行完这个语句后,临时对
    象变自动消失了。

    //例3.27  演示调用拷贝构造函数的3中情况
    
    #include<iostream>
    using namespace std;
    class Point{
      public:
              Point(int a=0,int b=0); //声明构造函数
              Point::Point(const Point &p); //声明拷贝构造函数
            void print()
            {
              cout<<x<<" "<<y<<endl; 
            } 
      private:
            int x,y;
    }; 
    Point::Point(int a,int b)  //定义构造函数 
    {
       x = a;
       y = b;
       cout<<"Using normal constructor
    ";
    }
    Point::Point(const Point &p)//定义拷贝构造函数 
    {
       x = 2 * p.x;
       y = 2 * p.y;
       cout<<"Using copy constructor
    "; 
    } 
    void fun1(Point p)    //形参是类Point的对象p
    {
       p.print(); 
    }
    Point fun2()  //函数fun2()的返回值类型是Point类类型 
    {
       Point p4(10,30);  //定义类Point的对象p4 
       return p4;        // 函数的返回值是Point类的对象 
    } 
    int main()
    {
       Point p1(30,40);//定义对象p1时,第1次调用普通的构造函数 
       p1.print();      
       
       Point p2(p1); //用带入法,用对象p1为对象p2进行初始化。此时会第1次调用拷贝构造函数 
       p2.print();
       
       Point p3=p1; //用赋值法,用对象p1为对象p3进行初始化。此时会第2次调用拷贝构造函数 
       p3.print();
       
       fun1(p1);  //在调用fun1函数,实参和形参结合时, 此时会第3次调用拷贝构造函数 
       
       p2=fun2(); //在调用fun2函数,在函数内部第2次调用普通构造函数。而且,当调用fun2函数结束时,
                  //还是用赋值法,用返回的对象p4为对象p2进行赋值,会第4次调用拷贝构造函数 
       p2.print();
       
       return 0;  
    } 
    
    运行结果:
              Using normal constructor 
              30    40 
              Using Cpy constructor 
              60    80 
              Using Cpy constructor 
              60    80 
              Using Cpy constructor 
              60    80 
              Using normal constructor
              Using Cpy constructor
              20    60
              
              
    当没有自定义的拷贝构造函数时的运行结果:
            Using normal constructor 
              30    40 
              30    40 
              30    40  
              30    40 
              Using normal constructor
              10    30         

    //再举一个实例为:

    #include<iostream>
    using namespace std;
    class Point{
      public:
        Point(int a,int b)
         {
           x = a;
           y = b;
         }
         /* Point::*/Point(const Point &p) //拷贝构造函数(用初始化过的对象为没有初始化过的对象进行初始化) 
         {
           x = 2 * p.x;
           y = 2 * p.y;
         }
         Point& operator = (const Point &p)//赋值运算符重载函数(用初始化过的对象为初始化过的对象进行赋值) 
         {
           x = p.x;
           y = p.y;
         }
         Point fun();
         void print()
         {
           cout<<x<<" "<<y<<endl;
         }
       private:
            int x,y;     
    };
    Point::Point fun(Point p4)
    {
        return p4; 
    }
    int main()
    {
      Point p1(10,20);
      Point p3(15,15);
      
      p3=p1;//调用赋值运算符重载函数 
      p3.print();
      
      Point p2 = fun(p1);//调用fun函数时,实参和形参结合,会第1次调用拷贝构造函数;调用fun函数结束时,
       p2.print() ;         // 返回的对象p4为未初始化的p2进行初始化,会第2次调用拷贝构造函数 
    }
  • 相关阅读:
    ASP.NET 服务器控件属性
    SQL Server CE和.NET Compact Framework概述
    用户控件在父页面上事件处理
    jquery怎么实现点击一个元素更换背景图片,连续点击永远在2张图片之间更换
    javascript函数大全
    在代码运行时或者在禁用“只要一个进程中断,就中断所有进程”选项时,不允许进行更改。
    jquery的html,text,val
    饥饿会让人更聪明
    用jquery解析JSON数据的方法
    AOP与OOP
  • 原文地址:https://www.cnblogs.com/XYQ-208910/p/4912150.html
Copyright © 2020-2023  润新知