精华内容
下载资源
问答
  • 但C#赋值操作符无法重载</strong>,无法将n已经注册的changed事件赋值给nx。 三、请问,如优雅的实现赋值操作符 = 的重载? 以实现可以将n 已经注册的changed事件赋值给nx,实现n &#...
  • 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 操作符重载概念引入1.1 复数解决方案1.2 操作符重载的概念2 C++中操作符重载的两种实现方式2.1 全局的操作符重载函数2.2 类内的操作符重载函数 1 操作符重载概念引入 1.1 复数解决方案 下面复数...

    1 C++中数学运算、比较、赋值操作符的重载

    1.1 完善的复数类

    复数类应该具有的操作:

    • 运算:+,-,*,/
    • 比较:==、!=
    • 赋值:=
    • 求模:modulus

    我们需要利用操作符重载:

    • 统一复数与实数的运算方式。
    • 统一复数与实数的比较方式。
      在这里插入图片描述
      复数类的实现:

    Complex.h:

    
    #ifndef _COMPLEX_H_
    #define _COMPLEX_H_
    
    class Complex
    {
        double a;
        double b;
    public:
        Complex(double a = 0, double b = 0);
        double getA();
        double getB();
        double getModulus();
        
        Complex operator + (const Complex& c);
        Complex operator - (const Complex& c);
        Complex operator * (const Complex& c);
        Complex operator / (const Complex& c);
        
        bool operator == (const Complex& c);
        bool operator != (const Complex& c);
        
        Complex& operator = (const Complex& c);
    };
    
    #endif
    

    Complex.cpp:

    #include "Complex.h"
    #include "math.h"
    
    Complex::Complex(double a, double b)
    {
        this->a = a;
        this->b = b;
    }
    
    double Complex::getA()
    {
        return a;
    }
    
    double Complex::getB()
    {
        return b;
    }
    
    double Complex::getModulus()
    {
        return sqrt(a * a + b * b);
    }
    
    Complex Complex::operator + (const Complex& c)
    {
        double na = a + c.a;
        double nb = b + c.b;
        Complex ret(na, nb);
        
        return ret;
    }
    
    Complex Complex::operator - (const Complex& c)
    {
        double na = a - c.a;
        double nb = b - c.b;
        Complex ret(na, nb);
        
        return ret;
    }
    
    Complex Complex::operator * (const Complex& c)
    {
        double na = a * c.a - b * c.b;
        double nb = a * c.b + b * c.a;
        Complex ret(na, nb);
        
        return ret;
    }
    
    Complex Complex::operator / (const Complex& c)
    {
        double cm = c.a * c.a + c.b * c.b;
        double na = (a * c.a + b * c.b) / cm;
        double nb = (b * c.a - a * c.b) / cm;
        Complex ret(na, nb);
        
        return ret;
    }
        
    bool Complex::operator == (const Complex& c)
    {
        return (a == c.a) && (b == c.b);
    }
    
    bool Complex::operator != (const Complex& c)
    {
        return !(*this == c);
    }
        
    Complex& Complex::operator = (const Complex& c)
    {
        if( this != &c )
        {
            a = c.a;
            b = c.b;
        }
        
        return *this;
    }
    

    main.cpp:

    #include <stdio.h>
    #include "Complex.h"
    
    int main()
    {
        Complex c1(1, 2);
        Complex c2(3, 6);
        Complex c3 = c2 - c1;
        Complex c4 = c1 * c3;
        Complex c5 = c2 / c1;
        
        printf("c3.a = %f, c3.b = %f\n", c3.getA(), c3.getB());
        printf("c4.a = %f, c4.b = %f\n", c4.getA(), c4.getB());
        printf("c5.a = %f, c5.b = %f\n", c5.getA(), c5.getB());
        
        Complex c6(2, 4);
        
        printf("c3 == c6 : %d\n", c3 == c6);
        printf("c3 != c4 : %d\n", c3 != c4);
        
        (c3 = c2) = c1;
        
        printf("c1.a = %f, c1.b = %f\n", c1.getA(), c1.getB());
        printf("c2.a = %f, c2.b = %f\n", c2.getA(), c2.getB());
        printf("c3.a = %f, c3.b = %f\n", c3.getA(), c3.getB());
        
        return 0;
    }
    
    

    参考资料:

    1. C++深度解析教程
    展开全文
  • 赋值操作符的重载  当成员变量有指针的时候,会用到它。上代码: #include using namespace std; class Person { char* pName; public: Person(char* pN="noName") { cout; pName = new ch

    赋值操作符的重载

           当成员变量有指针的时候,会用到它。上代码:

    #include <iostream>
    using namespace std;
    
    class Person
    {
    	char* pName;
    public:
    	Person(char* pN="noName")
    	{
    		cout<<"Constructing "<<pN<<"\n";
    		pName = new char[strlen(pN)+1];
    		if (pName)
    			strcpy(pName, pN);
    	}
    	Person(const Person& s)
    	{
    		cout<<"copy constructing "<<s.pName<<"\n";
    		pName = new char[strlen(s.pName)+1];
    		if (pName)
    			strcpy(pName, s.pName);
    	}
    	Person& operator=(Person& s)
    	{
    		cout<<"Assigning "<<s.pName<<"\n";
    		if (this==&s)    //重要的判定,是不是给自己赋值。
    			return s;
    		delete[] pName;
    		pName = new char[strlen(s.pName)+1];
    		if (pName)
    			strcpy(pName, s.pName);
    		return *this;
    	}
    	~Person()
    	{
    		cout<<"Destructing "<<pName<<"\n";
    		delete[] pName;
    	}
    };

     

          任何类,C++都有一个默认的赋值操作符,用来进行对象本体的复制。重载了赋值操作符,默认的赋值操作符就不复存在。一般来说,赋值操作符是与拷贝构造函数和析构函数结对而行的。

     

     

    展开全文
  • 复制构造函数与赋值操作符的重载

    千次阅读 2012-08-10 16:21:23
    1. 试验功能:带有指针成员且指针成员为类类型时,自定义复制构造函数与赋值操作符的行为是怎样的。 2. 试验结果: (1)当指针成员为一般普通成员时,我们可以自己在复制构造函数中为指针分配内存并拷贝内容,在...

    1. 试验功能:带有指针成员且指针成员为类类型时,自定义复制构造函数与赋值操作符的行为是怎样的。

    2. 试验结果:

    (1)当指针成员为一般普通成员时,我们可以自己在复制构造函数中为指针分配内存并拷贝内容,在赋值操作符函数中,检查或者删除左操作数的内存,并分配新内存然后拷贝内容到新分配内存中;

    (2)当指针成员为类类型(本例即是)时,称为第一级类类型的指针成员:

             在复制构造函数中:就要显式调用指针成员所属类型的复制构造函数生成一个临时对象,然后将该临时对象赋值给指针成员,可以通过两个方式进行赋值,一是:在复制构造函数的初始化式中以“指针变量(new 指针成员所属类类型复制构造函数)”的方式,二是:”指针变量=new 指针成员所属类类型复制构造函数“,本例方式为第一种方式,注意:这种方式显式调用了指针成员所属类的复制构造函数;

             在赋值操作符函数中:可以直接用”对象=对象“的形式实现赋值操作符,如本例中:*pcd = *obj.pcd; pcd就是一个类类型的指针成员,注意这种赋值就会调用指针成员所属类的赋值操作符函数;

    3. 当指针成员所属的类类型又包含指针成员时,仍然遵循本实验,即在一般指针成员时可以用(1)的方式实现拷贝,指针成员又为类类型时,就按照(2)的方式为该指针成员所属类实现复制构造函数和赋值操作符;这个指针成员所属类的复制构造函数和赋值操作符函数会在第一级指针成员所属类的复制构造函数和赋值操作符函数被调用的时候分别被调用,如果类类型指针成员所属类又包含了多级类类型的指针成员,就要为其做(2)的工作。

    代码:

    c.h 定义类 CC

    //c.h defined class CC
    //
    #include "d.h"
    
    class CC
    {
      public:
        CC();
        CC(int v1, int v2);
        virtual ~CC();
        CC(const CC &obj);
        CC& operator=(const CC &obj);
        int getVal() const;
        int getObjVal() const;
    
      private:
        int a;
        CD *pcd;
    };
    c.cpp

    #include "iostream"
    #include "c.h"
    
    CC::CC():a(0), pcd(new CD(0))
    {
      std::cout << "CC Default constructor CC();" << std::endl;
    }
    
    CC::CC(int v1, int v2):a(v1), pcd(new CD(v2))
    {
      std::cout << "CC constructor CC(int v1, intv2);" << std::endl;
    }
    
    CC::CC(const CC &obj):a(obj.a), pcd(new CD(*obj.pcd))
    {
      std::cout << "CC Copy constructor CC(const CC &obj);" << std::endl;
    }
    
    CC &CC::operator=(const CC &obj)
    {
      std::cout << "CC assign constructor operator=(const CC &obj);" << std::endl;
      a = obj.a;
      *pcd = *obj.pcd;
    }
    
    CC::~CC()
    {
      std::cout << "CC DeConstructor ~CC();" << std::endl;
      delete pcd;
    }
    
    int CC::getVal() const
    {
      return a;
    }
    
    int CC::getObjVal() const
    {
      return pcd->getVal();
    }
    d.h 定义类CD

    //d.h defined class CD
    //
    class CD
    {
      public:
        CD() {};
        CD(int v1):b(v1) {};
        virtual ~CD() {};
        CD(const CD &obj);
        CD &operator=(const CD& obj);
        int getVal() const;
      private:
        int b;
    };
    
    d.cpp

    #include <iostream>
    #include "d.h"
    CD::CD(const CD &obj)
    {
      std::cout << "CD Copy constructor CD(const CD &obj);" << std::endl;
      b = obj.b;
    }
    
    CD &CD::operator=(const CD& obj)
    {
      std::cout << "CD assign constructor operator=(const CD &obj);" << std::endl;
      b = obj.b;
    }
    
    int CD::getVal() const
    {
      return b;
    }
    main.cpp

    #include <iostream>
    #include "c.h"
    using namespace std;
    
    int test(CC o);
    int main()
    {
      CC c1(1, 10);
      CC c2(c1);
      cout << "c1.a:" << c1.getVal() << endl;
      cout << "c1.cd.b:" << c1.getObjVal() << endl;
    
      cout << "c2.a:" << c2.getVal() << endl;
      cout << "c2.cd.b:" << c2.getObjVal() << endl;
    
      cout << "test:" << test(c1) << endl;
    
      CC c3(3, 30);
      c3 = c2;
      cout << "c3.a:" << c3.getVal() << endl;
      cout << "c3.cd.b:" << c3.getObjVal() << endl;
    
    
    /*
      CD cd1;
      CD cd2;
      cd1 = cd2;
    */
    
      return 0;
    }
    
    int test(CC o)
    {
      return o.getObjVal();
    }
    

    ---------------------------------

    main 运行结果:

    [root@centos cc++]# ./main
    CC constructor CC(int v1, intv2);
    CD Copy constructor CD(const CD &obj);
    CC Copy constructor CC(const CC &obj);
    c1.a:1
    c1.cd.b:10
    c2.a:1
    c2.cd.b:10
    CD Copy constructor CD(const CD &obj);
    CC Copy constructor CC(const CC &obj);
    test:10
    CC DeConstructor ~CC();
    CC constructor CC(int v1, intv2);
    CC assign constructor operator=(const CC &obj); //CC包含类型为CD的指针成员pcd,当执行*pcd = *obj.pcd时,就会调用CC的赋值操作符,
    CD assign constructor operator=(const CD &obj);//这时CD的赋值操作符就会被自动调用,因为这是对象赋值而不是指针赋值,指针赋值时,复制构造函数和赋值操作符就不会被自动调用,依次类推,如果CD又包含类类型的指针成员,那么我们也要为其实现复制构造函数和赋值操作符,那么他们也会被自动调用
    c3.a:1
    c3.cd.b:10
    CC DeConstructor ~CC();
    CC DeConstructor ~CC();
    CC DeConstructor ~CC();


    展开全文
  • 指针悬挂: 问题:使用new申请内存内存空间无法访问,也无法释放。 原因:直接对指向new申请存储空间指针变量进行...容易引起指针悬挂条件:类中含有指针类型成员时,使用默认拷贝构造函数和赋值函数都会
  • 原因:直接对指向new申请存储空间指针变量进行赋值修改 后果:失去了原来地址,原来空间无法访问也无法释放,造成内存泄漏  还可能造成同一个内存释放两次 容易引起指针悬挂方式:对象初始化...
  • 《构造函数》,《析构函数》,《拷贝构造函数》,《赋值操作符的重载》, 《const成员函数》,《取地址及const取地址操作重载》,下来就一个一个看, ----》《构造函数》《----- **构造函数:**是一个特殊的成员...
  • 2.重载的算术操作符一般返回非引用对象,因为返回为局部变量。 3.String *favorite = new String(sayings[choice]); //将调用 calss_name(Type_name); //可能转化为: class_name *ptr = ne
  • 1、拷贝构造函数概述  在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”): 1) 一个对象作为函数参数,以值传递方式传入函数体; 2) 一个对象作为函数返回值,以值传递方式从函数...
  • // 一般认为gcc是学习C/C++最佳编译器。可是下面这个程序在g++3.4.4上竟然可以编译通过。#include #include using namespace std;class val_box{ private: int val;public: int get() { return val; } void ...
  • 1.赋值操作符重载的原因 赋值操作符是一个使用频率最高操作之一,通常情况下它意义十分明确,就是将两个同类型变量值从一端(右端)传到另一端(左端)。但在以下两种情况下,需要对赋值操作符进行重载。 一...
  • 文章目录1 C++中的赋值操作符重载1.1 赋值操作符重载 1 C++中的赋值操作符重载 1.1 赋值操作符重载 关于赋值操作符: 编译器为每个类默认重载赋值操作符。 默认的赋值操作符仅完成浅拷贝。 当需要进行深拷贝时...
  • 赋值操作符重载

    2018-02-08 18:07:43
    什么时候需要重载赋值操作符?编译器是否提供默认赋值操作? 编译器为每个类默认重载赋值操作符。 默认的赋值操作符仅完成浅拷贝。 当需要进行深拷贝时必须重载赋值操作符赋值操作符与拷贝构造函数有相同...
  • C++赋值操作符重载

    千次阅读 2015-11-21 18:32:57
    1.赋值操作符重载的原因赋值操作符是一个使用频率最高操作之一,通常情况下它意义十分明确,就是将两个同类型变量值从一端(右端)传到另一端(左端)。但在以下两种情况下,需要对赋值操作符进行重载。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,539
精华内容 615
关键字:

赋值操作符的重载