美文网首页
C++ 编程技巧与规范(二)

C++ 编程技巧与规范(二)

作者: e196efe3d7df | 来源:发表于2020-11-19 15:30 被阅读0次

    拷贝构造函数和拷贝复制运算符的书写

    如下:

    namespace demo2 {
        class A {
        public:
            A():m_i(0), m_j(0) {}
            A(int i, int j):m_i(i), m_j(j) {}
    
            A(const A& temp) {
                m_i = temp.m_i;
                m_j = temp.m_j;
            }
    
            A& operator=(const A& temp)
            {
                m_i = temp.m_i;
                m_j = temp.m_j;
                return *this;
            }
    
        public:
            int m_i;
            int m_j;
        };
    }
    
    int main()
    {
        demo2::A a1;
        demo2::A a2 = a1;   //拷贝构造
        demo2::A a3;
        a3 = a1;            //拷贝赋值
    }
    

    对象自我赋值产生的问题

    对象的自我赋值,会产生一些问题,比如:

    #pragma warning(disable:4996) //strcpy会触发4996警告,头部加入 #pragma warning(disable:4996) 即可
    namespace demo3 {
        class A {
        public:
            A() :m_i(0), m_j(0), m_charPtr(new char[100]){}
            A(int i, int j) :m_i(i), m_j(j), m_charPtr(new char[100]) {}
    
            A(const A& temp) {
                m_charPtr = new char[100];
                memcpy(m_charPtr, temp.m_charPtr, 100);
                m_i = temp.m_i;
                m_j = temp.m_j;
            }
    
            A& operator=(const A& temp)
            {
                delete m_charPtr;
    
                m_charPtr = new char[100];
                memcpy(m_charPtr, temp.m_charPtr, 100);
                m_i = temp.m_i;
                m_j = temp.m_j;
                return *this;
            }
    
        public:
            int m_i;
            int m_j;
            char* m_charPtr;
        };
    }
    
    int main()
    {
        demo3::A a;
        strcpy(a.m_charPtr, "abcdef"); //strcpy会触发4996警告,头部加入 #pragma warning(disable:4996) 即可
        a = a;
    }
    

    A的赋值运算符重载函数,会先清理自身的m_charPtr,然后重新分配内存,把目标的m_charPtr赋值给自身的m_charPtr。当自我赋值时,会目标对象和自身对象是同一块内存,所以清理后,数据丢失,无法进行复制。可以进行如下改动

    1. 如果是同一对象的自我赋值,则直接返回自身。
    A& operator=(const A& temp)
    {
        if (this == &temp)
        {
            return *this;
        }
    
        delete m_charPtr;
    
        m_charPtr = new char[100];
        memcpy(m_charPtr, temp.m_charPtr, 100);
        m_i = temp.m_i;
        m_j = temp.m_j;
        return *this;
    }
    
    1. 先把目标对象的资源复制一份,然后用复制的资源进行赋值。
    A& operator=(const A& temp)
    {
        char* tempCharPtr = new char[100];
        memcpy(tempCharPtr, temp.m_charPtr, 100);
    
        delete m_charPtr;
    
        m_charPtr = tempCharPtr;
        m_i = temp.m_i;
        m_j = temp.m_j;
        return *this;
    }
    

    第一种,简单明了。第二种,效率可能会更高

    继承关系下拷贝构造函数和拷贝复制运算符的书写

    如果在子类中没有定义自己的拷贝构造函数和拷贝复制运算符函数,则编译器会调用父类的拷贝构造函数和拷贝复制运算符函数。
    反之,如果子类中定义自己的拷贝构造函数和拷贝复制运算符函数,则编译器会调用子类字自身的拷贝构造函数和拷贝复制运算符函数,不再调用父类的拷贝构造函数和拷贝复制运算符函数,需要程序员自己去调用父类的拷贝构造函数和拷贝复制运算符函数。如下:

    namespace demo4 {
        class A {
        public:
            A() :m_i(0), m_j(0), m_charPtr(new char[100]) {}
            A(int i, int j) :m_i(i), m_j(j), m_charPtr(new char[100]) {}
    
            A(const A& temp) {
                m_charPtr = new char[100];
                memcpy(m_charPtr, temp.m_charPtr, 100);
                m_i = temp.m_i;
                m_j = temp.m_j;
            }
    
            A& operator=(const A& temp)
            {
                char* tempCharPtr = new char[100];
                memcpy(tempCharPtr, temp.m_charPtr, 100);
    
                delete m_charPtr;
    
                m_charPtr = tempCharPtr;
                m_i = temp.m_i;
                m_j = temp.m_j;
                return *this;
            }
            
            virtual ~A() {}
    
        public:
            int m_i;
            int m_j;
            char* m_charPtr;
        };
    
        class B :public A {
        public:
            B() {}
            ~B(){}
    
            B(const B& temp) :A(temp) {
                //A(temp) 不能将调用父类复制构造函数放在此处,会造成二义性,编译错误,编译器会解析成 A temp;
            }
    
            B& operator=(const B& temp) {
                A::operator=(temp);
                return *this;
            }       
        };
    }
    
    int main()
    {
        demo4::B b1;
        demo4::B b2 = b1;   //拷贝构造函数
        b2 = b1;            //拷贝赋值运算
    }
    

    拷贝构造函数和拷贝复制运算符中重复代码的处理

    不建议拷贝构造函数和拷贝复制运算符函数互相调用

    相关文章

      网友评论

          本文标题:C++ 编程技巧与规范(二)

          本文链接:https://www.haomeiwen.com/subject/agbkiktx.html