Shallow Copy vs. Deep Copy

Hey guys,

Currently I'm trying to create a shallow copy and a deep copy. Currently, I have two deep copies I believe. I can't understand why I have two deep copies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  #include <cstdlib>
#include <iostream>
using namespace std;

class WrapArrayDeep{
private:
    char *pca;
public:
    WrapArrayDeep(){
        pca = new char[5];
        *pca = 97;
    }
    
    WrapArrayDeep(const WrapArrayDeep &wad){
        cout << "This is a copy constructor for deep. \n";
        this->pca = new char(*wad.pca);
    }
    
    void load(){
        for(int i = 0; i < 5; i++){
            pca[i] = *pca + i;
        }    
    }
    void change(){
        pca = new char[5];
    }
    
    void display(){
        for(int i = 0; i < 5; i++){
              cout << pca[i] << "\t";
        }
         cout<<endl;
    }
    ~WrapArrayDeep(){
        cout << "WrapArrayDeep Deconstructor" << endl;
    }
};

class WrapArrayShallow{
private:
    char *pca;
public:
    WrapArrayShallow(){
        pca = new char[5];
       *pca = 97;
    }
    
    WrapArrayShallow(WrapArrayShallow &was){
        cout << "This is a copy constructor for shallow. \n";
        this->pca = (was.pca);
    }
    
    void load(){
        pca[0] = 'a';
        pca[1] = 'b';
        pca[2] = 'c';
        pca[3] = 'd';
        pca[4] = 'e';
    }
    
    void display(){
        for(int i = 0; i < 5; i++){
            cout << *(pca + i) << "\t";
        }
        cout << endl;
    }
    
    void change(){
        pca = new char[5];
    }
    ~WrapArrayShallow(){
        cout << "Calling Deconstructor" << endl;
    }
};

int main(int argc, char** argv) {
    WrapArrayDeep wad1;
    
    cout << "WrapArrayDeep 1" << endl;
    wad1.load();
    wad1.display();
    
    WrapArrayDeep wad2(wad1);
    wad2.load();
    wad2.display();
    
    cout << "Changing WrapArrayDeep 1" << endl;
    wad1.change();
    wad1.display();
    cout <<"Displaying WrapArrayDeep 2" << endl;
    wad2.display();
    
    cout << "Lets do the same with Shallow copy." << endl;
    
    WrapArrayShallow was1;
    
    cout << "WrapArrayShallow 1" << endl;
    was1.load();
    was1.display();
    
    WrapArrayShallow was2(was1);
    was2.load();
    was2.display();
    
    was1.change();
    cout << "Changed Shallow" << endl;
    was1.display();
    cout << "WrapArrayShallow2" << endl;
    was2.display();
    
    system("PAUSE");
    return 0;
}


Is it because of the way I'm storing into the array in the class WrapperArrayShallow? I just need some guidance on how to create a shallow copy.

I understand that a pointer has to be pointing to the same array. Once you change that array, it changes both values.

EDIT: Okay I'm extremely confused. I changed my change function to separate places in the array and it works just fine.
Last edited on
> I just need some guidance on how to create a shallow copy.

In this example, with a shallow copy, only the pointer is copied; after which both the copy and the original point to the same object. With a deep copy, the pointer itself is not copied; copy of the object pointed to is made and the pointer (which is not a copy of the original pointer) is made to point to the copy.

Here, to implement a shallow copy, we do not need to do anything: the implicitly declared copy constructor (and the implicitly declared assignment operator) perform shallow copy (and shallow assignment).

Note that in a class like this: struct toy_string { char* pstr ...., there is an embarrassingly simple distinction between the physical state of an object and its logical state. The value of the pointer pstr represents the physical state of the object, and the contents of the pointed to sequence of characters represent the logical state of the string. Two strings compare equal if they have equivalent logical state, even if their physical states may be different. The shallow copy copies the physical state (copies the pointer), the deep copy copies the logical state
(the sequence of characters that are pointed to).

Even though many palookas fail to understand this extremely simple concept, it is absolutely fundamental to C++. In this example, note that the shallow copy violates the important notion of immutability (or const-ness) of a C++ object: when we make a shallow copy of a const object, and later modify that copy, the original const object is modified. This is because the shallow copy ignores logical state, and instead believes that the physical state also represents the logical state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <cstring>
#include <iomanip>

namespace shallow {

    struct toy_string {

        static constexpr std::size_t N = 5 ;
        char* pstr = new char[N] { 'a', 'b', 'c', 'd', '\0' } ;

        void change( const char* cstr ) noexcept { std::strncpy( pstr, cstr, N-1 ) ; }

        void dump( const char* name = "" ) const {

            std::cout << "toy_string (shallow) " << name << " @ " << this << " pstr: "
                      << (const void*) pstr << " => " << std::quoted(pstr) << '\n' ;
        }
    };

    // true if a and b have the same logical state
    bool operator== ( const toy_string& a, const toy_string& b )
    { return std::strcmp( a.pstr, b.pstr ) == 0 ; }

    bool operator!= ( const toy_string& a, const toy_string& b ) { return !(a==b) ; }
}

namespace deep {

    struct toy_string {

        static constexpr std::size_t N = 5 ;
        char* pstr = new char[N] { 'a', 'b', 'c', 'd', '\0' } ;

        toy_string() = default ;
        toy_string( const toy_string& that ) : pstr( new char[N]{} ) { change(that.pstr) ; }
        toy_string( toy_string&& that ) noexcept : pstr(nullptr) { swap(that) ; }
        toy_string& operator= ( toy_string that ) noexcept { swap(that) ; return *this ; }
        ~toy_string() { delete[] pstr ; }

        void swap( toy_string& that ) noexcept { using std::swap ; swap( pstr, that.pstr) ; }

        void change( const char* cstr ) noexcept { std::strncpy( pstr, cstr, N-1 ) ; }

        void dump( const char* name = "" ) const {

            std::cout << "toy_string (deep) " << name << " @ " << this << " pstr: "
                      << (const void*) pstr << " => " << std::quoted(pstr) << '\n' ;
        }
    };

    // true if a and b have the same logical state
    bool operator== ( const toy_string& a, const toy_string& b )
    { return std::strcmp( a.pstr, b.pstr ) == 0 ; }

    bool operator!= ( const toy_string& a, const toy_string& b ) { return !(a==b) ; }
}

#define DUMP(desc,x) { std::cout << std::setw(25) << desc << ": " ; (x).dump(#x) ; }

#define CHECK_EQUAL(a,b) ( std::cout << "    " #a " == " #b " ? " << std::boolalpha << (a==b) << "\n\n" )

template < typename T > void test() {

     const T const_aaa{} ;
     DUMP( "init", const_aaa ) ;

     T mutable_b = const_aaa ;
     DUMP( "copy construct", mutable_b ) ;
     CHECK_EQUAL( const_aaa, mutable_b ) ;

     mutable_b.change("**") ;
     DUMP( "after change mutable_b", mutable_b ) ;
     DUMP( "after change mutable_b", const_aaa ) ;
     CHECK_EQUAL( const_aaa, mutable_b ) ;

     const T const_ccc{} ;
     DUMP( "init", const_ccc ) ;

     mutable_b = const_ccc ;
     DUMP( "after assignment", mutable_b ) ;
     CHECK_EQUAL( const_ccc, mutable_b ) ;

     mutable_b.change("!!!") ;
     DUMP( "after change mutable_b", mutable_b ) ;
     DUMP( "after change mutable_b", const_ccc ) ;
     CHECK_EQUAL( const_ccc, mutable_b ) ;
}

int main() {

    std::cout << "shallow\n---------\n" ;
    test<shallow::toy_string>() ;

    std::cout << "\ndeep\n---------\n" ;
    test<deep::toy_string>() ;
}

shallow
---------
                     init: toy_string (shallow) const_aaa @ 0x7ffc103a3ec8 pstr: 0x2126c30 => "abcd"
           copy construct: toy_string (shallow) mutable_b @ 0x7ffc103a3ed0 pstr: 0x2126c30 => "abcd"
    const_aaa == mutable_b ? true

   after change mutable_b: toy_string (shallow) mutable_b @ 0x7ffc103a3ed0 pstr: 0x2126c30 => "**"
   after change mutable_b: toy_string (shallow) const_aaa @ 0x7ffc103a3ec8 pstr: 0x2126c30 => "**"
    const_aaa == mutable_b ? true

                     init: toy_string (shallow) const_ccc @ 0x7ffc103a3ed8 pstr: 0x2126c50 => "abcd"
         after assignment: toy_string (shallow) mutable_b @ 0x7ffc103a3ed0 pstr: 0x2126c50 => "abcd"
    const_ccc == mutable_b ? true

   after change mutable_b: toy_string (shallow) mutable_b @ 0x7ffc103a3ed0 pstr: 0x2126c50 => "!!!"
   after change mutable_b: toy_string (shallow) const_ccc @ 0x7ffc103a3ed8 pstr: 0x2126c50 => "!!!"
    const_ccc == mutable_b ? true


deep
---------
                     init: toy_string (deep) const_aaa @ 0x7ffc103a3e58 pstr: 0x2126c70 => "abcd"
           copy construct: toy_string (deep) mutable_b @ 0x7ffc103a3e60 pstr: 0x2126c90 => "abcd"
    const_aaa == mutable_b ? true

   after change mutable_b: toy_string (deep) mutable_b @ 0x7ffc103a3e60 pstr: 0x2126c90 => "**"
   after change mutable_b: toy_string (deep) const_aaa @ 0x7ffc103a3e58 pstr: 0x2126c70 => "abcd"
    const_aaa == mutable_b ? false

                     init: toy_string (deep) const_ccc @ 0x7ffc103a3e68 pstr: 0x2126cb0 => "abcd"
         after assignment: toy_string (deep) mutable_b @ 0x7ffc103a3e60 pstr: 0x2126cd0 => "abcd"
    const_ccc == mutable_b ? true

   after change mutable_b: toy_string (deep) mutable_b @ 0x7ffc103a3e60 pstr: 0x2126cd0 => "!!!"
   after change mutable_b: toy_string (deep) const_ccc @ 0x7ffc103a3e68 pstr: 0x2126cb0 => "abcd"
    const_ccc == mutable_b ? false

http://coliru.stacked-crooked.com/a/94fad7b24dd6dcc1
http://rextester.com/GVO56365
Topic archived. No new replies allowed.