2

I don't understand why we need a copy constructor (to begin with..) especially for the sake of reference members.

Given a class

class Foo {
   Obj &obj;
};

I must now write a copy constructor and = operator (and maybe 5 other secret things), otherwise it won't compile. Why? Why would a compiler be able to figure out that it should copy an int or string but not a reference? Same for a pointer? What does it think I might want to do instead? I have to write out exactly what the compiler writes out for every other property. What if I add, or worse, someone else adds, a new property to the class and forgets to add that to the copy ctor and operator?

  • 2
    The compiler does shallow copy by default. If you don't know the difference between a shallow copy and a deep copy, that'd be the question to ask first. It will help answer why we need copy constructors at all, as well. – sweenish Aug 12 '20 at 15:08
  • It's worse when it does compile and blows up at runtime. With a compiler error, you know you have a problem. With a runtime error, you better pray you find it before the airplane's crew does. – user4581301 Aug 12 '20 at 15:17
  • @sweenish the shallow copy point is moot in case of references. – SergeyA Aug 12 '20 at 15:19
  • 1
    Imagine the reference is initialised to refer to another member of the class. The compiler-generated copy constructor will, when copying an instance of `Foo`, cause the reference in the copy to refer to a member of the original object, rather than to its own corresponding member. A hand-rolled copy constructor is needed if that is not the desired behaviour. – Peter Aug 12 '20 at 15:20
  • But there's nothing special about a reference, or even a pointer. A reference should be dead easy for the compiler to figure out. It doesn't need to allocate anything. Some with a pointer. Just copy the pointer address to the new pointer. You don't need to reallocate anything at all. The memory is in the same place on the heap. Just copy the pointer value. Why is it like this? – tanks_____tanks Aug 12 '20 at 15:20
  • @tanks_____tanks Your last comment is incredibly naive. – sweenish Aug 12 '20 at 15:21
  • 1
    Also remember that a reference cannot be reassigned. – user4581301 Aug 12 '20 at 15:21
  • @SergeyA Sure, but OP is also asking why a copy ctor is needed, period. Not just because of a reference. – sweenish Aug 12 '20 at 15:22
  • @tanks_____tanks No, it is not the same thing as a pointer. It is not possible to change what a reference refers to, which makes the behavior you expect impossible. This is the intuitive behavior a lot of beginners would expect, so doing something different would be surprising and you never want the language to be surprising. There isn't a "safe" course of action the language can default to so instead it asks you to tell it what you want. In general, reference data members have a lot of problems and should be avoided in favor of raw pointers. – François Andrieux Aug 12 '20 at 15:23
  • The classic failure example is with ownership of a resource. Say in the constructor the object allocates a block of memory. Someone has to free is, so you add a destructor to put the memory back. Then you copy the object. The default behaviour now has two objects pointing to the same memory and they will both attempt to free it. Doesn't happen very often with references, but it happens all the time with pointers. – user4581301 Aug 12 '20 at 15:25
  • Ok I see. It sounds like no matter what I do, I am using C++ wrong. There are just so many traps. I might just go back to C. – tanks_____tanks Aug 12 '20 at 15:30
  • @sweenish Yes maybe it is naive. But (as everyone who downvotes me as usually) people forget we come to ask questions, not to write answers to things we already know. "Did you know that you need to write a copy ctor in C++" wouldn't be a great question.. – tanks_____tanks Aug 12 '20 at 15:33
  • @tanks_____tanks But "why do we even need one" is beginner-level C++ OOP. If you don't know the answer to this question, there are better questions to be asking, like I mentioned in my first comment. Or it's time to get a "good book." A raw pointer, for example, can be dynamic data that the object needs to own, or simply an observer to a shared resource. The compiler can't figure that out on its own like you think it should. And how you copy those are very different. – sweenish Aug 12 '20 at 15:44
  • You either need to learn the tool, or you're using the wrong tool. SO is a terrible learning site. It's a pretty good Q&A site for your specific questions. Some of the answers and comments here are tropey and mis-applied like "never use raw pointers." That's silly advice. Raw pointers still have a place, as do static arrays. Here's the link to the "good book" list: https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list – sweenish Aug 12 '20 at 16:06
  • The concern usually isn't with the pointer; the presence of a pointer is usually just a side-effect of a different design mistake. Prefer to pass with a reference over a pointer is about the only "Ermagerd! You used a pointer!" The rest usually resolve to unnecessary dynamic allocation (prefer Automatic allocation) or how you're managing necessary dynamically allocated objects (use containers and smart pointers). Dogma is the enemy. Understanding the rational the dogma grew out of is what gets you out of tight scrapes. – user4581301 Aug 12 '20 at 16:59

1 Answers1

4

The short answer is: because C++ standard says so. But this is not a very interesting answer.

To have a more interesting answer, we can ask a different question: what might be the rationale for such behavior?

While I do not claim to know the canonical answer to this, I would not be surprised if it is just to reduce confusion. References as members could behave unexpectedly, and copying classes with them could yield results you are not expecting.

Technically speaking, nothing stops compiler from honestly copying the referenced member from source to destination. But a lot of people would expect the class instead to "repoint" the reference (similar to pointers). But references can not be repointed, and rationale for that is given in "Design and Evolution of C++":

The reason that C++ does not allow you to rebind references is given in Stroustrup's "Design and Evolution of C++" :

... I had in the past been bitten by Algol68 references where r1=r2 can either assign through r1 to the object referred to or assign a new reference value to r1 (re-binding r1) depending on the type of r2. I wanted to avoid such problems in C++.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Yeah. I guess I just think "its 2020, why do we still have to manually type this out when it should know by now what I want.." Like, doesn't it? Don't the designers get that we all know exactly what our copy ctor and = operator want to do? We all know!! So just do it for us! This is causing me anxiety because someone else, or myself, will eventually forget to copy a new member of the class!!! People don't read your code at all when they modify it.. – tanks_____tanks Aug 12 '20 at 15:39
  • 1
    @tanks_____tanks but how compiler can know what you want? The only way for compiler to know that is when you write your copy code! – SergeyA Aug 12 '20 at 15:45
  • I don't know! Haha. It should just know. I get that it's so cool that we can mange memory down to the address level. But what if we could just write some code that did what we wanted instead of pleasing the compiler or the language in obscure ways. But I dream.. – tanks_____tanks Aug 12 '20 at 15:54
  • I'm waiting for the telepathic compiler extensions to be added in C++79. – user4581301 Aug 12 '20 at 16:38