精华内容
下载资源
问答
  • 重载赋值运算符

    2019-11-25 19:46:06
    1、为什么要重载赋值运算符? 预定义的赋值运算符"="要求左右两边的操作数类型是匹配的或者至少是兼容的。有时希望赋值运算符两边的操作数即使类型不兼容也能成立。此时可以通过重载赋值运算符来完成这种需求。 2...

    1、为什么要重载赋值运算符?
    预定义的赋值运算符"="要求左右两边的操作数类型是匹配的或者至少是兼容的。有时希望赋值运算符两边的操作数即使类型不兼容也能成立。此时可以通过重载赋值运算符来完成这种需求。

    2、重载赋值运算符的特点

    • C++规定,只能将赋值运算符重载为成员函数类型。

    3、举例

    class String {
    private:
    	char * str;
    public:
    	String ():str(new char[1]) { str[0] = 0;}//无参构造函数
    	const char * c_str() { return str; };//普通成员函数
    	String & operator = (const char * s);//重载赋值运算符的符号成员函数
    	String( ) { delete [] str; } //析构成员函数
    };
    
    //重载“=”以使得 obj = “hello”能够成立
    String & String::operator = (const char * s)
    {   
    	delete [] str;
    	str = new char[strlen(s)+1];
    	strcpy( str, s);
    	return * this;
    }
    
    int main()
    {
    	String s;
    	s = "Good Luck," ; //等价于 s.operator=("Good Luck,");  左边是String类类型,右边是string类型。赋值符号两边虽然类型不匹配,但是由于重载了赋值运算符,它能够调用符号函数,使得编译能够通过。
    	cout << s.c_str() << endl;
    	// String s2 = "hello!";  这条语句要是不注释掉就会出错。因为这是初始化语句,而不是赋值语句。重载赋值运算符只在赋值语句中起作用。
    	s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!");  这是属于赋值语句。
    	cout << s.c_str() << endl;
    	return 0;
    }
    /*
    输出:
    Good Luck,
    Shenzhou 8!
    */
    

    4、该例中存在的问题

    上述代码中执行:

    String s2 = "hello!";
    

    这条语句会发生错误,因为这里的等号并不是赋值的意思,而是初始化的意思。那么如何解决这种定义时初始化的问题呢?

    解决方案:
    可以定义一个类型转换构造函数:

    	String(char* s){
            str = new char[strlen(s)+1];
            strcpy( str, s);
        }
    

    这样,在这行这条语句时,运行的将会是类型转换构造函数。

    5、为什么将赋值运算符函数的返回值写成引用类型?

    首先考虑可选的类型:

    • void
    • String
    • String

    我们写符号函数应该尽量符合符号原有的用法习惯。
    (1)写成void的带来的问题:

    a = b = c;
    

    如果赋值运算符的返回类型为空,那么执行该语句就会出现错误。首先,运算符重载并不改变运算符的优先级和结合性,由于赋值运算符的右结合性,因此先执行b=c,此时能够正确执行,但是执行的结果为void类型,将一个空类型赋值给String类型的a对象,显然会导致错误。但是按照我们原有的对赋值运算符的用法,它是可以被用来连等的。解决这个问题的方法是,将返回值类型设置为String,或者String&。

    (2)写成String带来的问题:

    (a=b)=c;
    

    解释一:
    此时由于括号“()”改变了结合顺序,所以先执行a=b,结果是将b的值赋值给a,这里没有问题。但是,因为函数的返回值类型只有为引用时返回得到的才是左值,为其他类型时返回得到是右值。右值是不可以位于赋值运算符的左侧的,因此该语句会导致结果异常。

    解释二:
    此时由于括号“()”改变了结合顺序,所以先执行a=b,结果是将b的值赋值给a,这里没有问题。该函数表达式调用赋值符号函数,返回一个String类型的临时变量。然后再次调用赋值符号函数,将c的值赋值给该临时变量。因此,输出a的话得到的结果仍是b的值。

    这两条解释的区别在于,赋值符号函数返回的结果是左值,还是非左值。可以在编译器上跑一下是哪种结果。此时可以把=看成是一个函数,这个函数返回值是左值,那么赋值符号返回的就是左值。这个函数返回值是右值,那么赋值符号返回的就是右值。在《C++ primer》一书中提到,函数返回值只有为引用类型时,返回结果才是左值,而这里显然不是左值。所以按照这样分析,应该是解释一成立。

    实际上,无论这两条解释的哪一条成立,导致的结果是:我们不想看到的效果。

    6、实验验证上述解释

    • 将赋值符号函数返回值改为void类型,在Codeblock上面跑此代码。确实如同上面预想的一样。在执行连等的时候会发生意外。
    • 将赋值符号函数返回值改为String类型,在Codeblock上面跑此代码。和上述解释中任何一个都不符合,实验的结果显示,将赋值符号函数返回值更改为String类型后,甚至连一个等号的赋值都会发生意外。代码与结果如下(代码较上述代码有所优化):
    #include<iostream>
    #include<cstring>
    using namespace std;
    class String {
    private:
    	char * str;
    public:
    	String ():str(NULL) { }//无参构造函数
    	const char * c_str() const{ return str; };//普通成员函数
    	String  operator = (const char * s);//重载赋值运算符的符号成员函数
    	~String( ) {
    	if(str)
            delete [] str;
        } //析构成员函数
    };
    
    //重载“=”以使得 obj = “hello”能够成立
    String String::operator = (const char * s)
    {
        cout <<"operator = " << endl;
        if(str)
            delete [] str;
        if(s){
        cout <<"if s " << endl;
            str = new char[strlen(s)+1];
            strcpy( str, s);
        }else{
            str=NULL;
        }
        cout << (*this).c_str() << endl;
    	return *this;
    }
    
    int main()
    {
    	String s,s1,s2;
    	s = "Good Luck,") ; 
        cout <<"s1"<< s1.c_str() << endl;
        s1 = "shenzhou8";
        cout <<"s1:"<< s1.c_str() << endl;
        s2=s1=s;
        cout <<"s2:"<< s2.c_str() << endl;
    	return 0;
    }
    

    在这里插入图片描述

    展开全文
  • C++重载赋值运算符

    2021-06-17 10:26:53
    重载赋值运算符 上面我们讨论了“对象赋值异常”的问题。当对象的成员变量是指针类型时,对象之间的赋值,应该要对指针成员变量进行特殊操作。 要解决这个问题,需要重载赋值运算符。那么,运算符的重载在后续...

    重载赋值运算符

           上面我们讨论了“对象赋值异常”的问题。当对象的成员变量是指针类型时,对象之间的赋值,应该要对指针成员变量进行特殊操作。

           要解决这个问题,需要重载赋值运算符。那么,运算符的重载在后续章节深入学习。

           在这里,我们预先学习“赋值运算符重载”的知识。是为了让知识点归纳连贯起来。让读者知道,对象的赋值存在浅拷贝的问题。需要重载赋值运算符,进行深拷贝,才可以解决问题。

           所以,我们教学的方法,不是死板地列出C++的知识点,而是针对问题,分析问题,解决问题。当学习了运算符的重载之后,再回来看该章节也行。但是,我们需要知道的一个知识点是:对象赋值存在浅拷贝问题,需要重载赋值运算符,进行深拷贝。

           下面是一个测试例子,重载了赋值运算符。

    程序运行结果如下:

           可以看到,使用stud对象给stud1对象赋值,当stud对象销毁之后,stud1对象的数据还是正常。因为我们增加了“重载赋值运算符”函数的定义,在该函数中,对涉及到指针类型的成员变量,进行内存申请和数据拷贝。该函数的定义如下:

        void operator = (const student& s){

            if(NULL != name){

                delete []name; //如果name已经申请内存,先释放内存;

            }

            name = new char[32]; //申请内存

            strcpy(name, s.name); //拷贝数据

            if(NULL != addr){

                delete []addr; //如果addr已经申请内存,先释放内存;

            }

            addr = new char[32]; //申请内存

            strcpy(addr, s.addr); //拷贝数据

            number = s.number;

        }

           可以看到,给对象赋值的时候,对象的name, addr是指针类型,那么,需要申请内存再拷贝数据。而且,如果name, addr成员变量不是NULL类型,可能是已经申请了内存空间,那么,我们对内存空间进行释放,再次申请新的内存空间。保证对象的指针成员变量得到合法的内存地址。

           上面的代码,我们重载了赋值运算符,满足两个对象之间的赋值。那么,如果有如下的main()函数,会产生什么结果?

    int main(void){

        student stud1, stud2;

        if(1){

            student stud("wkf", "www.mylinux.vip", 13926572996);

            stud1 = stud2 = stud;

        }

        stud1.print();

        stud2.print();

        return 0;

    }

           编译异常,异常信息如下:

    wkf@ubuntu:~/c++$ g++ test.cpp -o exe

    test.cpp: In function ‘int main()’:

    test.cpp:55: error: no match for ‘operator=’ in ‘stud1 = stud2.student::operator=(((const student&)((const student*)(& stud))))’

    test.cpp:36: note: candidates are: void student::operator=(const student&)

           可以看到,执行stud1 = stud2 语句的时候,出现了错误。那么,为什么会这样?

           执行 stud2 = stud; 成功,但是,执行 stud1 = stud2 语句失败?

           这个问题,当深入学习运算符重载的时候,就可以解决。解决方法就是修改重载赋值运算符,如下:

        student& operator = (const student& s){

            if(NULL != name){

                delete []name;

            }

            name = new char[32];

            strcpy(name, s.name);

            if(NULL != addr){

                delete []addr;

            }

            addr = new char[32];

            strcpy(addr, s.addr);

            number = s.number;

            return *this;

        }

           此时,编译程序,运行OK。程序分析如下:

    (1) 执行stud1 = stud2 = stud;语句,分成两个步骤,先执行 stud2 = stud; 语句,把得到的返回值再赋给stud1对象。

    (2) 执行 stud2 = stud; 语句的时候,调用重载的赋值运算符,相当于:

    stud2.operator = (stud);

           此时,是stud2对象调用重载的赋值运算符函数,调用完之后,返回一个student对象的引用。所以,在重载赋值运算符中,赋值完之后,返回*this对象,就是返回stud2对象。

           所以,把stud2对象返回,再赋值给stud1对象;相当于stud1 = stud2; 此时,才是正确的逻辑。

    韦凯峰 Linux C/C++ 程序设计教程,Linux 系统编程,Openwrt 系统开发,微信:13926572996,QQ:1523520001,博客:www.mylinux.vip

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • C++ 重载赋值运算符

    2020-03-19 09:10:34
    在定义的同时进行赋值叫做初始化(Initialization),定义完成以后再赋值(不管在定义...即使我们没有显式的重载赋值运算符,编译器也会以默认地方式重载它。默认重载的赋值运算符功能很简单,就是将原有对象的所有...

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

    当以拷贝的方式初始化一个对象时,会调用拷贝构造函数;当给一个对象赋值时,会调用重载过的赋值运算符。

    即使我们没有显式的重载赋值运算符,编译器也会以默认地方式重载它。默认重载的赋值运算符功能很简单,就是将原有对象的所有成员变量一一赋值给新对象,这和默认拷贝构造函数的功能类似。

    对于简单的类,默认的赋值运算符一般就够用了,我们也没有必要再显式地重载它。但是当类持有其它资源时,例如动态分配的内存、打开的文件、指向其他数据的指针、网络连接等,默认的赋值运算符就不能处理了,我们必须显式地重载它,这样才能将原有对象的所有数据都赋值给新对象。

    我们以 Array 类为例,该类拥有一个指针成员,指向动态分配的内存。为了让 Array 类的对象之间能够正确地赋值,我们必须重载赋值运算符。请看下面的代码:

    #include <iostream>
    #include <cstdlib>
    using namespace std;
    
    //变长数组类
    class Array{
    public:
        Array(int len);
        Array(const Array &arr);  //拷贝构造函数
        ~Array();
    public:
        int operator[](int i) const { return m_p[i]; }  //获取元素(读取)
        int &operator[](int i){ return m_p[i]; }  //获取元素(写入)
        Array & operator=(const Array &arr);  //重载赋值运算符
        int length() const { return m_len; }
    private:
        int m_len;
        int *m_p;
    };
    
    Array::Array(int len): m_len(len){
        m_p = (int*)calloc( len, sizeof(int) );
    }
    
    Array::Array(const Array &arr){  //拷贝构造函数
        this->m_len = arr.m_len;
        this->m_p = (int*)calloc( this->m_len, sizeof(int) );
        memcpy( this->m_p, arr.m_p, m_len * sizeof(int) );
    }
    
    Array::~Array(){ free(m_p); }
    
    Array &Array::operator=(const Array &arr){  //重载赋值运算符
        if( this != &arr){  //判断是否是给自己赋值
            this->m_len = arr.m_len;
            free(this->m_p);  //释放原来的内存
            this->m_p = (int*)calloc( this->m_len, sizeof(int) );
            memcpy( this->m_p, arr.m_p, m_len * sizeof(int) );
        }
        return *this;
    }
    
    //打印数组元素
    void printArray(const Array &arr){
        int len = arr.length();
        for(int i=0; i<len; i++){
            if(i == len-1){
                cout<<arr[i]<<endl;
            }else{
                cout<<arr[i]<<", ";
            }
        }
    }
    
    int main(){
        Array arr1(10);
        for(int i=0; i<10; i++){
            arr1[i] = i;
        }
        printArray(arr1);
       
        Array arr2(5);
        for(int i=0; i<5; i++){
            arr2[i] = i;
        }
        printArray(arr2);
        arr2 = arr1;  //调用operator=()
        printArray(arr2);
        arr2[3] = 234;  //修改arr1的数据不会影响arr2
        arr2[7] = 920;
        printArray(arr1);
       
        return 0;
    }
    

    运行结果:

    0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    0, 1, 2, 3, 4
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    

    将 arr1 赋值给 arr2 后,修改 arr2 的数据不会影响 arr1。如果把 operator=() 注释掉,那么运行结果将变为:

    0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    0, 1, 2, 3, 4
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    0, 1, 2, 234, 4, 5, 6, 920, 8, 9 
    

    去掉operator=()后,由于 m_p 指向的堆内存会被 free() 两次,所以还会导致内存错误。

    分析重载过的赋值运算符。

    1 operator=() 的返回值类型为Array &,这样不但能够避免在返回数据时调用拷贝构造函数,还能够达到连续赋值的目的。下面的语句就是连续赋值:
    arr4 = arr3 = arr2 = arr1;

    2 if( this != &arr)语句的作用是「判断是否是给同一个对象赋值」:如果是,那就什么也不做;如果不是,那就将原有对象的所有成员变量一一赋值给新对象,并为新对象重新分配内存。下面的语句就是给同一个对象赋值:

    arr1 = arr1;
    arr2 = arr2;
    

    3 return *this表示返回当前对象(新对象)。

    4 operator=() 的形参类型为const Array &,这样不但能够避免在传参时调用拷贝构造函数,还能够同时接收 const 类型和非 const 类型的实参。

    5 赋值运算符重载函数除了能有对象引用这样的参数之外,也能有其它参数。但是其它参数必须给出默认值,例如:

    Array & operator=(const Array &arr, int a = 100);
    
    展开全文
  • c++何时需要重载赋值运算符(operator=) 某些情况下,当我们编写一个类的时候,,并不需要为该类重载“=”运算符,因为编译系统为每个类提供了默认的赋值运算符“=”,使用这个默认的赋值运算符操作类对象时,该...

    c++何时需要重载赋值运算符(operator=)

    某些情况下,当我们编写一个类的时候,,并不需要为该类重载“=”运算符,因为编译系统为每个类提供了默认的赋值运算符“=”,使用这个默认的赋值运算符操作类对象时,该运算符会把这个类的所有数据成员都进行一次赋值操作。

    以下三种情况显性重载赋值运算符

    1. 当对同类的两个对象a,b,代码中有语句a=b,在进行析构的时候,由于重复释放一块内存,会导致程序崩溃报错。在这种情况下,就需要我们重载赋值运算符“=”。

    2. 用非类A类型的值为类A的对象赋值时

    3. 当用类A类型的值为类A的对象赋值,且类A的数据成员中含有指针的情况下,必须显式提供赋值运算符重载函数

    展开全文
  • 重载赋值运算符(解决指针悬挂) —— (与拷贝构造函数结合起来理解) c++中,对于任何一个类,如果没有用户自定义的赋值运算符函数,系统会自动的为其生成一个默认的赋值运算符函数,以完成数据成员之间的逐位复制...
  • mfc 重载赋值运算符

    2016-12-15 10:15:00
    重载赋值运算符= 一、重载运算符格式 返回类型 operator 运算符 (参数); 如: bool operator=(char*s); int operator>(char*s); bool operator new(int size); 二、重载赋值运算符= //头文件 ...
  • 重载赋值运算符=

    2015-01-05 11:37:29
    1.注意重载赋值运算符和[],(),->运算符必须定义为类的成员函数。 2.注意:如果程序不提供显示的赋值运算符则系统会提供一个默认的赋值运算符。 3.什么时候重载赋值运算符:当类中含有指针成员时,一般都要重...
  • C++中重载赋值运算符=

    千次阅读 2019-04-16 09:02:09
    1.C++重载=(赋值运算符) 在定义的同时进行赋值叫做初始化(Initialization),定义完成以后再赋值(不管在定义的时候...即使没有显式的重载赋值运算符,编译器也会以默认地方式重载它。默认重载的赋值运算符功能很...

空空如也

空空如也

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

重载赋值运算符