0

With respect to the question What is the difference between memberwise copy, bitwise copy, shallow copy and deep copy? author of first answer says:

Shallow Copy

Refers to copying just the immediate members of an object, without duplicating whatever structures are pointed by them. It is what you get when you do a bit-wise copy.

Why are we having 2 terms used for same thing as Bitwise copy and Shallow copy. Is there really no difference between them?

Does the same apply for other languages (not just C++) and can I use these words interchangeable generally?

Tatranskymedved
  • 4,194
  • 3
  • 21
  • 47
  • 1
    You can disable shallow copying by `= delate` ing the copy constructor, but you can't prevent determined idiots from shooting themselves in the foot by `memcpy()`ing the bitwise representation of the object. – EOF Dec 27 '20 at 11:00
  • 1
    Copying the immediate members of an object will not copy any padding bytes. A bitwise copy usually implies copying the padding. – Richard Critten Dec 27 '20 at 11:00
  • @EOF is the word "delate" right? Did you mean `delete`-ing? Or this is some kind of dark cpp I don't know yet about? :) Could you provide reference for that? – Tatranskymedved Dec 27 '20 at 19:02
  • @Tatranskymedved I can't type to save my life. `= delete` is correct. – EOF Dec 27 '20 at 21:02

2 Answers2

1

A bit-wise copy is a shallow copy but not necessarily vice versa.

The reason is that due to padding there can be bits in objects that you usually ignore, yet they are part of an object.

For example this

struct bar {
    int x;
    foo b;
};

Can lool like this in memory:

| x | some padding | b | more padding |

When you copy the bits via eg memcpy then the padding bits will be copied as well. By comparing the members x and b you cannot tell the difference, but at the level of bits there is a difference. You can notice it when you compare two objects bit wise (instead of member-wise).


As pointed out by Yakk - Adam Nevraumont in a comment, padding is not the only reason why a shallow copy can differ from a bitwise copy. For example

struct foo{ 
    foo* self;
    foo() : self(this) {}
    foo& operator=(const foo& other) {}
    foo(const foo& other) : self(this) {}
};

The member self should point to the object itself, thats an invariant of the class (proper encapsulation omitted for the sake of simplicity). Just copying the pointer would make self->this point to other not to this, hence break the invariant. The operator= does not have to copy anything and a copy constructor merely has to properly initialize self. This can be considered as a shallow copy, because we are only "copying" the pointer self not what it points to (and actually a deep copy would be fatal here). However, a bitwise copy would be different, it would cause a.self point to b after copying b to a (which again would break the invariant).


Consider this example:

#include <iostream>
#include <cstring>

struct X {
    int a = 1;
    double b = 2;
    float c = 3;
    X& operator=(const X& x){
        a = x.a;
        b = x.b;
        c = x.c;
        return *this;
    }
};

int main()
{
    X a;
    X b;
    std::cout << "sizeof(X) " << sizeof(X) << "\n";
    std::cout << "sizeof(int) " << sizeof(int) << "\n";
    std::cout << "sizeof(double) " << sizeof(double) << "\n";
    std::cout << "sizeof(float) " << sizeof(float) << "\n";
    
    //memcpy(&a,&b,sizeof(X));
    a = b;
    char* aptr = reinterpret_cast<char*>(&a);
    char* bptr = reinterpret_cast<char*>(&b);
    for (size_t i = 0; i < sizeof(X); ++i) {
        if (aptr[i] != bptr[i]) std::cout << " !!! ";
    }
}

Possible output is:

sizeof(X) 24
sizeof(int) 4
sizeof(double) 8
sizeof(float) 4
 !!! 

The size of X is not the sum of the size of its members. That is padding. For more details I refer you to Struct padding in C++.

The operator= does a member-wise copy. Because the object contain bytes that are not used for the members, you can observe a difference between a and b after copying the members and looking at the bit representation.

On the other hand, if the copy was made via memcpy then the bit representation of a and b would be guaranteed to be identical (ie no output !!!).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • So technically I can write shallow copy in a way that it will do bitwise copy on each reference separately, thus ignoring padding. In that case we would consider that we did `bitwise` copy of an object. Otherwise (using `memcpy`) we just call it `shallow` copy, because there might be and we don't really care if there is any overhead. Correct? – Tatranskymedved Dec 27 '20 at 11:09
  • @Tatranskymedved you need to be very careful about resources owned by the member objects if you attempt a shallow copy using memcpy as you will end up with unexpected shared ownership. – Richard Critten Dec 27 '20 at 11:14
  • @Tatranskymedved I don't really understand your comment. As long as you do not look at the individual bits of the object a bitwise and a shallow copy are the same. "In that case we would consider that we did bitwise copy of an object" No, because you can look at the bits of the object and padding is part of that, – 463035818_is_not_an_ai Dec 27 '20 at 11:18
  • I clearly don't understand what is padding how it gets there. Never heard of it (I'm coming from C# environment and I wasn't dealing with this low-level inspection). Sorry for confusion. – Tatranskymedved Dec 27 '20 at 11:26
  • 1
    @Tatranskymedved often this difference does not matter. It does matter when you copy objects and expect their bit representation to be identical – 463035818_is_not_an_ai Dec 27 '20 at 11:40
  • Note that padding isn't the only possible difference. `struct foo{foo*self;foo():self(this){}};` copied bitwise loses the invariant that self points to itself. A shallow copy that repoints the pointer remains shallow, but is not bitwise. – Yakk - Adam Nevraumont Dec 27 '20 at 13:11
  • @Yakk-AdamNevraumont good point. I wrongly assumed that a shallow copy is always a member wise copy (what you get from the compiler generated copies), but it isnt. I added it to the answer – 463035818_is_not_an_ai Dec 27 '20 at 13:56
  • @Yakk-AdamNevraumont actually not sure if I interpreted your example correcty... – 463035818_is_not_an_ai Dec 27 '20 at 13:58
  • Naw that is right. Now self reference usually isn't that naked, but it definitely happens. A std::function that needs a this pointer, for example. – Yakk - Adam Nevraumont Dec 27 '20 at 18:15
0

If I understand correctly, a bitwise copy is a shallow copy. A bitwise copy is simply copying over each bit to create a copy, while a shallow copy is more general in copying the immediate members as said in your quote.

Matthew Lin
  • 371
  • 1
  • 7
  • Does that mean that immediate members **doesn't have to be** bitwise copied? I probably don't understand this part. Can you elaborate a little more? – Tatranskymedved Dec 27 '20 at 11:04
  • Well I mean in theory under the hood it could literally be doing an assignment for each member of an object, but a bitwise copy may be used for like an `int` where it will literally copy over the bits – Matthew Lin Dec 27 '20 at 11:06