1

I'm trying to understand Deep Copy. But I'm just confused about allocating dynamically memory by calling Constructor.

Here is my successful program for Deep copy:

#include<iostream>
using namespace std;
class A{
    public:
    int *p;
    A(){
        p=new int;
    }
    void set(int b){
        *p=b;
    }
    int get(){
        return *p;
    }
    void operator=(const A &k){
        p = new int;
        *p=*(k.p);
    }
    ~A(){
        delete p;
    }
};
int main()
{
    A obj;
    obj.set(3);
    A obj1;
    obj1=obj;
    obj1.set(5);
    cout << obj.get() << endl;
    cout << obj1.get() << endl;
}

Now I just want to ask that I created two objects and constructor will call for two times and in constructor there dynamically memory is allocating.

Then My Question is that pointer should point two different dynamically memory location(2 object and 2 pointer) or is pointer same as Static data members(then no deep copy should require)? Mean one pointer for all objects of class.

Gabriel
  • 763
  • 1
  • 10
  • 28
  • Irrelevant to the question but be careful, you're leaking memory in your assignment operator (you should release the memory allocated before assigning `p`). – Caninonos Aug 02 '15 at 19:48
  • @Caninonos can you confirm how I can solve that problem? that you told me memory leakage in = operator. –  Aug 02 '15 at 20:20

1 Answers1

0

If you create two instances of that class, they will have two different p members each containing a different address of different regions in memory, yes.
You can probably tell if you run your program: it will display 3 and 5, if the pointer was the same, it would have displayed 5 twice.

Edit: as asked, some additional explanation (and a summary of what have been said in the comments)

First of all, your operator= is leaking memory, instead you should remember to release the memory already allocated in p before reassigning it:

void operator=(const A &k){
    delete p;
    // as JoshuaGreen states in the comments, you can set p to nullptr
    // here, that way, if new fails and throws, p will be set to nullptr
    // and you'll know it doesn't contain anything (you'll have to test
    // it in other methods to benefit from this modification though,
    // but it will be safer)
    p = nullptr;
    p = new int;
    *p=*(k.p);
}

Although in this specific case, you could just avoid reallocating:

void operator=(const A &k){
    // p = new int; // not needed
    *p=*(k.p);
}

Now, that was indeed important to overload the assignment operator= (and you actually forgot to overload the copy constructor), let's see what would have happened if this assignment operator= wasn't defined.
Your class would instead look like this:

#include<iostream>
using namespace std;
class A{
public:
    int *p;

    A(){
        p=new int;
    }

    void set(int b){
        *p=b;
    }

    int get(){
        return *p;
    }

    ~A(){
        delete p;
    }
};

But actually, the compiler will generate an implicitly defined default copy constructor and default assignment operator= for you. Of course you don't see their implementation, but they would behave exactly the same as if the class was defined like this:

#include<iostream>
using namespace std;
class A{
public:
    int *p;
    A(){
        p=new int;
    }

    A(const A& other) :
    p(other.p) {} // ! we're copying the pointer instead of reallocating memory

    void set(int b){
        *p=b;
    }

    int get(){
        return *p;
    }

    A& operator=(const A& other){
        p = other.p; // same here!
    }

    ~A(){
        delete p;
    }
};

When you're class deals with dynamically allocated memory, that's bad. Let's see what would happen in your main:

int main()
{
    // allocate a new pointer to int, let's call it p
    A obj;
    // set the content of p to 3
    obj.set(3);
    // allocate a new pointer to int, let's call it p1
    A obj1;
    // /!\
    // instead of copying the content of p to the content of p1, we're
    // actually doing p1 = p here! we're leaking memory AND the two
    // objects point on the same memory!
    obj1=obj;
    // set the content of p1 to 5, but p1 is now equal to p because of
    // the bad assignment, so we're also setting p's content to 5
    obj1.set(5);
    // print the content of p (5)
    cout << obj.get() << endl;
    // print the content of p1 (5)
    cout << obj1.get() << endl;
}
// delete the contents of p and p1 /!\ we're actually deleting the same
// allocated memory twice! that's bad

That's why you have to redefine the "big three" (copy constructor, copy assignment operator, and destructor)

Community
  • 1
  • 1
Caninonos
  • 1,214
  • 7
  • 12
  • in my program answer is 3 & 5. But when I delete (overloaded = ) operator then it show 5 & 5. –  Aug 02 '15 at 20:00
  • @Let Do it: when you replace `operator=`'s implementation with `delete p; p = new int; *p=*(k.p);`it shows 5 twice? really? are you sure you didn't modify something else? – Caninonos Aug 02 '15 at 20:03
  • Sorry I also remove destructor. but why it's showing 5 twice? pointer should be 2 and each pointer should point 2 different dynamic allocated memory. –  Aug 02 '15 at 20:06
  • @Let Do it: ah no wait i got it, if you remove `operator=`'s declaration (and definition) it shows 5 twice. Yes. That's perfectly normal. It's because when the assignment operator isn't defined the compiler generates one for you (the implicitly defined copy assignment operator) and this implicit operator is pretty dumb: it just copies every member. So when you execute `obj1=obj;` if you simply remove the copy assignment operator, it will replace `obj1.p` with `obj.p` instead of either assigning its content or reallocating it, then assigning its content like you did. – Caninonos Aug 02 '15 at 20:08
  • Great. Now About close. Thanks.. Update your answer so I will be ab to mark as best. and Just to confirm that... there are 2 pointer that point to 2 different dynamically allocated memories when constructor call for each object in my program.? –  Aug 02 '15 at 20:14
  • @Let Do it: yes, there are, i'll update my answer to summarize these comments if you want – Caninonos Aug 02 '15 at 20:29
  • one more... if I will not delete any memory and program exit. Then there could be any risk? Someone told me that if we will not deallocate the memory before program exit, then that memory will unusable until operator system reinstalled. –  Aug 02 '15 at 20:39
  • @Let Do it: Not releasing the memory when your program exits isn't clean, but don't worry, it won't affect your operating system that much, besides, you're actually releasing the memory when `A`'s destructor is called (`obj`and `obj1` are local variables whose lifetime is the scope of the function `main`, once `main` is executed, their destructor is called). Actually, the operating system creates a pool of virtual memory for your program, and cleans that pool when your program exits, so you will be able to reuse it without a reinstall. – Caninonos Aug 02 '15 at 20:47
  • However, avoid memory leaks, because if your program runs for a long time, that pool of virtual memory pool will grow and potentially prevent other programs from allocating the memory they would need (also slowing down the OS and your program in the meantime) – Caninonos Aug 02 '15 at 20:48
  • As you told me in 2nd last comment, `exits isn't clean,` But when I call delete then memory will also not clean. –  Aug 02 '15 at 20:52
  • 1
    Actually, when i say that "Not releasing the memory when your program exits isn't clean" i don't mean that the memory won't be cleaned (it will be by the OS), but that it's usually seen as bad practice (suppose you suddenly add code before the end of your program, and now, you have memory leaks). In any case, if you `delete` all the memory you have dynamically allocated with `new` once you know you don't need it anymore, then there's nothing more you could do and you won't have any memory leak. Also, memory leaks don't exist anymore (and don't harm your OS) once your program exited. – Caninonos Aug 02 '15 at 20:57
  • Just one more question, Someone told me that we should add `NULL` to pointer when we delete memory. Will this speed up or performance? or just save data leakage? –  Aug 02 '15 at 21:05
  • 1
    Small quibble: If `operator=` is written as `{delete p; p = new int; *p=*(k.p);}` then `p` will (probably) contain a dangling pointer if the call to `new` somehow fails. This will lead to problems when `~A()` is called, if not sooner. _If_ one wants to go down this path, `p` should be set to `NULL` before calling `new`. – Joshua Green Aug 02 '15 at 21:28
  • @Joshua Green actually, if i'm not mistaken, [that version of new (the first)](http://en.cppreference.com/w/cpp/memory/new/operator_new) throws instead of returning nullptr when a problem happens, so you'd have to use a try/catch block instead (or change it so that it returns a nullptr instead of throwing) otherwise, you're right – Caninonos Aug 02 '15 at 21:31
  • @Caninonos, I see nothing wrong with allowing such an exception to directly exit `operator=`; we just need to ensure that the `A` we're assigning to is left in a valid state. Setting `p` to `NULL` before calling `new` at least ensures that `~A()` can be called safely. This is one of the benefits of throwing-`new` -- we don't have to `catch` anything, as sending the exception to the caller is simply the right thing to do. – Joshua Green Aug 02 '15 at 21:40
  • @JoshuaGreen you're right my mistake, my last comment was stupid (besides, if that was the non-throwing new, it would be set to nullptr anyway), i'll edit my answer to include that suggestion – Caninonos Aug 02 '15 at 21:42
  • @Caninonos, though what you wrote above is accurate, it's not exactly what I was aiming for. Rather, I was thinking in terms of exception safety. If `operator=` throws an exception, the assigned-to object will be left in _some_ state. Normally an operation should at least provide the "weak/basic guarantee" that the object will be in some "valid" state. What that means isn't so well-defined, but a _minimum_ requirement is that one should be able safely to destroy it (e.g,, to allow exception propagation). If `p` contains a dangling pointer, one won't be able to safely call `~A()`. – Joshua Green Aug 03 '15 at 01:37
  • @JoshuaGreen, yes, i understand but i thought i could as well write something more general. The first place to check whether `p` is `nullptr` (if it can hold such a value) is obviously the destructor (as each object will ultimately be destroyed), but that doesn't mean it couldn't be checked in other methods (although i admit it would make very little sense, holding the allocated pointer in a temporary and deleting afterwards would have been a better choice if we wanted to reuse that object). Still, if you deem it necessary, i can change the wording to insist on the destructor's safety. – Caninonos Aug 03 '15 at 02:21
  • In the destructor, there's no way to determine if `p` has already been `delete`d. Nonetheless, I feel your wording above is fine and also covers an important point. – Joshua Green Aug 03 '15 at 23:20
  • @Caninonos Hi I'm back to ask just one question. In Term deep copy there should be allocation with new? or any copy that we do with explicitly `operator =` is called deep copy? As in your answer 2nd programming example there is line `// p = new int; // not needed` I remove this line in my program and it's work. but can I call it deep copy? –  Aug 19 '15 at 02:54
  • @LetDoIt If the copy of a composite implies the copy of its components it's a deep copy. That copy is trivial when it doesn't involve dynamic allocation, but it's still a deep copy (that's why I rarely talk about the deep copy for a simple struct, it's more relevant for big recursive structures like trees where a deep copy has its downsides). On the other hand, copying smart pointers such as `std::shared_ptr` doesn't perform a deep copy as it doesn't copy the pointed memory but share it across several wrappers. – Caninonos Aug 19 '15 at 13:38