精华内容
下载资源
问答
  • 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 1.复制构造函数和重载赋值操作符的定义; 2.复制构造函数和重载赋值操作符的调用时机; 3.复制构造函数和重载赋值操作符的实现要点; 4....
  • 在C++中复制控制是一个比较重要的话题,主要包括复制构造函数、重载赋值操作符、析构函数这三部分,这三个函数是一致的,如果需要手动定义了其中了一个,那么另外的两个也需要定义,通常在存在指针或者前期相关操作...
  • C++复制构造函数与重载赋值操作符

    千次阅读 2015-07-30 16:21:48
    因此如果对象中含有动态分配的内存,就需要我们自己重写复制构造函数和重载赋值操作符来实现“deep copy”,确保数据的完整性和安全性。 例如:类内成员变量需要动态开辟堆内存,如果实行浅拷贝,也就是把对象里...

    内容整理自:

    C++拷贝构造函数(深拷贝,浅拷贝)

    C++中复制构造函数与重载赋值操作符总结

    深拷贝和浅拷贝的区别 

    对深拷贝与浅拷贝的再次理解

    禁止使用类的copy构造函数和赋值操作符

    拷贝构造函数中的陷阱

    函数原型

    在C++中建立一个类,这个类中肯定会包括构造函数、析构函数、复制构造函数和重载赋值操作。

    复制构造函数是一种特殊的构造函数,其作用也是为类的成员初始化以及为对象的构造分配存储空间。函数的名称必须和类名称一致,无返回类型,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变。

    复制构造函数原型如下:

    class_name(const class_name &src);

    对于一个类X, 如果一个构造函数的第一个参数是下列之一:a) & X; b) const & X; c) volatile & X; d) const volatile & X;
    且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数,如下:
    X::X(const & X);   
    X::X(& X, int=1); 
    X::X(& X, int a=1, int b=2); 

    重载赋值操作符是一个特别的赋值运算符,通常是用来把已存在的对象赋值给其它相同类型的对象。

    重载赋值操作符的原型如下:

    class_name& operator=(const class_name &src);


    复制构造函数与重载赋值操作符的调用

    当类的对象需要拷贝时,复制构造函数将会被调用。以下情况都会调用复制构造函数:
    一个对象以值传递的方式传入函数体;
    一个对象以值传递的方式从函数返回;
    一个对象需要通过另外一个对象进行初始化。

    如果对象在声明的同时将另一个已存在的对象赋给它,就会调用复制构造函数;如果对象已经存在了,然后再将另一个已存在的对象赋给它,调用的就是重载赋值运算符。

    #include <iostream>
    using namespace std;
    
    class CTest
    {
    public:
         CTest(){}
         ~CTest(){}
    
         CTest(const CTest &test)
         {
              cout<<"copy constructor."<<endl;
         }
    
         void operator=(const CTest &test)
         {
              cout<<"operator="<<endl;
         }
    
         void Test(CTest test)
         {}
    
         CTest Test2()
         {
              CTest a;
              return a;
         }
    
         void Test3(CTest &test)
         {}
    
         CTest &Test4()
         {
              CTest *pA = new CTest;
              return *pA;
         }
    };
    
    int main()
    {
         CTest obj;
    
         CTest obj1(obj); // 调用复制构造函数
    
         obj1 = obj; // 调用重载赋值操作符
    
         /* 传参的过程中,要调用一次复制构造函数
         * obj1入栈时会调用复制构造函数创建一个临时对象,与函数内的局部变量具有相同的作用域
         */
         obj.Test(obj1);
    
         /* 函数返回值时,调用复制构造函数;将返回值赋值给obj2时,调用重载赋值操作符
         * 函数返回值时,也会构造一个临时对象;调用复制构造函数将返回值复制到临时对象上
         */
         CTest obj2;
         obj2 = obj.Test2();
    
         obj2.Test3(obj); // 参数是引用,没有调用复制构造函数
    
         CTest obj3;
         obj2.Test4(); // 返回值是引用,没有调用复制构造函数
    
         return 0;
    }

    深拷贝(deep copy)与浅拷贝(shallow copy)

    如果在类中没有显式地声明,那么编译器会自动生成默认的复制构造函数和重载赋值操作符。默认的复制构造函数和赋值运算符进行的都是“shallow copy”,只是简单地复制字段,把值一一赋给要拷贝的值。因此如果对象中含有动态分配的内存,就需要我们自己重写复制构造函数和重载赋值操作符来实现“deep copy”,确保数据的完整性和安全性。

    例如:类内成员变量需要动态开辟堆内存,如果实行浅拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

    深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,资源重新分配,使对象拥有不同的资源,但资源的内容是一样的,这个过程就是深拷贝;反之,没有重新分配资源,两个对象就有用共同的资源,同时对资源可以访问,就是浅拷贝。浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

    #include <iostream>
    using namespace std;
    class CA
    {
     public:
      CA(int b,char* cstr)
      {
       a=b;
       str=new char[b];
       strcpy(str,cstr);
      }
      CA(const CA& C)
      {
       a=C.a;
       str=new char[a]; //深拷贝
       if(str!=0)
        strcpy(str,C.str);
      }
      void Show()
      {
       cout<<str<<endl;
      }
      ~CA()
      {
       delete str;
      }
     private:
      int a;
      char *str;
    };
    
    int main()
    {
     CA A(10,"Hello!");
     CA B=A;
     B.Show();
     return 0;
    } 


    三法则(英语:rule of three,the Law of The Big Three,The Big Three;三法则,三大定律)在 C++ 程序设计里,它是一个以设计的基本原则而制定的定律,三法则的要求在于,假如类型有明显地定义下列其中一个成员函数,那么程序员必须连其他二个成员函数也一同编写至类型内,亦即下列三个成员函数缺一不可:

    • 析构函数(Destructor)
    • 复制构造函数(copy constructor)
    • 复制赋值运算符(copy assignment operator)


    有时候为了防止默认拷贝发生,可以将复制构造函数和重载赋值运算符设为private来禁止拷贝,并且只是声明,不用实现。这样的话,如果试图调用 A  b(a); 就调用了私有的复制构造函数,编译器会报错。

    class Widget  
    {  
    public:  
        int* pi;  
    private:  
        Widget(const Widget&);  
        Widget& operator=(const Widget&);  
    }; 

    这种方法的一点小缺陷是,如果该类的成员函数或其友元函数调用复制构造函数或赋值操作符函数,会将错误推后到连接期。有一种方法可以将连接期错误移至编译期:先定义一个基类,然后让你的类继承该类。

    展开全文
  • CPP中重载赋值操作符

    2021-10-06 21:27:55
    在CPP中,析构函数,拷贝构造函数和赋值操作符重载总是绑定在一起的。 一、几个问题 1、赋值操作符重载函数 参数为:const Test& obj,加上const的原因是我们不希望此函数对用来进行赋值的obj进行任何...

    在CPP中,析构函数,拷贝构造函数和赋值操作符的重载总是绑定在一起的。

     

    一、几个问题

    1、赋值操作符重载函数

            参数为:const Test& obj,加上const的原因是我们不希望此函数对用来进行赋值的obj进行任何修改,其次加上const的形参,能接受const和非const的实参,反之只能接受非const的实参。

            返回值为:Test&,返回值是返回被赋值着的引用,即*this,这样可以实现连续复制,类似

    t2 = t1 = t3。如果返回不是引用类型而是Test,那么返回的是*this的副本,再用这个副本作为左值,那么就会出错。

            避免自赋值:c/c++的语法并不反对类似 t2 = t2 这样的自赋值语法,所以要在操作符重载加以判断避免自赋值操作,一来为了提高效率,二来避免出错。假设如上代码去掉 if 判断:

    那么*this与obj是同一个对象时,执行 delete m_pointer 也就意味着c.m_pointer也被delete,那么执行到 m_pointer = new int(*obj.m_pointer) 时 m_pointer就是一个野指针了,对一个野指针解引用就会出错。

            为什么先 delete m_pointer 再new:因为原先的m_pointer是通过类的构造函数new的,要再new一个空间并初始化为 *obj.m_pointer 就需要先将原来的 m_pointer 给delete,不然将造成内存泄漏,其实在这里还可以复用 m_pointer 原先的堆空间

            赋值运算符的重载函数只能是类的成员函数,不能是是类的静态函数(因为静态成员函数只能操作类的静态成员),也不能是(友元)全局函数,否则在编译阶段就出错了!假设可以为全局函数,c++类已经默认提供了赋值重载函数了,那么在赋值运算符重载函数(全局函数)和赋值运算符重载函数(类的成员函数)同时存在的情况下,当进行相同类型间的赋值时,编译器就不知道要调用哪一个函数了。

            调用时机:对比拷贝构造函数和赋值运算符重载函数的代码,可见除了避免自赋值判断之外,赋值运算符重载函数还比拷贝构造函数多了一句delete。其实初始化和赋值这两个东西不一样。初始化调用的是拷贝构造函数,m_pointer = new int(*obj.m_pointer) 语句是对象首次动态分配空间中边分配边为该空间初始化的,但是在赋值时调用的是赋值运算符重载函数,m_pointer = new int(*obj.m_pointer) 是在第二次分配空间的时候变分配边为该空间初始化的,所以需要把上次的new到的空间delete。

     

    展开全文
  • c++中赋值操作符重载

    千次阅读 2017-08-08 12:43:17
    直接抛问题,两个同类型的对象可以相互赋值?class cls { public: int a; char c; cls() : a(10), c('g') {} cls(int a, char c) : a(a), c(c) {}};int main(void) { cls c1(6, 's'); cls c2; c2 = c1;

      直接抛问题,两个同类型的对象可以相互赋值?

    class cls
    {
    public:
        int a;
        char c;
    
        cls() : a(10), c('g')
        {}
    
        cls(int a, char c) : a(a), c(c)
        {}
    
    };
    
    int main(void)
    {
        cls c1(6, 's');
    
        cls c2;
    
        c2 = c1;
    
        printf("c2.a = %d, c2.c = %c\n", c2.a, c2.c);   
    
        return 0;
    }

      编译运行:
    这里写图片描述

      显然c++支持两个同类型的对象可以相互赋值的。

      ”c2 = c1;”注意要跟”cls c2 = c1;”区分开,前者是赋值语句,后者是初始化。对于后者,对象c2会调用类的拷贝构造函数,实现将c1到c2的拷贝。关于拷贝构造函数的使用,在前面http://blog.csdn.net/qq_29344757/article/details/76037255一文中有详细介绍。
      两个同类型的对象可以直接使用”=”(赋值操作符)进行赋值,其实这是类已经实现对赋值操作符的重载。但是在上面的代码中,我们并没有定义赋值操作符重载函数,可见,c++类会默认为我们定义。

      综合前面所学习的可知,一个空的c++类:

    class cls
    {
    };

      编译器会为我们默认定义构造函数、拷贝构造函数、析构函数以及赋值操作符重载函数,也就变成:

    class cls
    {
    public:
        cls();
        ~cls();
        cls(const cls& c);
        cls& operator= ();
    };

      那么问题出现,默认的赋值操作符重载函数内部实现的深度拷贝还是浅拷贝?

    class cls
    {
    public:
        int a;
        int *p;
    
        cls()
        {
            p = new int(0);
            a = 0;
        }
    
        cls(int a, int b)
        {
            p = new int(b);
            this->a = a;
        }
    
        ~cls()
        {
            delete p;
        }
    };
    
    int main(void)
    {
        cls c1(6, 7);
        cls c2;
    
        c2 = c1;    //调用默认的赋值运算符重载函数
    
        printf("c2.a = %d, *c2.p = %d\n", c2.a, *c2.p);
    
        return 0;
    }

      编译运行:
    这里写图片描述
      堆栈出错,提示重复释放内存。
      显然,跟默认拷贝构造一个样,默认的赋值运算符重载函数也是浅拷贝,所以指针变量c2.p和c1.p是一样的。我们需要自定义赋值运算符重载函数(顺便把拷贝构造函数也自定义):

    class cls
    {
    public:
        int a;
        int *p;
    
        cls()
        {
            p = new int(0);
            a = 0;
        }
    
        cls(int a, int b)
        {
            p = new int(b);
            this->a = a;
        }
    
        //自定义拷贝构造函数
        cls(const cls& c)
        {
            p = new int(*c.p);
        }
    
        //自定义赋值运算符重载函数
        cls& operator= (const cls& c)
        {
            if (this == &c)
                return *this;
    
            delete p;
            p = NULL;
    
            p = new int(*c.p);
    
            return *this;
        }
    
        ~cls()
        {
            delete p;
        }
    };
    
    int main(void)
    {
        cls c1(6, 7);
        cls c2;
    
        c2 = c1;        //调用了cls& operator= (const cls& c)
    
        printf("c1.p = %p, c2.p = %p\n", c1.p, c2.p);
    
        printf("c2.a = %d, *c2.p = %d\n", c2.a, *c2.p);
    
        return 0;
    }

    编译运行正常:
    这里写图片描述

    下来仔细看看自定义的操作符重载函数:

    cls& operator= (const cls& c)
    {
        if (this == &c)
            return *this;
    
        delete p;
        p = NULL;
    
        p = new int(*c.p);
    
        return *this;
    }

    (1) 参数const cls& c:加上const原因在于我们不希望此函数对用来进行赋值的c做任何修改,其次有加上const的形参,能接受const和非const的实参,反之只能接收非const的实参
    (2) 返回值cls&:返回值是返回被赋值着的引用,即*this,这样可以实现连续赋值,即类似于:

    x = y = z;

    若不是返回引用”cls& “而是直接是”cls”,那返回的是(*this)的副本,再用这个副本做左值,那么就出错了。
    (3)避免自赋值:c/c++的语法并不反对类似”a = a”这样的自赋值语法,所以要在操作符重载加以判断避免自赋值操作,一来为了提高效率,二来避免出错。假设如上代码去掉if判断:

    cls& operator= (const cls& c)
    {
        delete p;
        p = NULL;
    
        p = new int(*c.p);
    
        return *this;
    }

    而*this跟参数c是同一个对象,那么在执行”delete p;”后也就意味着c.p也被delete了,那执行到”p = new int(*c.p);”就出错了,因为被delete后的p已经是一个野指针,对一个野指针解引用就会Segmentation fault。
    (4)为什么要先”delete p;”再执行new
    因为原先的p是通过类的构造函数new的,要再new一个空间并初始化为(*c.p)就需要先将原来p给delete,不然将造成内存泄漏。其实在这里可以复用p原型的堆空间,那么代码将改成:

    cls& operator= (const cls& c)
    {
        if (this == &c)
            return *this;
    
        *p = *c.p;      //这也是深度拷贝
    
        return *this;
    }

    这样改成反而看着简单。
    (6)赋值运算符的重载函数只能是类的成员函数,不能是是类的静态函数(因为静态成员函数只能操作类的静态成员),也不能是(友元)全局函数,否则在编译阶段就出错了!假设可以为全局函数,c++类已经默认提供了赋值重载函数了,那么在赋值运算符重载函数(全局函数)和赋值运算符重载函数(类的成员函数)同时存在的情况下,当进行相同类型间的赋值时,编译器就不知道要调用哪一个函数了。再者,假设可以用全局函数重载赋值操作符:

    int operator= (int a, cls& c)
    {
        //...
    }
    
    int main(void)
    {
        cls c(5);
    
        6 = c;      //哥们,这就有点过分了
    
        return 0;
    }

    (7) 调用的时机
    对比拷贝构造函数和赋值运算符重载函数的代码,可见除了避免自赋值判断之外,赋值运算符重载函数还比拷贝构造函数多了一句delete。一开始我很纳闷,想不出为何,其实那是我忽略了初始化和赋值这两个小玩意。初始化调用的是拷贝构造函数,”p = new int(*c.p);”语句是对象首次动态分配空间中边分配边为该空间初始化的,但是在赋值时调用的是赋值运算符重载函数,”p = newint(*c.p);”是在第二次分配空间的时候变分配边为该空间初始化的,所以需要把上次的new到的空间delete。

    展开全文
  • 一、操作符重载 1.什么是操作符重载 就是把一些操作符通过关键字operator,组装成一个函数,关键字operator后面接需要重载操作符符号 2.为什么需要重载操作符? (1)简单、方便,比起封装一个函数,使用...

    一、操作符重载

    1.什么是操作符重载

    就是把一些操作符通过关键字operator,组装成一个函数,关键字operator后面接需要重载的操作符符号

    2.为什么需要重载操作符?

    (1)简单、方便,比起封装一个函数,使用operator比较形象直观
    (2)可以提高代码的可读性
    (3)为了处理自定义类型和内置类型之间的运算

    3.操作符重载的格式

    函数的返回类型 operator 操作符(参数列表)

    例如:int operator=(int a,int b);
    

    ps:操作符重载也是一个函数,具有返回值和参数,此函数的形参个数与操作符的操作数目相关,例如 ‘+’ 是双目操作符,也就是说 ’+‘ 是有两个参数的,那重载’+‘时,就需要两个参数。

    4.不能被重载的操作符

    1).        //成员选择符2).*       //成员对象选择符3)::       //域解析操作符4)?:       //条件操作符5sizeof   

    ps:除了赋值号(=)外,基类中被重载的操作符都将被派生类继承
    ###5.重载操作符注意事项

    (1)不能通过连接其他符号来创建新的操作符,例如operator@
    (2)重载操作符时必须有一个类类型或者枚举类型的操作数
    (3)用于内置类型的操作符被重载时,其含义不能被改变,例如操作符加号(+)就是用来求和的,重载后,不能改变此操作符的意义
    (4)作为类成员的重载函数,其形参的数目看起来比操作数数目少一个,这是因为成员函数的操作符有一个默认的形参this,限定为第一个参数
    (5)一般将算术操作符定义为非成员函数,赋值运算符定义为成员函数
    (6)操作符定义为类的成员函数时,一般将其定义为类的友元函数
    (7)操作符:==和!=一定要成对重载
    (8)下标操作符[ ]:重载此操作符时,需要重载两个,一个是非const成员并返回引用,一个是const成员并返回引用
    (9)在重载操作符:解引用操作符*和->时,不显示任何参数
    (10)重载前置++(前置–)时,返回值是以引用的形式返回,而且没有参数
    (11)重载后置++(后置–)时,返回值是值的形式返回,但是需要一个参数,此参数没有任何作用,只是为了与前置++区分,也是为了和前置++构成重载,此参数编译器自动维护。
    (12)输入操作符>>和输出操作符<<必须定义为类的友元函数

    前置++(--)
    这里写图片描述
    ps:前置++和前置- -的原理相同
    后置++
    这里写图片描述
    ps:后置++和后置- -的原理相同

    6.重载操作符的优缺点

    (1)优点

    a.可以使程序更自然、更直观;
    b.可以提高代码的可读性;
    c.会使程序的效率增加

    (2)缺点

    a.会使类难以理解,也就是会降低代码的可读性;
    b.可能也会程序的效率

    二、赋值操作符重载

    赋值操作符重载就是把等号(=)重载,形成一个函数
    这里写图片描述
    重载操作符赋值号时,一定要注意这四个问题,也就是说呢,这四个方面就是考点啦

    展开全文
  • 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 复制构造函数和重载赋值操作符的定义; 复制构造函数和重载赋值操作符的调用时机; 复制构造函数和重载赋值操作符的实现要点; 复制...
  • C++重载赋值操作符

    2018-06-24 19:15:19
     类重载赋值操作符一般都是作为成员函数而存在的,那函数应该返回什么类型呢?参考内置类型的赋值操作,例如 int x,y,z; x=y=z=15; 赋值行为相当于x=(y=(z=15)),也就是赋值操作应该返回左操作数的引用,因此,...
  • QT重载赋值操作符=

    千次阅读 2017-08-06 12:21:00
    2019独角兽企业重金招聘Python工程师标准>>> ...
  • 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 复制构造函数和重载赋值操作符的定义;复制构造函数和重载赋值操作符的调用时机;复制构造函数和重载赋值操作符的实现要点;复制构造...
  • 1.赋值操作符重载的原因 赋值操作符是一个使用频率最高的操作之一,通常情况下它的意义十分明确,就是将两个同类型的变量的值从一端(右端)传到另一端(左端)。但在以下两种情况下,需要对赋值操作符进行重载。 一...
  • 默认重载赋值操作符

    2018-01-01 15:49:56
    编译器为每个类默认重载了赋值操作符 默认的赋值操作符仅完成浅拷贝 当需要进行深拷贝时必须重载赋值操作符 赋值操作符与拷贝构造函数有相同的存在意义 重载复制操作符,必然需要实现深拷贝
  • 书上都说C++在重载赋值操作符(=)时,应该返回一个引用,这是因为能够实现链式赋值:比如a=b=c=1 但是,返回一个临时变量,会出现什么问题呢? 比如 class A{...};中有一个赋值操作符重载,返回A,原型如下: A ...
  • C++ class perator= 重载赋值操作符。 关于重载赋值操作符的例子网上已经是一搜一大把了,在这里我就不做这些介绍了,只给大家总结一下对于这个操作符的基本注意事项。 1. 首先在函数中做 if(this == & rhs); ...
  • 拷贝构造函数和重载赋值操作符一般都是一起出现的。 拷贝构造函数: A(const A &rhs) { name=rhs.name; age=new int(); *age=*rhs.age; } 重载赋值操作符: A& operator = (const A &rhs) { delete...
  • 比如以下是CMyString的声明,请为该类添加赋值操作符Class CMyString{Public:CMyString(char *pDatra = nullptr);...为这个函数重载一个赋值操作符在为这个类重载赋值操作符的时候首先需要注意一下...
  • C++ 拷贝构造函数和重载赋值操作符不能相互调用转载 2014年02月14日 09:35:05795拷贝构造函数调用重载赋值操作符重载赋值操作符调用拷贝构造函数的写法都是没有意义的。首先:拷贝构造函数的存在意义--------是...
  • 编译器为每个类默认重载了(=)赋值操作符 默认的(=)赋值操作符仅完成浅拷贝 默认的赋值操作符和默认的拷贝构造函数有相同的存在意义 (=)赋值操作符注意事项 首先要判断两个操作数是否相等 返回值一定是 return *...
  • C++ 重载赋值操作符

    2015-02-13 17:12:25
    1、赋值操作符 =、+=、-=、*=、/=、%=、&=、|=、^=、>= 2、赋值操作必须返回对*this的引用,不能做成非成员函数 例:String& operator=(String const &);
  • 关于重载赋值操作符需要返回引用

    千次阅读 2016-09-21 21:04:06
    如果赋值操作符不返回引用代码也能编译通过 但会增加调用copy构造函数的开销(因为返回局部对象会调用拷贝构造函数)。 返回引用的话能减少调用copy构造函数 (effective Item10) */ class A { public: A &...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 101,416
精华内容 40,566
关键字:

重载赋值操作符