1

To my knowledge, in theory, if a class has a raw pointer member, then the default copy constructor will do a shallow copy of that pointer, such that when the original object is destroyed, the pointer member in the copy will have had the value to which it pointed deleted. This seems to imply that outside of the case where we want to restrict copying for some reason, any class with a raw pointer member should define a copy constructor to do a deep copy on that pointer.

I am working with a respectable third party API, and I've come across a class with a raw pointer member, but no defined copy constructor, which casts doubt on my understanding above. Am I missing something?

UPDATE: The third party informed me that this class is not meant to be copied, as the object represents a visual element. They noted that they should have made a private copy constructor.

Kromster
  • 7,181
  • 7
  • 63
  • 111
Jayz7522
  • 324
  • 1
  • 13
  • 2
    It depends on what the destructor of the original object does. If it deletes it, then your analysis is correct. It is likely that they don't expect copies to be made the way you are discussing. – jschultz410 Mar 09 '15 at 01:54
  • 1
    @ikegami: A non-copyable class should have a copy constructor that's either `private` or `delete`d. We can't be 100% sure here, but the way I read the question neither is the case. – Lightness Races in Orbit Mar 09 '15 at 01:54
  • @ikegami: And I didn't claim that you did. – Lightness Races in Orbit Mar 09 '15 at 01:55
  • Fine, I'll rephrase: Not all classes are written to handle attempts to copy. That's a feature, and features require effort (coding, testing, maintenance). – ikegami Mar 09 '15 at 01:59
  • @jschultz410 Perhaps a separate question altogether, but under what circumstances, at the end of destruction, can a member be anything but deleted (if not leaked)? – Jayz7522 Mar 09 '15 at 02:02
  • 1
    @Jayz7522 When some other construct is responsible for deallocating the memory (if that is ever necessary). Maybe the pointer points at statically allocated stuff for example. An iterator might point at an internal node within a collection's implementation, but it certainly shouldn't delete it when the iterator is destructed. – jschultz410 Mar 09 '15 at 02:04
  • Does the third-party library define a copy-assignment function (operator=) and/or destructor? If so, you have probably found a bug; if not, you probably have not (http://stackoverflow.com/q/4172722/) – Nemo Mar 09 '15 at 02:11
  • @Nemo It does define a destructor, but no copy-assignment function - the implementation of which is buried in a dll. Sounds like at best the destructor purposefully does not delete the memory pointed to by the raw pointer; at worst, it is a bug.. – Jayz7522 Mar 09 '15 at 02:19

3 Answers3

5

I think std::reference_wrapper is part of a respectable API (the C++ standard library). It has a copy constructor but that's not necessarily explicitly defined in the implementation code, because it just copies the raw pointer. So there you are: having a pointer member doesn't always imply ownership.

As a counter-example, an example of where you have non-owning pointer member but still need to take charge of copying, an object can contain a pointer to a part of itself. It would be ungood if copying such an object resulted in its internal pointer pointing to a part of some other object.

In summary, it depends. Must often you need a defined copy constructor. But absolutely not always.


For C++03 the law of three (or "rule of three") was a rule of thumb that if you needed either a destructor, a copy assignment operator or a copy constructor, then you probably needed all three.

So check if there is a copy assignment operator or destructor. In that case a copy constructor is probably required, and missing.

In C++11 and later some people extend it to the law of five, by including move assignment operator and move constructor, and some people reduce to the law of zero, by requiring that all ownership should be expressed via smart pointers or collection objects.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Thanks a lot. Ignorance revealed: could you say, or point me to something that says, what a non-owning pointer is, exactly? The name is somewhat intuitive, but I could use some additional clarity. – Jayz7522 Mar 09 '15 at 02:27
  • 1
    The basic example of an owning pointer is where when that pointer ceases to exist, the object it points to must be destroyed. I.e. the pointer owns the object that it refers to. `std::unique_ptr` automates one kind of such single ownership, where the pointer can't be copied but can be moved. An alternative is an automatic deep copy, cloning, but the standard library does not support that. Ownership can be shared so that when the last pointer to an object ceases to exist, the object must be destroyed. `std::shared_ptr` automates this kind. A non-owning pointer is simply not owning. Pure ref. – Cheers and hth. - Alf Mar 09 '15 at 02:42
3

In an ideal world, any raw pointer member could be assumed to be a non-owning pointer. Since, if you had wanted an owning pointer you would have used a smart pointer!

A non-owning pointer member will point at an object whose lifetime is managed outside of the class and should be (by design) longer than the lifetime of the pointer.

The default behaviour when you copy the class is for the copy to also point at the same object. Often that is the behaviour you want. If not, yes, you will have to change that behavior.

Of course, we don't live in an ideal world and there are many places where owning raw pointer members are used. In that case, you are right, the default copy constructor is not appropriate (see Alf's answer regarding the rule of 3/5).

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
0

No, you're not missing anything. In general.

I'm hesitant to make any specific guarantees without knowing what library you're talking about and what the semantics of that class are (documented or otherwise) but, on the surface of it, this sounds like a library bug.

It's possible that the class doesn't own the pointee. This will likely be the case if its constructor doesn't allocate it, and its destructor doesn't de-allocate it.

Barring that, though, you're right.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055