精华内容
下载资源
问答
  • 对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=88;...下面看一个类对象拷贝的简单例子。 #include using namespace std; class CExample { private: int a; public: CExample(in
    对于普通类型的对象来说,它们之间的复制是很简单的,例如:
    int a=88;
    int b=a; 

    而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。下面看一个类对象拷贝的简单例子。

    #include <iostream>
    using namespace std;
    class CExample {
    private:
         int a;
    public:
         CExample(int b)
         { a=b;}
         void Show ()
         {
            cout<<a<<endl;
        }
    };
    int main()
    {
         CExample A(100);
         CExample B=A;
         B.Show ();
         return 0;
    } 
    运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象B分配了内存并完成了与对象A的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。下面举例说明拷贝构造函数的工作过程。

    #include <iostream>
    using namespace std;
    class CExample {
    private:
        int a;
    public:
        CExample(int b)
        { a=b;}
    
        CExample(const CExample& C)
        {
            a=C.a;
        }
        void Show ()
        {
            cout<<a<<endl;
        }
    };
    int main()
    {
        CExample A(100);
        CExample B=A;
        B.Show ();
        return 0;
    }  
    CExample(const CExample& C)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。
    例如:类X的拷贝构造函数的形式为X(X& x)。

    当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
    一个对象以值传递的方式传入函数体 
    一个对象以值传递的方式从函数返回 
    一个对象需要通过另外一个对象进行初始化。

    如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝,后面将进行说明。

    自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。

    浅拷贝和深拷贝
    在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

    深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。

    #include <iostream>
    using namespace std;
    class CA
    {
     public:
      CA(int b,char* cstr)
      {
       a=b;
       str=new char[b];
       strcpy(str,cstr);
      }
      CA(const CA& C)
      {
       a=C.a;
       str=new char[a]; //深拷贝
       if(str!=0)
        strcpy(str,C.str);
      }
      void Show()
      {
       cout<<str<<endl;
      }
      ~CA()
      {
       delete str;
      }
     private:
      int a;
      char *str;
    };
    int main()
    {
     CA A(10,"Hello!");
     CA B=A;
     B.Show();
     return 0;
    } 
    深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。

    浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。

    Test(Test &c_t)是自定义的拷贝构造函数,拷贝构造函数的名称必须与类名称一致,函数的形式参数是本类型的一个引用变量,且必须是引用。

    当用一个已经初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用,如果你没有自定义拷贝构造函数的时候,系统将会提供给一个默认的拷贝构造函数来完成这个过程,上面代码的复制核心语句就是通过Test(Test &c_t)拷贝构造函数内的p1=c_t.p1;语句完成的。




     
    展开全文
  • 下面,通过代码来说说C++中的深浅拷贝 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class Name { public: Name(const char*myp){ m_len = strlen(myp); mp...

     

    下面,通过代码来说说C++中的深浅拷贝

    #define   _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    using namespace std;
    class Name
    {
    public:
        Name(const char*myp){
            m_len = strlen(myp);
            mp = (char*)malloc(m_len + 1);
            strcpy(mp, myp);
        }
        ~Name(){
            if (mp != NULL)
            {
                free(mp);
                mp = NULL;
                m_len = 0;
            }
        }
    private:
        char *mp;
        int m_len;
    };
    
    void playmain(){
        Name obj1("abcdef");
        Name obj2 = obj1;  // 执行默认拷贝构造函数
    }
    int main()
    {
        playmain();
        cout <<"hello world"<<endl;
        system("pause");
        return 0;
    }

    我们通过断点,一步一步地调试程序:

    继续往下走:

    继续往下走:

    由于我们并没有重写自己的拷贝构造函数,因此执行的是默认的拷贝构造函数。当Name obj2=obj1执行完毕后,也就是函数playmain()执行完毕了,开始调用对象的析构函数:

    首先析构的是obj2,当其析构完毕后,程序返回:

    我们接着往下走:

    继续F11往下走,我们会发现程序崩溃了:

    下面,我们来分析一下,为什么程序会崩溃在这里?原因很简单,因为我们重写自己的拷贝构造函数,而使用了默认的拷贝构造函数,也就是C++编译器为我们进行了一次浅拷贝。那么何为浅拷贝呢?下面来看张图:

    也就是说,当我们第一次析构对象obj2的时候,已经将内存空间0x1111释放了,而obj1和obj2都指向了同一块内存空间,当obj1执行析构函数的时候,它所指向的内存空间已经被释放,再次进程释放,肯定程序会崩溃。到此 ,我们清楚知道,导致程序崩溃的原因是C++编译器仅仅执行了浅拷贝,而浅拷贝的根源在于我们没有重写自己的拷贝构造函数,所以解决办法,当然是重写自己的拷贝构造函数,从而实现深拷贝-------将对象完完全全的赋值一份(包括指针指向的内存空间也复制一份)

    再次执行程序,不会出现崩溃现象。上述对应的内存四区模型如下:

     同理,如下代码中也会出现程序崩溃,也需要我们显式重载"="运算符

    没有重载=运算符内存四区模型如下:

    初始条件:


    执行等号操作后:

    解决方案:

    #define  _CRT_SECURE_NO_WARNINGS 
    #include <iostream>
    using namespace std;
    
    class  Name
    {
    public:
        Name(const char *myp)
        {
            m_len = strlen(myp);
            m_p =(char *) malloc(m_len + 1); 
            strcpy(m_p, myp);
        }
    
        //解决方案: 手工的编写拷贝构造函数 使用深copy
        Name(const Name& obj1)
        {
            m_len = obj1.m_len;
            m_p = (char *)malloc(m_len + 1);
            strcpy(m_p, obj1.m_p);
        }
        //obj3 = obj1;  // C++编译器提供的 等号操作 也属 浅拷贝
        //obj3.operator=(obj1)
        Name& operator=(Name &obj1) // 重载等号运算符
        {
            //先释放旧的内存
            if (this->m_p != NULL)
            {
                delete[] m_p;
                m_len = 0;
            }
            //2 根据obj1分配内存大小
            this->m_len = obj1.m_len;
            this->m_p = new char [m_len+1];
            
            //把obj1赋值
            strcpy(m_p, obj1.m_p);
            return *this;
        }
        ~Name()
        {
            if (m_p != NULL)
            {
                free(m_p);
                m_p = NULL;
                m_len = 0;
            }
        }
    protected:
    private:
        char *m_p ;
        int m_len; 
    };
    
    //对象析构的时候 出现coredump
    void objplaymain()
    {
        Name obj1("abcdefg");
        Name obj2 = obj1;  //C++编译器提供的 默认的copy构造函数  浅拷贝
        Name obj3("obj3");
    
        obj3 = obj1;  // C++编译器提供的 等号操作 也属 浅拷贝
        //obj3.operator=(obj1)
        //operato=(Name &obj1)
        obj1 = obj2 = obj3;
        //obj2.operator=(obj3);
        //obj1 = void;
    }
    
    void main()
    {
        objplaymain();
        cout<<"hello..."<<endl;
        system("pause");
        return ;
    }

     

    转载于:https://www.cnblogs.com/vipchenwei/p/7294416.html

    展开全文
  • 指针赋值(深拷贝 浅拷贝)

    千次阅读 2019-06-12 13:56:45
    当我申请了一个char型指针,想给另一个指针用等于号赋值时,这两个指针将指向同一块内存,如果我把第一个指针释放,那么另一个指针指向的数据也没有了。 for (int i = 0; i < 3; i++) { Node *n = new ...

     

    当我申请了一个char型指针,想给另一个指针用等于号赋值时,这两个指针将指向同一块内存,如果我把第一个指针释放,那么另一个指针指向的数据也没有了。

     

    for (int i = 0; i < 3; i++) 
    	{
    		Node *n = new Node(i+'0',i);
    		char *c = (char*)malloc(sizeof(Node));
    		memcpy(c,n,sizeof(Node));
    		array[i] = c;
    		//array[i] = (char*)malloc(sizeof(Node));
    		//memcpy(array[i], c, sizeof(Node));
    		Node *temp = (Node*)((char*)array[i]);
    		//std::cout << temp->x << " " << temp->y << std::endl;
    		//std::cout<<n->x<<" "<<n->y<<std::endl;
    		length++;
    		free(c);
    	}
    ?-572662307
    ?-572662307
    ?-572662307
    Hello World!

    所以我们可以对另一个指针重新申请内存,然后用memcpy拷贝,那么第一个指针被释放,也完全没有影响。

    array[i] = (char*)malloc(sizeof(Node));
    memcpy(array[i], c, sizeof(Node));

     

    0 0
    1 1
    2 2
    Hello World!

     

    ​
    // 指针数组.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include "pch.h"
    #include <iostream>
    #include <cstring>
    const int max = 100;
    typedef struct Node 
    {
    	char x;
    	int y;
    	Node(int a, int b) :x(a),y(b){}
    }Node;
    
    void fun(char **array,int length)
    {
    	for (int i = 0; i < 3; i++)
    	{
    		Node *n = new Node(i + '0', i);
    		char *c = (char*)malloc(sizeof(Node));
    		memcpy(c, n, sizeof(Node));
    		array[i] = c;
    		Node *temp = (Node*)((char*)array[i]);
    		//std::cout << temp->x << " " << temp->y << std::endl;
    		//std::cout<<n->x<<" "<<n->y<<std::endl;
    		length++;
    		//free(c);
    	}
    }
    
    
    int main()
    {
    	char **array;
    	int length = 0;
    	array = new char*[max];
    	
    	for (int i = 0; i < 3; i++) 
    	{
    		Node *n = new Node(i+'0',i);
    		char *c = (char*)malloc(sizeof(Node));
    		memcpy(c,n,sizeof(Node));
    		array[i] = (char*)malloc(sizeof(Node));
    		memcpy(array[i], c, sizeof(Node));
    		Node *temp = (Node*)((char*)array[i]);
    		//std::cout << temp->x << " " << temp->y << std::endl;
    		//std::cout<<n->x<<" "<<n->y<<std::endl;
    		length++;
    		free(c);
    	}
    	//fun(array,length);
    	for (int i = 0; i < length; i++) 
    	{
    		Node *temp = (Node*)((char*)array[i]);
    		std::cout << temp->x << " " << temp->y << std::endl;
    	}
        std::cout << "Hello World!\n"; 
    }
    
    // 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
    // 调试程序: F5 或调试 >“开始调试”菜单
    
    // 入门提示: 
    //   1. 使用解决方案资源管理器窗口添加/管理文件
    //   2. 使用团队资源管理器窗口连接到源代码管理
    //   3. 使用输出窗口查看生成输出和其他消息
    //   4. 使用错误列表窗口查看错误
    //   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
    //   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
    
    ​

     

    展开全文
  • 深拷贝的优点:每一个的对象(哪怕是通过拷贝构造函数实例化的对象)的指针都有指向的内存空间,而不是共享,所以在对象析构的时候就不存在重复释放或内存泄露的问题了。 深拷贝的缺点:内存开销大。 浅拷贝优点:...

    深拷贝和浅拷贝

    对内存资源使用的讨论,必然要先从深拷贝浅拷贝说起。
    深拷贝的优点:每一个的对象(哪怕是通过拷贝构造函数实例化的对象)的指针都有指向的内存空间,而不是共享,所以在对象析构的时候就不存在重复释放或内存泄露的问题了。
    深拷贝的缺点:内存开销大。
    浅拷贝优点:通过拷贝构造函数实例化的对象的指针数据变量指向的共享的内存空间,因此内存开销较小。
    浅拷贝缺点:对象析构的时候就可能会重复释放或造成内存泄露。

    鉴于深拷贝和浅拷贝的优缺点,可采用引用计数技术,既减小了内存开销,又避免了堆的重复释放或内存泄露问题。
    浅拷贝实例:

    #include <iostream>
    
    using std::string;
    using std::cout;
    using std::endl;
    
    class Student
    {
    public:
        Student(string* name)
        {
            m_name = name;
        }
    
        Student(Student& obj)
        {
            m_name = obj.m_name;
        }
    
        ~Student()
        {
            if (m_name != nullptr)
            {
                delete m_name;
                m_name = nullptr;
            }
        }
    
        Student& operator=(Student& obj)
        {
            m_name = obj.m_name;
            return *this;
        }
    
        void Show()
        {
            cout << "name: " << *m_name << endl;
        }
    
    private:
        string* m_name;
    };
    
    void test01()
    {
        Student stu1 = new string("zhang san");
        Student stu2 = new string("li si");
        Student stu3 = stu2;
    
        stu1.Show();                //name: zhang san
        stu2.Show();                //name: li si
        stu3.Show();                //name: li si
    }
    
    int main(int argc, char const *argv[])
    {
        test01();                   //stu2和stu3析构的时候会重复释放
    
        return 0;
    }
    

    浅拷贝虽然节省了内存开销,但是有一个致命的问题:一旦其中一个对象释放了资源,那么所有的其他对象的资源也被释放了。
    解决办法:增加一个变量,记录资源使用次数。

    引用计数

    我们把刚才的实例改写下:

    #include <iostream>
    
    using std::string;
    using std::cout;
    using std::endl;
    
    class Student
    {
    public:
        Student(string* name)
        {
            m_name = name;
            m_count = new int(1);
        }
    
        Student(Student& obj)
        {
            m_name = obj.m_name;
            m_count = obj.m_count;
            ++(*m_count);
        }
    
        ~Student()
        {
            if (--(*m_count) == 0)
            {
                //最后一个使用的对象
                delete m_name;
                m_name = nullptr;
                delete m_count;
                m_count = nullptr;
            }
        }
    
        Student& operator=(Student& obj)
        {
            if (obj.m_name == m_name)
            {
                return *this;
            }
    
            if (--(*m_count) == 0)
            {
                delete m_name;
                m_name = nullptr;
                delete m_count;
                m_count = nullptr;
            }
    
            m_name = obj.m_name;
            m_count = obj.m_count;
            ++(*m_count);
            return *this;
        }
    
        void Show()
        {
            cout << "name: " << *m_name << endl;
            cout << "count: " << *m_count << endl;
        }
    
    private:
        string* m_name;
        int* m_count;
    };
    
    void test01()
    {
        Student stu1 = new string("zhang san");
        stu1.Show();                    //name: zhang san   count: 1
        Student stu2 = new string("li si");  
        stu2.Show();                    //name: li si       count: 1
        Student stu3 = stu2;
        stu3.Show();                    //name: li si       count: 2
    }
    
    int main(int argc, char const *argv[])
    {
        //引用计数方式不会重复释放,用户使用的时候不需要关心和手动释放
        test01();                       
    
        return 0;
    }
    

    我们将该引用计数做一个简易的封装,把引用计数作为一个新的类来使用。

    #include <iostream>
    
    using std::string;
    using std::cout;
    using std::endl;
    
    class RefValue
    {
    public:
        RefValue(string* name)
        {
            m_name = name;
            m_count = 1;
        }
    
        void Release()
        {
            if (--m_count == 0)
            {
                delete this;
            }
        }
    
        ~RefValue()
        {
            if (m_name != nullptr)
            {
                delete m_name;
                m_name = nullptr;
            }
        }
    
        void AddRef()
        {
            ++m_count;
        }
    
        void Show()
        {
            cout << "name: " << *m_name << endl;
            cout << "count: " << m_count << endl;
        }
    
    private:
        string* m_name;
        int m_count;
    };
    
    class Student
    {
    public:
        Student(string* name)
        {
            m_pValue = new RefValue(name);
        }
    
        Student(Student& obj)
        {
            m_pValue = obj.m_pValue;
            m_pValue->AddRef();
        }
    
        void Release()
        {
            m_pValue->Release();
        }
    
        ~Student()
        {
            Release();
        }
    
        Student& operator=(Student& obj)
        {
            if (obj.m_pValue == m_pValue)
            {
                return *this;
            }
    
            m_pValue->Release();
            m_pValue = obj.m_pValue;
            m_pValue->AddRef();
            return *this;
        }
    
        void Show()
        {
            m_pValue->Show();
        }
        
    private:
        RefValue* m_pValue;
    };
    
    void test01()
    {
        Student stu1 = new string("zhang san");
        stu1.Show();                    //name: zhang san   count: 1
        Student stu2 = new string("li si");  
        stu2.Show();                    //name: li si       count: 1
        Student stu3 = stu2;
        stu3.Show();                    //name: li si       count: 2
    }
    
    int main(int argc, char const *argv[])
    {
        test01();
    
        return 0;
    }
    

    上面的做法能在一定程度上解决资源多次重复申请的浪费,但是仍然存在两个核心的问题。
    1.如果对其中某一个类对象中的资源进行了修改,那么所有引用该资源的对象全部会被修改,这显然是错误的。
    2.当前的计数器作用于Student类,在使用时候,需要强行加上引用计数类,这样复用性不好,使用不方便。

    写时拷贝

    为了解决上述问题1,我们引入写时拷贝的思想。在使用引用计数时,当发生共享资源值改变的时候,需要对其资源进行重新的拷贝,这样改变的时拷贝的值,而不影响原有的对象中的共享资源。

    #include <iostream>
    
    using std::string;
    using std::cout;
    using std::endl;
    
    class RefValue
    {
    public:
        RefValue(string* name)
        {
            m_name = name;
            m_count = 1;
        }
    
        void Release()
        {
            if (--m_count == 0)
            {
                delete this;
            }
        }
    
        ~RefValue()
        {
            if (m_name != nullptr)
            {
                delete m_name;
                m_name = nullptr;
            }
        }
    
        void AddRef()
        {
            ++m_count;
        }
    
        void Show()
        {
            cout << "name: " << *m_name << endl;
            cout << "count: " << m_count << endl;
        }
    
    private:
        string* m_name;
        int m_count;
    };
    
    class Student
    {
    public:
        Student(string* name)
        {
            m_pValue = new RefValue(name);
        }
    
        Student(Student& obj)
        {
            m_pValue = obj.m_pValue;
            m_pValue->AddRef();
        }
    
        ~Student()
        {
            m_pValue->Release();
        }
    
        void SetName(string* name)
        {
            m_pValue->Release();
            m_pValue = new RefValue(name);
        }
    
        Student& operator=(Student& obj)
        {
            if (obj.m_pValue == m_pValue)
            {
                return *this;
            }
    
            m_pValue->Release();
            m_pValue = obj.m_pValue;
            m_pValue->AddRef();
            return *this;
        }
    
        void Show()
        {
            m_pValue->Show();
        }
        
    private:
        RefValue* m_pValue;
    };
    
    void test01()
    {
        Student stu1 = new string("zhang san");
        stu1.Show();                                    //name: zhang san   count: 1
        Student stu2 = new string("li si");  
        stu2.Show();                                    //name: li si       count: 1
        Student stu3 = stu2;
        stu3.Show();                                    //name: li si       count: 2
        stu2.SetName(new string("li si2"));
        stu2.Show();                                    //name: li si2      count: 1
        stu3.Show();                                    //name: li si       count: 1
    }
    
    int main(int argc, char const *argv[])
    {
        test01();
    
        return 0;
    }   
    

    简易版智能指针

    我们可以利用模板技术来解决上述第二个问题,使我们自己定义的智能指针更加通用。

    #include <iostream>
    
    using std::string;
    using std::cout;
    using std::endl;
    
    template <class T>
    class RefValue
    {
    public:
        RefValue(T* name)
        {
            m_name = name;
            m_count = 1;
        }
    
        void Release()
        {
            if (--m_count == 0)
            {
                delete this;
            }
        }
    
        ~RefValue()
        {
            if (m_name != nullptr)
            {
                delete m_name;
                m_name = nullptr;
            }
        }
    
        void AddRef()
        {
            ++m_count;
        }
    
        T* m_name;
    
    private:
        int m_count;
    };
    
    template <class T>
    class SmartPtr
    {
    public:
        SmartPtr()
        {
            m_pValue = nullptr;
        }
    
        SmartPtr(T* name)
        {
            m_pValue = new RefValue<T>(name);
        }
    
        SmartPtr(SmartPtr& obj)
        {
            m_pValue = obj.m_pValue;
            m_pValue->AddRef();
        }
    
        ~SmartPtr()
        {
            m_pValue->Release();
        }
    
        SmartPtr& operator=(SmartPtr& obj)
        {
            if (obj.m_pValue == m_pValue)
            {
                return *this;
            }
    
            if (m_pValue != nullptr)
            {
                m_pValue->Release();
            }
            m_pValue = obj.m_pValue;
            m_pValue->AddRef();
            return *this;
        }
    
        T* operator->()
        {
            return m_pValue->m_name;
        }
        
    private:
        RefValue<T>* m_pValue;
    };
    
    class Student
    {
    public:
        Student() = default;
        Student(string name, int age) : m_name(name), m_age(age)
        {}
    
        Student(Student& stu)
        {
            m_name = stu.m_name;
            m_age = stu.m_age;
        }
    
        void Show()
        {
            cout << "name: " << m_name << "\tage:" << m_age << endl;
        }
    
        ~Student()
        {}
    
    private:
        string m_name;
        int m_age;
    };
    
    
    void test01()
    {
        SmartPtr<Student> p1 = new Student("zhang san", 20);
        p1->Show();                                             //name: zhang san	age:20
        SmartPtr<Student> p2 = new Student("li si", 18);
        p2->Show();                                             //name: li si	age:18
        SmartPtr<Student> p3 = p2;
        p3->Show();                                             //name: li si	age:18
       
        {
            SmartPtr<Student> p4;
            p4 = p1;
            p4 = p2;
        }
        p1->Show();                                             //name: zhang san	age:20
        p2->Show();                                            //name: li si	age:18
        p3->Show();                                            //name: li si	age:18
    }
    
    int main(int argc, char const *argv[])
    {
        test01();
    
        return 0;
    }
    

    至此,我们已经实现了一个简易版的智能指针。通过以上的学习对C++中std::shared_ptr的实现和使用有了一定的认知,但是增加了引用计数后就没有别的问题了吗?

    循环引用

    我们看下面这段代码:

    #include <iostream>
    #include <memory>
    
    using std::cout;
    using std::endl;
    
    class Son;
    class Parent;
    
    class Son
    {
    public:
        Son()
        {}
    
        ~Son()
        {}
    
        void Set(std::shared_ptr<Parent> parent)
        {
            m_pParent = parent;
        }
    
        std::shared_ptr<Parent> m_pParent;
    };
    
    class Parent
    {
    public:
        Parent()
        {}
    
        ~Parent()
        {}
    
        void Set(std::shared_ptr<Son> pSon)
        {
            m_pSon = pSon;
        }
    
        std::shared_ptr<Son> m_pSon;
    };
    
    void testShared()
    {
        Son *pSon = new Son();
        Parent *pParent = new Parent();
        {
            std::shared_ptr<Parent> shared_Parent(pParent);
            std::shared_ptr<Son> shared_Son(pSon);
    
            shared_Parent->Set(shared_Son);
            shared_Son->Set(shared_Parent);
    
            cout << "shared_Parent.use_count: " << shared_Parent.use_count() << endl;           //shared_Parent.use_count: 2
            cout << "shared_Son.use_count: " << shared_Son.use_count() << endl;                 //shared_Son.use_count: 2
        }
        //通过gdb调试,在这个位置可以看到shared_Parent.use_count和shared_Son.use_count均为1,两个对象均未被销毁
        /*最后两者的引用计数均为1,原因是出了块作用域之后,两个shared_parent和shared_son均会析构,
        在这两个智能指针的内部,均会先去判断对应的内部指针是否-1是否为0,显然这里相互引用的情况下,
        引用计数初值为2,减1后值为1,所以两个指针均不会被释放。这里,其实只需要一个释放了,
        另外一个也能跟着释放。*/
    }
    
    int main(int argc, char const *argv[])
    {
    
        testShared();
    
        return 0;
    }
    
    

    放上我用gdb调试的过程

    [root@root-centos8 c++11,14,17]$ gdb ./20_循环引用 
    GNU gdb (GDB) Red Hat Enterprise Linux 8.2-6.el8
    Copyright (C) 2018 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "x86_64-redhat-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from ./20_循环引用...done.
    (gdb) start
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Temporary breakpoint 2 at 0x400dde: file 20_循环引用.cpp, line 62.
    Starting program: /home/wyl/work/c++11,14,17/20_循环引用 
    warning: Loadable section ".note.gnu.property" outside of ELF segments
    warning: Loadable section ".note.gnu.property" outside of ELF segments
    
    Temporary breakpoint 2, main (argc=1, argv=0x7fffffffe0a8)
        at 20_循环引用.cpp:62
    62	    testShared();
    (gdb) s
    testShared () at 20_循环引用.cpp:46
    46	    Son *pSon = new Son();
    (gdb) n
    47	    Parent *pParent = new Parent();
    (gdb) 
    49	        std::shared_ptr<Parent> shared_Parent(pParent);
    (gdb) 
    50	        std::shared_ptr<Son> shared_Son(pSon);
    (gdb) 
    52	        shared_Parent->Set(shared_Son);
    (gdb) 
    53	        shared_Son->Set(shared_Parent);
    (gdb) 
    55	        cout << "shared_Parent.use_count: " << shared_Parent.use_count() << endl;           //shared_Parent.use_count: 2
    (gdb) whatis shared_Parent
    type = std::shared_ptr<Parent>
    (gdb) whatis shared_Son
    type = std::shared_ptr<Son>
    (gdb) p shared_Parent
    $5 = std::shared_ptr<Parent> (use count 2, weak count 0) = {get() = 0x615e90}
    (gdb) p shared_Son
    $6 = std::shared_ptr<Son> (use count 2, weak count 0) = {get() = 0x615e70}
    (gdb) n
    shared_Parent.use_count: 2
    56	        cout << "shared_Son.use_count: " << shared_Son.use_count() << endl;                 //shared_Son.use_count: 2
    (gdb) 
    shared_Son.use_count: 2
    50	        std::shared_ptr<Son> shared_Son(pSon);
    (gdb) 
    49	        std::shared_ptr<Parent> shared_Parent(pParent);
    (gdb) 
    58	}
    (gdb) p *(Parent*)0x615e90
    $7 = {m_pSon = std::shared_ptr<Son> (use count 1, weak count 0) = {
        get() = 0x615e70}}
    (gdb) p *(Son*)0x615e70
    $8 = {m_pParent = std::shared_ptr<Parent> (use count 1, weak count 0) = {
        get() = 0x615e90}}
    

    可以看到,代码最后有内存泄漏的问题。
    相互引用
    实际上,如上图所示,a,b 内部的pointer 同时又引用了a,b,这使得a,b 的引用计数均变为了2,而离开作用域时,a,b 智能指针被析构,却只能造成这块区域的引用计数减一,这样就导致了a,b 对象指向的内存区域引用计数不为零,而外部已经没有办法找到这块区域了,也就造成了内存泄露。
    在这里插入图片描述
    解决办法也很简单,就是采用弱引用。弱引用不会引起引用计数增加,当换用弱引用时候,最终的释放流程如上图。

    更多C++新特性

    展开全文
  • 先考虑一种情况,对一个已知对象进行拷贝,编译系统会自动调用一种构造函数——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。 #include &lt;iostream&gt; #include "...
  • 深拷贝 //strcpy(name,o1.name); } ~obj(){ free(name); } private: char *name; }; int main() { obj o1("hello"); obj o2 = o1; return 0; } 浅拷贝错误原因: 由于先构造的后析构,后构造的先析构 o1 o2 name都...
  • C++深拷贝与浅拷贝

    2019-03-16 09:06:43
    C++深拷贝与浅拷贝 C++在涉及到类的拷贝时,有深拷贝和浅拷贝两种情况,默认情况下的拷贝构造函数是浅拷贝。 1.深拷贝与浅拷贝区别: 当类的数据成员中没有涉及到指针时,可以使用默认情况下的浅拷贝构造函数。 当类...
  • 1.深拷贝与浅拷贝如果一个类包含指针形式的成员变量,系统提供的缺省拷贝构造函数,只是复制了指针成员变量本身,而没有复制该变量所指向的内容,这种拷贝方式被称为浅拷贝。浅拷贝将导致不同对象间的数据共享,同时...
  • C++深拷贝浅拷贝

    2017-02-17 20:38:07
    拷贝构造函数分为深拷贝和浅拷贝,浅拷贝是将对象的成员依次进行赋值操作,而深拷贝则是将对象中若是存在指针成员,将指针成员所指向的成员依次进行赋值操作。 Array.h class Array { public: Array(int count); ...
  • 深拷贝与浅拷贝

    2019-09-29 09:44:48
    深拷贝) 当类中包含指向动态存储空间指针类型的数据成员,并且通过该指针在构造函数中动态申请了空间。 例子:验证浅拷贝的不足 Student.h #ifndef STUDENT_H #define STUDENT_H class ...
  • C++ 浅拷贝 & 深拷贝

    千次阅读 多人点赞 2021-05-07 02:19:39
    C++ 对象的赋值, 赋值, 介绍浅拷贝 (shallow copy) 和深拷贝 (deep copy) 的区别.
  • C++:深拷贝与浅拷贝

    2020-02-29 22:23:51
    3-1 C++深拷贝浅拷贝 浅拷贝例子: 浅拷贝例子plus: 上图浅拷贝存在的问题:arr2的指针和arr1的指针指向同一个内存,销毁的时候同一段内存被销毁两次; 解决方法:深拷贝(不是简单的数值拷贝,而把堆中内存的...
  • 浅拷贝与深拷贝

    2018-12-06 20:26:12
    深拷贝就是拷贝该指针,并申请一块新内存,使得新指针指向该新内存 采用深拷贝时不会出现在浅拷贝时重复释放指针造成内存泄漏的情况!!! 简单说明浅拷贝与深拷贝 如果用户未定义拷贝构造函数,则编译器会默认...
  • c++浅拷贝和深拷贝

    2017-01-01 11:46:57
    浅拷贝 浅拷贝的问题 深拷贝
  • 这个默认的拷贝构造函数采用的是“位拷贝”(浅拷贝),而非“值拷贝”(深拷贝)的方式,如果类中含有指针变量,默认的拷贝构造函数必定出错。 用一句简单的话来说就是浅拷贝,只是对指针的拷贝,拷贝后两个
  • 浅拷贝和深拷贝

    2016-10-22 12:52:39
    一、浅拷贝和深拷贝  所谓浅拷贝,就是由默认的拷贝构造函数所实现的对数据成员逐一赋值。若类中含有指针类型的数据,这种方式只是简单的把指针的指向赋值给新成员,但并没有给新成员分配内存,因此这种方式必然会...
  • C++ 浅拷贝与深拷贝

    2021-02-07 00:35:15
    一、浅拷贝和深拷贝是什么? 什么是浅拷贝?只有普通变量初始化的拷贝构造函数就浅拷贝。咋算普通变量?如int,char, string…不涉及指针变量。可以直接使用默认拷贝构造函数。 什么是深拷贝?不用默认拷贝构造函数,...
  • 1 简单理解 深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的...**深拷贝(deepCopy)**是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存, 使用深拷贝的情况下,释放内存的时候不会
  • C++学习笔记:拷贝构造函数、深拷贝与浅拷贝1.拷贝构造函数的引入1.1 用对象来初始化对象1.2 为什么可以1.3 什么是拷贝构造函数2.浅拷贝与深拷贝2.1 浅拷贝的缺陷2.2 如何解决这个问题 1.拷贝构造函数的引入 1.1 用...
  • 深拷贝与浅拷贝的再次理解  记得11年底找工作的时候,面试时曾经遇到有面试官问的对深拷贝与浅拷贝的理解,那时候自己回来查了资料,写了篇博客,感觉自己理解了,其实理解的不深刻,最近在调试bug的时候...
  • 浅拷贝深拷贝

    2016-10-26 20:42:54
    深拷贝与浅拷贝的再次理解  记得11年底找工作的时候,面试时曾经遇到有面试官问的对深拷贝与浅拷贝的理解,那时候自己回来查了资料,写了篇博客,感觉自己理解了,其实理解的不深刻,最近在调试bug的时候,再次...
  • 带你了解Cpp深浅拷贝

    2020-03-26 17:00:15
    拷贝也有新花样
  • C++深拷贝和浅拷贝

    2017-06-10 23:53:22
    复制和浅复制详解。拷贝构造函数使用。
  • C++ 浅拷贝和深拷贝

    2019-10-13 22:44:00
    对一个已知对象进行拷贝,编译系统会自动调用一种构造函数——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。 //student.h #ifndef STUDENT_H #define STUDENT_H class Student { ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,266
精华内容 3,706
关键字:

cpp指针深拷贝