精华内容
下载资源
问答
  • 赋值操作符重载

    2018-02-08 18:07:43
    1 关于赋值的疑问 什么时候需要重载赋值操作符?编译器是否提供默认的赋值操作?...编程实验:默认赋值操作符重载 #include <iostream> #include <string> using n...

    1 关于赋值的疑问

    什么时候需要重载赋值操作符?编译器是否提供默认的赋值操作?

    • 编译器为每个类默认重载了赋值操作符。
    • 默认的赋值操作符仅完成浅拷贝。
    • 当需要进行深拷贝时必须重载赋值操作符。
    • 赋值操作符与拷贝构造函数有相同的存在意义。

    编程实验:默认赋值操作符重载

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Test
    {
        int* m_pointer;
    public:
        Test()
        {
            m_pointer = NULL;
        }
        Test(int i)
        {
            m_pointer = new int(i);
        }
        Test(const Test& obj)
        {
            m_pointer = new int(*obj.m_pointer);
        }
        Test& operator = (const Test& obj) /*为了连续赋值,赋值重载函数的返回值一定是个引用*/
        {
            if( this != &obj ) /*为了防止自赋值操作*/
            {
                delete m_pointer;
                m_pointer = new int(*obj.m_pointer);
            }
    
            return *this;
        }
        void print()
        {
            cout << "m_pointer = " << hex << m_pointer << endl;
        }
        ~Test()
        {
            delete m_pointer;
        }
    };
    
    int main()
    {
        Test t1 = 1;
        Test t2;
    
        t2 = t1;   /*同一个指针不能释放多次,否则会程序崩溃*/
    
        t1.print();
        t2.print();
    
        return 0;
    }
    

    这里写图片描述

    重载赋值操作符的四个注意事项:

    1.返回值类型为当前类的引用。
    2.参数为const修饰的类的引用。
    3.不能自赋值。
    4.返回当前对象(return *this)。

    一般性原则:重载赋值操作符,必然需要实现深拷贝!

    编程实验:数组类的优化

    IntArray.h

    #ifndef _INTARRAY_H_
    #define _INTARRAY_H_
    
    class IntArray
    {
    private:
        int m_length;
        int* m_pointer;
    
        IntArray(int len);
        IntArray(const IntArray& obj);
        bool construct();
    public:
        static IntArray* NewInstance(int length); 
        int length();
        bool get(int index, int& value);
        bool set(int index ,int value);
        int& operator [] (int index);
        IntArray& operator = (const IntArray& obj);
        IntArray& self();
        ~IntArray();
    };
    
    #endif
    

    IntArray.cpp

    #include "IntArray.h"
    
    IntArray::IntArray(int len)
    {
        m_length = len;
    }
    
    bool IntArray::construct()
    {
        bool ret = true;
    
        m_pointer = new int[m_length];
    
        if( m_pointer )
        {
            for(int i=0; i<m_length; i++)
            {
                m_pointer[i] = 0;
            }
        }
        else
        {
            ret = false;
        }
    
        return ret;
    }
    
    IntArray* IntArray::NewInstance(int length) 
    {
        IntArray* ret = new IntArray(length);
    
        if( !(ret && ret->construct()) ) 
        {
            delete ret;
            ret = 0;
        }
    
        return ret;
    }
    
    int IntArray::length()
    {
        return m_length;
    }
    
    bool IntArray::get(int index, int& value)
    {
        bool ret = (0 <= index) && (index < length());
    
        if( ret )
        {
            value = m_pointer[index];
        }
    
        return ret;
    }
    
    bool IntArray::set(int index, int value)
    {
        bool ret = (0 <= index) && (index < length());
    
        if( ret )
        {
            m_pointer[index] = value;
        }
    
        return ret;
    }
    
    int& IntArray::operator [] (int index)
    {
        return m_pointer[index];
    }
    
    IntArray& IntArray::operator = (const IntArray& obj)
    {
        if( this != &obj )
        {
            int* pointer = new int[obj.m_length];
    
            if( pointer )
            {
                for(int i=0; i<obj.m_length; i++)
                {
                    pointer[i] = obj.m_pointer[i];
                }
    
                m_length = obj.m_length;
                delete m_pointer;
                m_pointer = pointer;
            }
        }
    
        return *this;
    }
    
    IntArray& IntArray::self()
    {
        return *this;
    }
    
    IntArray::~IntArray()
    {
        delete[]m_pointer;
    }
    

    main.cpp

    #include <iostream>
    #include <string>
    #include "IntArray.h"
    
    using namespace std;
    
    int main()
    {
        IntArray* a = IntArray::NewInstance(5);   
        IntArray* b = IntArray::NewInstance(10);
    
        if( a && b )
        {
            IntArray& array = a->self();
            IntArray& brray = b->self();
    
            cout << "array.length() = " << array.length() << endl;
            cout << "brray.length() = " << brray.length() << endl;
    
            array = brray;
    
            cout << "array.length() = " << array.length() << endl;
            cout << "brray.length() = " << brray.length() << endl;
        }
    
        delete a;
        delete b;
    
        return 0;
    }
    

    小结

    • 在需要进行深拷贝的时候必须重载赋值操作符。
    • 赋值操作符和拷贝构造函数有同等的意义。
    展开全文
  • 1.赋值操作符重载的原因 赋值操作符是一个使用频率最高的操作之一,通常情况下它的意义十分明确,就是将两个同类型的变量的值从一端(右端)传到另一端(左端)。但在以下两种情况下,需要对赋值操作符进行重载。 一...
  • 文章目录1 C++中的赋值操作符重载1.1 赋值操作符重载 1 C++中的赋值操作符重载 1.1 赋值操作符重载 关于赋值操作符: 编译器为每个类默认重载了赋值操作符。 默认的赋值操作符仅完成浅拷贝。 当需要进行深拷贝时...

    1 C++中的赋值操作符重载

    1.1 赋值操作符重载

    关于赋值操作符:

    • 编译器为每个类默认重载了赋值操作符。
    • 默认的赋值操作符仅完成浅拷贝。
    • 当需要进行深拷贝时必须重载赋值操作符。
    • 赋值操作符与拷贝构造函数有相同的存在意义。

    一般性原则:重载赋值操作符,必然需要实现深拷贝!

    赋值操作符重载示例:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Test
    {
        int* m_pointer;
    public:
        Test()
        {
            m_pointer = NULL;
        }
        Test(int i)
        {
            m_pointer = new int(i);
        }
        Test(const Test& obj)
        {
            m_pointer = new int(*obj.m_pointer);
        }
        Test& operator = (const Test& obj)
        {
            if( this != &obj )
            {
                delete m_pointer;
                m_pointer = new int(*obj.m_pointer);
            }
            
            return *this;
        }
        void print()
        {
            cout << "m_pointer = " << hex << m_pointer << endl;
        }
        ~Test()
        {
            delete m_pointer;
        }
    };
    
    int main()
    {
        Test t1 = 1;
        Test t2;
        
        t2 = t1;
        
        t1.print();
        t2.print();
        
        return 0;
    }
    
    

    参考资料:

    1. C++深度解析教程
    展开全文
  • C++赋值操作符重载

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

    1.赋值操作符重载的原因

    赋值操作符是一个使用频率最高的操作之一,通常情况下它的意义十分明确,就是将两个同类型的变量的值从一端(右端)传到另一端(左端)。但在以下两种情况下,需要对赋值操作符进行重载。
    一是赋值号两边的表达式类型不一样,且无法进行类型转换。
    二是需要进行深拷贝。

    2. 赋值操作符重载的注意事项

    赋值操作符只能通过类的成员函数的形式重载。这就说明了,如果要将用户自定义类型的值传递给基本数据类型的变量,只能通过类型转换机制,而不能利用重载来实现。

    当赋值号两边的表达式不一致的时候,可能需要对赋值操作符进行重载,见下面的例子。

    #include <iostream>
    using namespace std;
    
    class A
    {
    	int num;
    public:
    	A(){num=0;}
    	A(int i){num=i;}
    	void show(){
    		cout<<num<<endl;
    	}
    };
    
    int main(int argc, char* argv[])
    {
    	A a=5;		 //符值符号两边的数据类型不一样,这里表示创建新对象
    	a.show();
    	A a1;
    	a1=1;        //赋值号两边的数据类型不一样,这是真正的赋值运算
    	a1.show();   
    }
    

    程序的输出结果是:

    5
    1
    

    在语句A a=5中,虽然用到了“=”,但它的语义是构造一个类A的对象a,它等价于语句A a(5),所以该语句与赋值无关。而语句a1=1是一个真正的赋值语句,变量a1的类型是A,而常量1的类型是int,由于可以通过类A的构造函数A(int)将类型int转换成类型A(实际上是以int为参数构造了一个类A的临时对象),然后再完成赋值操作,所以不必再对赋值操作符进行重载。

    3.深拷贝情况下对赋值操作符重载

    深拷贝是对赋值操作符进行重载的一个因素。那么什么是深拷贝呢?简单的说,深拷贝是在把一个类对象a拷贝到另一个对象b中去时,如果对象a中包含非悬挂指针(野指针),那么要将a的指针所指区域的内容拷贝到b的相应指针所指的区域中去。进行深拷贝时,一般对象a和b有相同的数据类型。如果在进行赋值时发生深拷贝,就一定要对赋值操作符进行重载,否则赋值运算符就会按赋值的常规语义进行(成员变量之间传递数据),而不发生深拷贝。考察如下例子。

    #include <iostream>
    using namespace std;
    
    class Student
    {
    	char* name;
    	int age;
    public:
    	Student()
    	{
    		name=new char[20];
    	}
    
    	Student(char* n, int a)
    	{
    		name=new char[20];
    		if(name) strcpy(name,n);
    		age=a;
    	}
    
    	Student(const Student& s)
    	{
    		name=new char[20];
    		*this=s;
    	}
    	
    	void show()
    	{
    		cout<<"The student's name is "<<name;
    		cout<<" and of age "<<age<<endl;
    	}
    
    	~Student()
    	{
    		delete[] name;
    	}
    	
    	Student& operator=(const Student &s)
    	{
    		if(name) strcpy(name,s.name);
    		age=s.age;
    		return *this;
    	}
    };
    
    int main()
    {
    	Student s1("张三",18),s4("李四",20);
    	Student s2;
    	s1.show();
    	s2=s4;
    	s2.show();
    	Student s3=s1;
    	s3.show();
    	return 0;
    }
    

    程序的输出结果是:

    The student's name is 张三 and of age 18
    The student's name is 李四 and of age 20
    The student's name is 张三 and of age 18
    

    阅读以上程序,注意如下几点。
    (1)由于在类Student中,存在指针成员name,所以,当两个Student类成员之间赋值时,必须使用深拷贝。执行s2=s4;语句,就是将s4对象赋值给s2,其中将s4.name字符串的内容拷入s2.name就是对深拷贝的具体体现。

    (2)类的拷贝构造函数虽然与赋值操作符并不是一回事,但通常可以在拷贝构造函数中利用赋值操作符重载,以避免对两个对象之间传递数据的重复解释。

    (3)上面的程序,直接使用strcpy(name,s.name);实现两个对象的字符串成员的数据传递。这是一种简化的做法,存在很多隐患。比如如果源字符串的长度超过20个字符,此程序会出现运行时错误。解决的办法是根据原字符串的长度,重新分配目的字符串的长度,再次之前还要释放目的字符串的空间。另外,一个对象赋值给自己,也会出现问题,需要进行源对象和目的对象地址的比较,再考虑赋不赋值。

    (4)由于深拷贝会涉及到内存的动态分配和释放等一些较为复杂的操作,所以程序员在编写自定义类时要尽量避免深拷贝的出现。例如,在上例中,将成员变量name定义成string name,就可以避免自己编写实现深拷贝的代码。实际的深拷贝工作是由string类来完成,而string类是C++标准库提供的,我们可放心使用。

    (5)最赋值操作符进行重载时,通常将操作符函数的返回值定义为赋值左操作数类型的引用。这是为了实现对赋值表达式的求值,还有一个目的就是为了实现链式操作。

    展开全文
  • 赋值操作符重载 上篇文章着重讲述了复制构造函数的重要性,以及如果没有实现复制构造函数可能出现的致命错误,同时也指出赋值操作符重载在类中也和赋值构造函数一样重要. 但赋值操作符重载有着其独特的性质,本篇文章就...

    赋值操作符重载

    上篇文章着重讲述了复制构造函数的重要性,以及如果没有实现复制构造函数可能出现的致命错误,同时也指出赋值操作符重载在类中也和赋值构造函数一样重要.
    但赋值操作符重载有着其独特的性质,本篇文章就专门介绍赋值操作符重载的一些性质,以及在实际开发中需要注意的地方.

    引出问题

    为了描述问题,我们简单设计一个类People, 包含三个成员m_pName, m_nAge, m_nGender,其中m_pName被声明为指向char类型的指针,用于保存姓名字符串.类头文件和cpp实现文件如下:
    People.h

    #ifndef PEOPLE_H
    #define PEOPLE_H
    
    class People
    {
        public:
            People();
            People(const char *name, int age, int gender);
            //People(const People &pp);
    
            virtual ~People();
    
            People &operator=(const People &pp);
    
            void Print() const;
        protected:
    
        private:
            char *m_pName;
            int m_nAge;
            int m_nGender;
    };
    
    #endif // PEOPLE_H
    

    People.cpp

    #include "People.h"
    #include <string.h>
    #include <iostream>
    using namespace std;
    
    People::People()
    {
        m_pName = new char[1];
        m_pName[0] = 0;
        m_nAge = 0;
        m_nGender = -1;
    }
    
    People::People(const char *name, int age, int gender)
    {
        m_pName = new char[strlen(name)+1];
        strcpy(m_pName, name);
        m_nAge = age;
        m_nGender = gender;
    }
    
    //People::People(const People &pp)
    //{
    //    m_pName = new char[strlen(pp.m_pName)+1];
    //    strcpy(m_pName, pp.m_pName);
    //    m_nAge = pp.m_nAge;
    //    m_nGender = pp.m_nGender;
    //}
    
    People::~People()
    {
        delete []m_pName;
    }
    
    People &People::operator=(const People &pp)
    {
        if(this == &pp)
        {
            return *this;
        }
    
        delete []m_pName;
    
        m_pName = new char[strlen(pp.m_pName)+1];
        strcpy(m_pName, pp.m_pName);
        m_nAge = pp.m_nAge;
        m_nGender = pp.m_nGender;
    
        return *this;
    }
    
    void People::Print() const
    {
    
        std::cout<<"name:"<<m_pName<<endl;
        std::cout<<"age:"<<m_nAge<<endl;
        std::cout<<"gender:"<<m_nGender<<endl;
    }
    

    首先,我们吧复制构造函数注释掉,然后在main中调用People类的对象:
    main.cpp

    #include <iostream>
    
    using namespace std;
    
    #include "People.h"
    
    int main()
    {
        cout << "Hello world!" << endl;
        {
            People p("Emily", 28, 0);
            p.Print();
    
            People p1 = p;
            p1.Print();
    
            People p2;
            p2 = p1;
            p2.Print();
        }
        return 0;
    }
    

    运行main,结果输出如下图所示:
    在这里插入图片描述从输出结果分析,程序对m_pName释放了两次,引发异常,程序崩溃.

    分析

    即使实现了赋值操作符重载,当使用形如"type_name obj_dst = obj_src;"这样的赋值语句对对象赋值时,程序仍然没有调用赋值操作符,而是调用了编译器默认提供的复制构造函数.
    这是因为默认的复制构造函数形参只有一个,C++中对对象赋值时,将调用只有一个参数的构造函数,而此时这个构造函数正好与复制构造函数相匹配,所以调用了复制构造函数.但是我们在本例子中,把复制构造函数注释掉了,这样,程序就调用了编译器生成的复制构造函数,而默认的复制构造函数没有深拷贝m_pName成员,所以在析构函数被执行时,对象就试图delete不属于它创建的内存.

    总结

    必须实现复制构造函数,否则,只能使用先声明,后赋值的语法对对象赋值,如:
    People p1(“Alex”, 30, 1);
    PeoPle p2;
    p2 = p1;
    这时,重载的赋值操作符将被调用.

    展开全文
  • 在我们学习C++的过程中,我们难免会遇到类,在类中有6个默认的函数,它们分别为:构造函数、拷贝构造函数、析构函数、赋值操作符重载、取地址操作符重载和const修饰的取地址操作符重载。
  • 其中赋值操作符重载函数的声明为:`String& operator = (const String& str);` 函数参数为String对象的常量引用。 main.cpp如下: ![图片说明]...
  • class Student{ public : Student(){ cout << "构造函数\n"; } ~Student(){ cout <... //赋值操作符重载 Student& operator=(Student& s){ cout << "赋值操作符重
  • 在我们学习C++的过程中,我们难免会遇到类,在类中有6个默认的函数,它们分别为:构造函数、拷贝构造函数、析构函数、赋值操作符重载、取地址操作符重载和const修饰的取地址操作符重载。一、拷贝构造函数:1、什么是...
  • 赋值操作符重载函数形参规格 赋值操作符重载函数的形参基本都写为常量引用,返回值为引用。 class A { public: A(int data = 0) : m_data(data) {} ~A() {} A& operator=(const A& other) { if (this ==...
  • 类的6个默认成员函数构造函数特性1-7析构函数特性1-5拷贝构造函数特性1-3赋值操作符重载注意事项1-3赋值操作符重载取地址操作符重载const修饰的取地址操作符重载 构造函数 构造函数是一个特殊的成员函数,名字与类名...
  • 一、操作符重载 1.什么是操作符重载 就是把一些操作符通过关键字operator,组装成一个函数,关键字operator后面接需要重载的操作符符号 2.为什么需要重载操作符? (1)简单、方便,比起封装一个函数,使用...
  • 首先,拷贝构造函数的调用时机存在以下三种情况: (1)类对象的初始化;...而赋值操作符重载函数则是用一个已存在的类对象去赋值给另一个已存在的对象,即只更新其内容或值,而不是像拷贝构造函数那样去构造一个新的
  • 1,什么时候需要重载赋值操作符? 2,编译器是否提供默认的赋值操作符? 2,关于赋值的疑问: 1,编译器为每个类默认重载赋值操作符; 1,意味着同类型的类对象可以相互赋值; 2,默认的赋值操作符仅...
  • C++深度解析 经典问题解析三 --- 赋值操作符重载 和 拷贝构造函数,string类的疑问(35)       赋值操作符 什么时候需要重载赋值操作符? 编译器是否提供默认的赋值操作? 关于赋值的疑问: 编译器为每...
  • 赋值操作符重载 const成员函数 取地址及const取地址操作符重载 2. 构造函数 2.1 概念 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合...
  • 赋值操作符重载则可以在任何地方调用,不管左操作对象为新旧,都是将右操作对象的属性值拷贝到左操作对象去覆盖.调用复制构造函数时不用调用默认构造函数,而赋值操作符重载则必先调用构造函数新建一个对象再赋值....
  • 构造函数,拷贝构造函数,析构函数,输出操作符重载,赋值操作符重载,头插尾插,头删尾删,任意位置插入,任意位置删除,查找等
  • 一般来讲 赋值,下标,调用 和成员放文件头必须定义为成员函数,否则在编译时会出现错误复合复制操作符通常也应该定义为类的成员(当然不这么做不会出现编译错误)改变对象状态的自增 自减 解引用 应该定义为类成员...

空空如也

空空如也

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

赋值操作符重载