Thread: "deep copy"

  1. #1
    Registered User
    Join Date
    May 2003
    Posts
    38

    "deep copy"

    I am having a problem with pointers (I think). I have "(1)" an instance of a data class, and "(2)" a pointer to a data class (the same data class that "(1)" is an instance of). This data class is build up of a variety of things (lets say; a few "doubles" and a few "data classes" and a few "pointers").

    Question1: How do I get a copy of what "(2)" is holding, stored into "(1)"?

    (should I just create an update function that does something like: storage.x = ptr_storage->x; )?

    Question2: If I am working with pointers to classes does this work?

    Code:
    DataClass storage;            // an instance of the data class
    DataClass *ptr_storage;     // a pointer to the data class
    ...
    storage.initialize();
    ptr_storage = new DataClass();
    ...
    // pretend there is code here that spawns off a thread, and continually updates "ptr_storage" with new data
    ...
    for loop{
       storage = *ptr_storage;  // DOES This give me a seperate and "deep" copy of the data object that ptr_storage is pointing too?
       ....
    }
    chris

  2. #2
    Has a Masters in B.S.
    Join Date
    Aug 2001
    Posts
    2,263
    have you overridden the = operator? the default is just a shallow copy...
    ADVISORY: This users posts are rated CP-MA, for Mature Audiences only.

  3. #3
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,090
    Quote Originally Posted by cjschw
    // DOES This give me a seperate and "deep" copy of the data object that ptr_storage is pointing too?
    That will give you a deep copy only if the operator= is implemented for DataClass. Otherwise it does a shallow copy of the data in the object pointed to by ptr_storage. Since your DataClass has pointers and other classes, it is quite possible that it requires a user defined operator=, copy constructor, and destructor. If so, then as long as those are implemented appropriately, then the copy on that line should work.

    So to answer your questions, as long as DataClass is implemented correctly, your usage of it appears to be correct: (object = *ptr;). Note that I am ignoring any issues that may arise from your usage of threads in this scenario, since I assume that is only an example and not the point of the question..

  4. #4
    Registered User
    Join Date
    May 2003
    Posts
    38
    Thanks for the help. Some addition follow up questions. What is the difference between a deep and a shallow copy? Is it true that anytime I am overwritting the operator= so that I get a deep copy, I also need to create a copy constructor? Normally my operator= are defined as "Object operator=(Object);" , but what also having this: "Object operator=(Object*);"
    chris

  5. #5
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    int * ptr1 = new int;
    *ptr1 = 4;

    int * ptr2 = ptr1; //shallow copy, just copy address in ptr1 to ptr2. works fine, unless you do this:

    delete ptr1;

    now the address in ptr1 and ptr2 is gone and ptr2 is pointing at nothing. To avoid this do deep copy--that is, transfer the data at the address, not the address itself.

    int * ptr3 = new int;
    *ptr3 = 5;
    int * ptr4 = new int;
    *ptr4 = *ptr3;

    now

    delete ptr3;

    and ptr4 still has value that was in ptr3, but it is stored at a different address so it isn't lost when ptr3 is deleted or destroyed or goes out of scope.

  6. #6
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,090
    If for some reason you want to be able to pass a pointer to operator=, I would suggest creating a different function instead (e.g. CopyFromPointer(Object*)). Of course, I see no reason why you'd need to do that, except maybe if you wanted to allow the caller to pass in a null pointer, but even then I don't know if that would be a good design. So in my opinion, operator=(Object*) is not a good idea.

    Also, any reason you have for creating an operator= will probably also be a good reason to create a copy constructor. So if you do one you'll probably do the other (and often times you want to implement the operator= in terms of the copy constructor and a swap() function).

    Of course, you don't always need an operator=/copy constructor. Whether you should define your own operator= in a class or not depends on the data in that class. For example, in the following code, the first class does not require an operator=, but the second one does:
    Code:
    class NoOpEqNeeded
    {
    public:
        NoOpEqNeeded(double newVal, const char* newName) : value(newVal), name(newName), index(0) { }
    private:
        double value;
        std::string name;
        int index;
        std::list<int> savedDigits;
    };
    
    class OpEqNeeded
    {
    public:
        OpEqNeeded(double newVal, const char* newName) : value(new double(newVal))
        {
            name = new char[strlen(newName) + 1];
            strcpy(name, newName);
        }
        ~OpEqNeeded() { delete value; delete name; }
    private:
        double* value;
        char* name;
    };
    The reason is that the second one has allocated memory stored as a pointer. If you don't add your own operator=, and you try Obj2 = Obj1, then value and name in Obj2 point to the same memory as value and name in Obj1. When their destructors are called, you will likely get a crash, because that memory will be deleted twice.

    On the other hand, the data in the first class is either POD types (e.g. int, double, char, float) or classes that already have their own operator= and copy constructor defined (like string and list). Because of that, you don't have to worry about writing your own, because the default one will work perfectly.

    Also notice how the first class doesn't need a destructor, but the second class does. The general rule is that if you need a destructor, then you will very likely need a copy constructor and an operator=, so the following three functions come together.

    ~Object();
    Object(const Object&);
    const Object& operator=(const Object&);

    Slight changes to those prototypes can be made if necessary, depending on your situation, but I think those are the normal forms.

Popular pages Recent additions subscribe to a feed