zoukankan      html  css  js  c++  java
  • 复制构造函数 与 赋值函数 的区别

    构造函数、析构函数、赋值函数是每个类最基本的的函数。每个类只有一个析构函数和一个赋值函数。但是有很多构造函数(一个为复制构造函数,其他为普通构造函数。对于一个类A,如果不编写上述四个函数,c++编译器将自动为A产生四个默认的函数,即:

    • A(void)                                    //默认无参数构造函数
    • A(const A &a)                         //默认复制构造函数
    • ~A(void);                                //默认的析构函数
    • A & operator = (const A &a); //默认的赋值函数

    既然能自动生成函数,为什么还需要自定义?原因之一是“默认的复制构造函数”和"默认的赋值函数“均采用”位拷贝“而非”值拷贝“

    位拷贝  v.s.  值拷贝

    为便于说明,以自定义String类为例,先定义类,而不去实现。

    复制代码
    #include <iostream>
    using namespace std;
    
    class String  
    {
        public:
            String(void);
            String(const String &other);
            ~String(void);
            String & operator =(const String &other);
        private:
     
    char *m_data;
    int val; };
    复制代码

    位拷贝拷贝的是地址,而值拷贝拷贝的是内容。

    如果定义两个String对象a, b。当利用位拷贝时,a=b,其中的a.val=b.val;但是a.m_data=b.m_data就错了:a.m_data和b.m_data指向同一个区域。这样出现问题:

    • a.m_data原来的内存区域未释放,造成内存泄露
    • a.m_data和b.m_data指向同一块区域,任何一方改变,会影响到另一方
    • 当对象释放时,b.m_data会释放掉两次

    因此

    当类中还有指针变量时,复制构造函数和赋值函数就隐含了错误。此时需要自己定义。

    结论

    • 有一种特别常见的情况需要自己定义复制控制函数:类具有指针哈函数。
    • 赋值操作符和复制构造函数可以看成一个单元,当需要其中一个时,我们几乎也肯定需要另一个
    • 三法则:如果类需要析构函数,则它也需要赋值操作符和复制构造函数

    注意

    • 如果定义复制构造函数(别的不管),编译器会自动生成默认复制构造函数
    • 如果定义了其他构造函数(包括复制构造函数),编译器不会生成默认构造函数
    • 即使自己写了析构函数,编译器也会自动生成默认析构函数

    因此此时如果写String s是错误的,因为定义了其他构造函数,就不会自动生成无参默认构造函数。

    复制构造函数  v.s.  赋值函数

    复制代码
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class String  
    {
        public:
            String(const char *str);
            String(const String &other);
            String & operator=(const String &other);
            ~String(void); 
        private:
            char *m_data;
    };
    
    String::String(const char *str)
    {
        cout << "自定义构造函数" << endl;
        if (str == NULL)
        {
            m_data = new char[1];
            *m_data = '';
        }
        else
        {
            int length = strlen(str);
            m_data = new char[length + 1];
            strcpy(m_data, str);
        }
    }
    
    String::String(const String &other)
    {
        cout << "自定义拷贝构造函数" << endl;
        int length = strlen(other.m_data);
        m_data = new char[length + 1];
        strcpy(m_data, other.m_data);
    }
    
    String & String::operator=(const String &other)
    {
        cout << "自定义赋值函数" << endl; 
    
        if (this == &other)
        {
            return *this;
        }
        else
        {
            delete [] m_data;
            int length = strlen(other.m_data);
            m_data = new char[length + 1];
            strcpy(m_data, other.m_data);
            return *this;
        }
    }
    
    String::~String(void)
    {
        cout << "自定义析构函数" << endl; 
        delete [] m_data;
    }
    int main()
    {
        cout << "a("abc")" << endl;
        String a("abc");
    
        cout << "b("cde")" << endl;
        String b("cde");
        
        cout << " d = a" << endl;
        String d = a;
    
        cout << "c(b)" << endl;
        String c(b);
    
        cout << "c = a" << endl;
        c = a;
    
        cout << endl;
    }
    复制代码

    执行结果

    说明几点

    1. 赋值函数中,上来比较 this == &other 是很必要的,因为防止自复制,这是很危险的,因为下面有delete []m_data,如果提前把m_data给释放了,指针已成野指针,再赋值就错了

    2. 赋值函数中,接着要释放掉m_data,否则就没机会了(下边又有新指向了)

    3. 拷贝构造函数是对象被创建时调用,赋值函数只能被已经存在了的对象调用

        注意:String a("hello"); String b("world");  调用自定义构造函数

                 String c=a;调用拷贝构造函数,因为c一开始不存在,最好写成String c(a);

  • 相关阅读:
    设计模式学习总结系列应用实例
    【研究课题】高校特殊学生的发现及培养机制研究
    Linux下Oracle11G RAC报错:在安装oracle软件时报file not found一例
    python pro practice
    openstack python sdk list tenants get token get servers
    openstack api
    python
    git for windows
    openstack api users list get token get servers
    linux 流量监控
  • 原文地址:https://www.cnblogs.com/wuchanming/p/3734344.html
Copyright © 2011-2022 走看看