13

Consider the following class:

class A {

char *p;
int a, b, c, d;

public:
   A(const &A);
};

Note that I have to define a copy constructor in order to do a deep copy of "p". This has two issues:

  1. Most of the fields should simply be copied. Copying them one by one is ugly and error prone.

  2. More importantly, whenever a new attribute is added to the class, the copy constructor needs to be updated, which creates a maintenance nightmare.

I would personally like to do something like:

A(const A &a) : A(a)
{
   // do deep copy of p
   :::
}

So the default copy constructor is called first and then the deep copy is performed.
Unfortunately this doesn't seem to work.

Is there any better way to do this? One restriction - I can't use shared/smart pointers.


Sbi's suggestions make a lot of sense. I think I'll go with creating wrapper classes for handling the resource. I don't want to user shared_ptr since boost libraries may not be available on all platforms (at least not in standard distributions, OpenSolaris is an example).

I still think it would have been great if you could somehow make the compiler to create the default constructor/assignment operators for you and you could just add your functionality on top of it. The manually created copy constructor/assignment operator functions I think will be a hassle to create and a nightmare to maintain. So my personal rule of thumb would be to avoid custom copy constructors/assignment operators at all cost.

Thanks everybody for their responses and helpful information and sorry about typos in my question. I was typing it from my phone.

icedwater
  • 4,701
  • 3
  • 35
  • 50
Roux hass
  • 754
  • 5
  • 5
  • 5
    Why can't you use smart pointers? –  Jul 07 '10 at 07:34
  • 4
    @Neil: Perhaps his keyboard has no underscore? – fredoverflow Jul 07 '10 at 07:56
  • All answers so far focus on not needing a custom copy c'tor. But the intersting part of the question raised here is: Is it possible to retain some of the automatically generated functionality and just extend it (not just limited to dealing with a pointer). – Gene Vincent Jul 07 '10 at 07:59
  • 3
    @Gene Vincent: Yes, by adding a derivation or composition relationship to a class with an autogenerated copy constructor. – Philipp Jul 07 '10 at 08:04
  • If you need a smart pointer which can allow deep copy you can check axter smart pointer. Works pretty good for me. Otherwise I also use the trick used by Charles in the answers from time to time. – n1ckp Jul 07 '10 at 10:48

9 Answers9

20

As a rule of thumb: If you have to manually manage resources, wrap each into its own object.

Put that char* into its own object with a proper copy constructor and let the compiler do the copy constructor for A. Note that this also deals with assignment and destruction, which you haven't mentioned in your question, but need to be dealt with nevertheless.
The standard library has several types to pick from for that, among them std::string and std::vector<char>.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 3
    +1 Never forget the big three: http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29 – Björn Pollex Jul 07 '10 at 07:54
  • 3
    This is THE advice to follow. Whenever you need to write a copy constructor for a "functional" class (as opposed to a technical one), you're probably doing something wrong. – Matthieu M. Jul 07 '10 at 08:49
4

Replace char* with std::string.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 2
    I think char* p is just an example for any pointer in a class in need of special treatment in a copy c'tor. – Gene Vincent Jul 07 '10 at 07:38
  • 2
    @Gene Vincent: Then the OP should say so in the question. In that case, a dedicated RAII class should be written for managing the pointer. – Philipp Jul 07 '10 at 07:42
  • 2
    @Philipp: ...or a suitable off-the-shelf one chosen. – sbi Jul 07 '10 at 07:48
3

Always use RAII objects to manage unmanages resources such as raw pointers, and use exactly one RAII object for each resource. Avoid raw pointers in general. In this case, using std::string is the best solution.

If that's not possible for some reason, factor the easy to copy parts out into a base class or a member object.

Philipp
  • 48,066
  • 12
  • 84
  • 109
3

You could separate your copyable members into a POD-struct and mantain your members requiring a managed copy separately.

As your data members are private this can be invisible to clients of your class.

E.g.

class A {

char *p;

struct POData {
    int a, b, c, d;
    // other copyable members
} data;

public:
   A(const &A);
};

A(const A& a)
    : data( a.data )
{
    p = DuplicateString( a.p );
    // other managed copies...
    // careful exception safe implementation, etc.
}
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
2

You really should use smart pointers here.

This would avoid rewriting both the copy constructor and the affectation operator (operator=).

Both of these are error prone.

A common mistake with the operator= is implementing it that way:

SomeClass& operator=(const SomeClass& b)
{
  delete this->pointer;
  this->pointer = new char(*b.pointer); // What if &b == this or if new throws ?

  return *this;
}

Which fails when one does:

SomeClass a;
a = a; // This will crash :)

Smart pointers already handle those cases and are obviously less error prone.

Moreover, Smart pointers, like boost::shared_ptr can even handle a custom deallocation function (by default it uses delete). In practice, I rarely faced a situation where using a smart pointer instead of a raw pointer was unpractical.

Just a quick note: boost smart pointer class, are header-only designed (based on templates) so they don't require additional dependencies. (Sometimes, it matters) You can just include them and everything should be fine.

ereOn
  • 53,676
  • 39
  • 161
  • 238
  • 1
    Using `shared_ptr` here implies that all copies of that object share the same instance of the object pointed to. That may not be what the OP wants. – Björn Pollex Jul 07 '10 at 07:51
  • @Space_C0wb0y, you're absolutely right. However, even if he wants each instance to have its own copy, using smart pointers is still much less error prone. – ereOn Jul 07 '10 at 07:54
  • 2
    `this.pointer` ?? Doesn't look like C++ to me. The explanation also misses another key C++ concept, exception safety. The code will likely crash if `new` throws, and `this->pointer` is deleted for the second time in the dtor. – MSalters Jul 07 '10 at 08:58
  • @MSalters: That was obviously a typo. I have been doing too much `C#` lately ;) Also, my answer was showing **one** possible and *common* mistake. Of course exception safety matters and yes, you're right; I updated my answer. Thanks. – ereOn Jul 07 '10 at 11:51
  • 1
    I'd have to dig up the precise Sutter article, but he showed that once you have exception-safety, safe self-assignment usually comes as a side-effect. This is because you normally get exception-safety from an "allocate new, deallocate old" order. This means you need not for self-assignment. It is also very rare, so those checks are also inefficient. Hence in cases one should advice to focus on exception-safety, and not bother with self-assignment. – MSalters Jul 07 '10 at 13:39
0

The question is, do you really need a pointer with deep-copy semantics in your class? In my experience, the answer almost always is no. Maybe you could explain your scenario, so we may show you alternative solutions.

That said, this article describes an implementation of a smart-pointer with deep-copy semantics.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
0

While I agree with others saying that you should wrap the pointer in its own class for RAII and let the compiler synthesise the copy contructor, destructor and assignment operator there is a way around your problem: declare (and define) private static function which will do whatever is needed and common for different constructors and call it from there.

Tomek
  • 4,554
  • 1
  • 19
  • 19
0

So the default copy constructor is called first and then the deep copy is performed. Unfortunately this doesn't seem to work.

Is there any better way to do this? One restriction - I can't use shared/smart pointers.

If I understand correctly, your question, you could consider using an initialization function:

class A
{
    int i, j;
    char* p;

    void Copy(int ii, int jj, char* pp); // assign the values to memebers of A
public:
    A(int i, int j, char* p);
    A(const A& a);
};

A::A(int i, int j, char* p)
{
    Copy(i, j, p);
}

A::A(const A& a)
{
    Copy(a.i, a.j, a.p);
}

That said, you really should consider using RAII ( there's a reason people keep recommending it :) ) for your extra resources.

If I can't use RAII, I still prefer creating the copy constructor and using initializer lists, for every member (actually, I prefer doing so even when using RAII):

A::A(int ii, int lj, char* pp)
    : i(ii)
    , j(jj)
    , p( function_that_creates_deep_copy(pp) )
{
}

A::A(const A& a)
    : i(a.i)
    , j(a.j)
    , p( function_that_creates_deep_copy(a.p) )
{
}

This has the advantage of "explicitness" and is easy to debug (you can step in and see what it does for each initialization).

mskfisher
  • 3,291
  • 4
  • 35
  • 48
utnapistim
  • 26,809
  • 3
  • 46
  • 82
0

Unless your class has one function, which is managing a resource, you should never manage any resources directly. Always use a smart pointer or custom management class of some description. Typically, it's best to leave the implicit copy constructor, if you can. This approach also allows easy maintenance of the destructor and assignment operators.

Puppy
  • 144,682
  • 38
  • 256
  • 465