zoukankan      html  css  js  c++  java
  • c++浅拷贝和深拷贝---14

    原创博文,转载请标明出处--周学伟http://www.cnblogs.com/zxouxuewei/

    1.什么是拷贝构造函数:

    拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。

    2.调用拷贝构造函数的情形:

    (1)一个对象以值传递的方式传入函数体

    #include <iostream>
    #include <cstring>

    using namespace std;

    class test
    {
    public:
        test();
        test(const test& ctest);
        ~test();
    private:
        char Name;
    };

    test::test()
    {
        cout <<"Construction "<<endl;
    }

    test::test(const test& ctest)
    {
        cout <<"Copy Construction "<<endl;
    }

    test::~test()
    {
        cout <<"Destruction "<<endl;
    }

    void function(test param_test)
    {
        cout <<"into function"<<endl;
    }

    int main(int argc, char const *argv[])
    {
        test Atest;
        function(Atest);

        return 0;
    }

    运行结果


    (2)一个对象以值传递的方式从函数返回

    #include <iostream>
    #include <cstring>

    using namespace std;

    class test
    {
    public:
        test();
        test(const test& ctest);
        ~test();
    private:
        char Name;
    };

    test::test()
    {
        cout <<"Construction "<<endl;
    }

    test::test(const test& ctest)
    {
        cout <<"Copy Construction "<<endl;
    }

    test::~test()
    {
        cout <<"Destruction "<<endl;
    }

    test function(test param_test)
    {
        cout <<"into function"<<endl;
        return param_test;
    }

    int main(int argc, char const *argv[])
    {
        test Atest;
        function(Atest);

        return 0;
    }

    运行结果:


    (3)一个对象需要通过另外一个对象进行初始化。

    #include <iostream>
    #include <cstring>

    using namespace std;

    class test
    {
    public:
        test();
        test(const test& ctest);
        ~test();
    private:
        char Name;
    };

    test::test()
    {
        cout <<"Construction "<<endl;
    }

    test::test(const test& ctest)
    {
        cout <<"Copy Construction "<<endl;
    }

    test::~test()
    {
        cout <<"Destruction "<<endl;
    }


    int main(int argc, char const *argv[])
    {
        test Atest;
        test Btest = Atest;

        return 0;
    }

    运行结果:

     3.浅拷贝和深拷贝:

     在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

    ---》深拷贝:(查看在堆上分配的内存资源进行深拷贝)

    #include <iostream>
    #include <cstring>

    using namespace std;

    class test
    {
    public:
        test(char* name);
        test(const test& ctest);
        ~test();
        void Printf();
    private:
        char* PName;
    };

    test::test(char* name)//普通构造函数
    {
        if(name != NULL)
        {
            cout <<"Construction "<<name<<endl;
            int len = strlen(name)+1;
            PName = new char[len];

            memset(PName, 0, len);
            strcpy(PName,name);
        }
        else
        {
            PName = NULL;
        }
        cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
    }

    test::test(const test& ctest)//深拷贝构造函数
    {
        if(ctest.PName != NULL)
        {
            cout <<"Copy Construction "<<endl;
            int len = strlen(ctest.PName)+1;
            PName = new char[len];
            memset(PName, 0, len);
            strcpy(PName,ctest.PName);
        }
        else
        {
            PName = NULL;
        }
        cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
    }

    test::~test()
    {
        cout <<"Destruction "<<endl;
        if(PName != NULL)
        {
            Printf();
            delete []PName;
            PName = NULL;
        }
    }

    void test::Printf()
    {
        cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
    }


    int main(int argc, char const *argv[])
    {
        test Atest((char*)"zhouxuewei");

        test Btest = Atest;//复制对象的副本,此刻调用拷贝构造函数

        return 0;
    }

     运行结果:

     

     ----》浅拷贝(使用编译器默认的拷贝构造函数)

    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    class test
    {
    public:
        test(char* name);
        //test(const test& ctest);
        ~test();
        void Printf();
    private:
        char* PName;
    };
    
    test::test(char* name)//普通构造函数
    {
        if(name != NULL)
        {
            cout <<"Construction "<<name<<endl;
            int len = strlen(name)+1;
            PName = new char[len];
    
            memset(PName, 0, len);
            strcpy(PName,name);
        }
        else
        {
            PName = NULL;
        }
        cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
    }
    /*
    test::test(const test& ctest)//深拷贝构造函数
    {
        if(ctest.PName != NULL)
        {
            cout <<"Copy Construction "<<endl;
            int len = strlen(ctest.PName)+1;
            PName = new char[len];
            memset(PName, 0, len);
            strcpy(PName,ctest.PName);
        }
        else
        {
            PName = NULL;
        } cout <<"&PName = "<<static_cast<void*>(PName)<<endl; }
    */ test::~test() { cout <<"Destruction "<<endl; if(PName != NULL) { Printf(); delete []PName; PName = NULL; } } void test::Printf() { cout <<"&PName = "<<static_cast<void*>(PName)<<endl; } int main(int argc, char const *argv[]) { test Atest((char*)"zhouxuewei"); test Btest = Atest;//复制对象的副本,此刻调用拷贝构造函数 return 0; }

    运行结果:结果可以看出,拷贝的对象使用的堆内存地址相同,析构时free两次,产生错误:

    4.赋值运算符

    #include <iostream>
    #include <cstring>

    using namespace std;

    class test
    {
    public:
        test(char* name,int value);
        test(const test& ctest);
        ~test();
        test& operator=(const test& otest);
        void Printf();
    private:
        char* PName;
        int value;
        static unsigned char count;
    };

    unsigned char test::count = 0;

    test::test(char* name,int val)//普通构造函数
    {
        if(name != NULL)
        {
            int len = strlen(name)+1;
            PName = new char[len];

            memset(PName, 0, len);
            strcpy(PName,name);
        }
        else
        {
            PName = NULL;
        }
        value = val;
        cout <<"&PName = "<<static_cast<void*>(PName)<<", name = "<<PName<<", value = "<<value<<endl;
        count++;
    }

    test::test(const test& ctest)//深拷贝构造函数
    {
        if(ctest.PName != NULL)
        {
            cout <<"Copy Construction "<<endl;
            int len = strlen(ctest.PName)+1;
            PName = new char[len];
            memset(PName, 0, len);
            strcpy(PName,ctest.PName);
        }
        else
        {
            PName = NULL;
        }
        cout <<"&PName = "<<static_cast<void*>(PName)<<", name = "<<PName<<endl;
        count++;
    }

    test::~test()
    {
        cout <<"Destruction "<<", count = "<<static_cast<int>(count)<<endl;
        if(PName != NULL)
        {
            Printf();
            delete []PName;
            PName = NULL;
        }
        count--;
    }

    test& test::operator=(const test& otest)
    {
        cout <<"into operator "<<endl;
       if(&otest == this)  //自赋值检查
       {
           return *this;
       }
       if(PName != NULL)
       {
            delete []PName;  //原有资源的释放
            PName = NULL;
       }
       if(otest.PName != NULL)
       {
            int len = strlen(otest.PName)+1;//开始赋值
            PName = new char[len];
            memset(PName, 0, len);
            strcpy(PName, otest.PName);
       }
       else
            PName = NULL;
        value = otest.value;
        return *this;
    }

    void test::Printf()
    {
        cout <<"&PName = "<<static_cast<void*>(PName)<<",name = "<<PName<<", value = "<<value<<endl;
    }


    int main(int argc, char const *argv[])
    {
        test Atest((char*)"zhouxuewei",111);
        test Btest((char*)"wanghuixi",222);


        Atest = Btest;

        return 0;
    }

    运行结果:

    总结:

    1》何时需要定义拷贝构造函数:

      a.类数据成员有指针。

      b.类数据成员管理资源(如打开文件)

    2》如果一个类需要析构函数来释放资源,则他需要一个拷贝构造函数。

    3》如果想禁止一个类的拷贝构造,则需要将拷贝构造函数声明为private。

  • 相关阅读:
    sql 作业创建
    sqlserver的触发器练习实例
    js按钮频繁提交解决方案:
    C# 使用WebClient下载文件到本地目录
    放松读物,再看css禅意花园-总结HTML-又收到两个标记
    放松读物,再看css禅意花园-总结HTML
    放松读物,再看css禅意花园-HTML是CSS设计的基础
    线段的垂直平分线12
    放松读物,再看css禅意花园-第一章学到的教训-文本的可缩放
    放松读物,再看css禅意花园-第一章继续学到的教训
  • 原文地址:https://www.cnblogs.com/zxouxuewei/p/6580635.html
Copyright © 2011-2022 走看看