Copy constructor with pointers
Asked Answered
E

10

21

I have recently discovered that when I have pointers within a class, I need to specify a Copy constructor.

To learn that, I have made the following simple code. It compiles, but gives me runtime error when performing the copy constructor.

I am trying to copy just the value from the pointer of the copied object, but avoiding assigning the same address.

So, what's wrong here?

    class TRY{
        public:
        TRY();
    ~TRY();
        TRY(TRY const &);

        int *pointer;

        void setPointer(int);
    };


    void TRY::setPointer(int a){
        *pointer = a;

        return;
    }


    TRY::TRY(){}


    TRY::~TRY(){}


    TRY::TRY(TRY const & copyTRY){
        int a = *copyTRY.pointer;
        *pointer = a;
    }



    int main(){

        TRY a;
        a.setPointer(5);

        TRY b = a;

        b.setPointer(8);

        cout << "Address of object a = " << &a << endl;
        cout << "Address of object b = " << &b << endl;

        cout << "Address of a.pointer = " << a.pointer << endl;
        cout << "Address of b.pointer = " << b.pointer << endl;

        cout << "Value in a.pointer = " << *a.pointer << endl;
        cout << "Value in b.pointer = " << *b.pointer << endl;

        return 0;
    }

I'll be using this concept for other classes with lots of pointers in it, where I need to copy all values from on object to the other. Copying is initially necessary for this code, so I would like to keep the copying possibility (I won't be hiding the copy constructor as private).

Besides, the real class I need to implement has like 10 pointers, and it might be changing with time. Isn't there a somewhat smarter way to have a deep copy constructor in C++?...

Emblazonment answered 23/4, 2009 at 13:39 Comment(0)
C
23

With the statement int* pointer you have just defined a pointer but has not allocated any memory. First you should make it point to a proper memory location by allocating some memory like this: int* pointer = new int. Then in the copy constructor again you have to allocate the memory for the copied object. Also, don't forget to release the memory using delete in the destructor.

I hope this example helps:

class B
{

public:
    B();
    B(const B& b);
    ~B();
    void setVal(int val);

private:
    int* m_p;
};

B::B() 
{
    //Allocate the memory to hold an int
    m_p = new int;

    *m_p = 0;
}

B::B(const B& b)
{
    //Allocate the memory first
    m_p = new int;

    //Then copy the value from the passed object
    *m_p = *b.m_p;
}

B::~B()
{

    //Release the memory allocated
    delete m_p;
    m_p = NULL;
}

void B::setVal(int val)
{
    *m_p = val;
}
Cummerbund answered 23/4, 2009 at 13:47 Comment(9)
Don't forget to delete m_p before allocating a new object for it.Durst
I have not defined the assignment operator, it is just a copy constructor. Hence there is no need to delete m_p.Cummerbund
In a constructor, m_p will initially be undefined unless you explicitly put it into the initializer list. If you try to delete an undefined pointer, bad things will happen.Count
Why not? (I mean in the destructor?)Emblazonment
There is already a destructor and the memory has been released properly.Cummerbund
Even if I have pointers in the class that have been allocated?Emblazonment
how would the overloaded assignment operator look for this B class?Krasnoff
There should always be a NULL pointer check. What if memory allocation failed and new returns NULL.Chansoo
@arjunjawalkar: If new fails, we get bad_alloc exception and not NULL pointers.Cummerbund
C
11

I have recently discovered that when I have pointers within a class, I need to specify a Copy constructor.

It is not completely true. When you have pointers in your class and allocate the memory using new then you have to worry about copy constructor. Also, don't forget the assignment operator and destructor. You have to delete the memory allocated using delete.

It's called Law Of The Big Three.

Example:

  ~Matrix();  //Destructor
  Matrix(const Matrix& m); //Copy constructor
  Matrix& operator= (const Matrix& m); //Assignment operator
Causative answered 23/4, 2009 at 14:7 Comment(0)
E
3

If you want to do a deep copy, you of course must also allocate new memory to hold the values. If the original has a pointer to an int, and you don't want the copy to use the same pointer value, you must allocate new memory to hold an int, and then copy the value there.

Your example is not very clear, it doesn't show the implementation of your copy constructor, or how the pointer member gets initialized.

Enkindle answered 23/4, 2009 at 13:48 Comment(0)
F
2

if it has a pointer to a regular type then

A::A(const A& a):
  pointer_( new int( *a.pointer_ ) )
{
}

if it has a pointer to some base class then

A::A(const &a ):
  pointer_( a.pointer_->clone() )
{
}

Clone is a implementation of a prototype pattern

Don't forget to delete the pointer in the destructor

A::~A()
{
    delete pointer_;
}

To fix your example

TRY::TRY(TRY const & copyTRY){
    int a = *copyTRY.pointer;
    pointer = new int(a);
}
Franke answered 23/4, 2009 at 13:46 Comment(0)
W
2

I have recently discovered that when I have pointers within a class, I need to specify a Copy constructor

More often than not it is a good idea to simply disable it by declaring it (and the assigment operator) private and not implementing it.

Waldo answered 23/4, 2009 at 14:8 Comment(2)
Why that? Is copying something conceptually "forbidden" in programming, or is it because copying may lead to trouble in OO languages?Emblazonment
It is not "forbidden" - it just often makes no sense and/or is expensive.Waldo
H
1

Your problem is in this line right here:

    *pointer = a;

All the stuff that normally happens in your default constructor hasn't happened yet, including the allocation of memory for *pointer.

The fix is to allocate memory for an integer. You can use malloc and friends or new for this, but make sure it's the same method you use in your default constructor, because you only get one destructor, and the calls have to match.

Heifetz answered 23/4, 2009 at 13:52 Comment(0)
F
1

If a member-wise (shallow) copy is okay, then you don't have to do anything. If you want a deep copy, you have to allocate new storage for copies of all the members.

Fruitcake answered 23/4, 2009 at 13:54 Comment(0)
D
0

When writing a Copy Constructor, you should allocate memory for all members. In your case:

TRY::TRY(TRY const & copyTRY){
    pointer = new int(*(copyTry.pointer));
}

Operator= is somehow similar, but with no memory allocation.

TRY& operator=(TRY const& otherTRY){
      this->a  = *(otherTry.pointer)
      return *this
}
Decrease answered 23/4, 2009 at 14:5 Comment(0)
W
-1

here is the example of a copy constructor

class Test
{​​​​​​​
private:
    int *s;
    int size;
public:
    Test(int a, int b);
    Test(const Test&t);
    ~Test();
    void setValue(int l);
    void getValues();
}​​​​​​​;


Test::Test(int a, int b)
{​​​​​​​
    s = new int;
    *s = a;
    this->size = b;
}​​​​​​​
Test::Test(const Test&t) {​​​​​​​
    s = new int;
    *s = *(t.s);
    this->size = t.size;
}​​​​​​​
void Test::setValue(int l) {​​​​​​​
    *s = l;
}​​​​​​​
void Test::getValues() {​​​​​​​
    cout << "value of s: " << *s << endl;
    cout << "value of size: " << this->size << endl;
}​​​​​​​


Test::~Test() {​​​​​​​
    cout << "memory de allocated!!!" << endl;
    delete s;
}​​​​​​​
Wideangle answered 26/6, 2021 at 4:6 Comment(1)
A copy constructor does a copy; the semantics of whether it is a "deep" or "shallow" copy is up to the implementer. A pointer data-member does not necessitate or require a custom copy-constructor, and a shallow copy does not mean undefined behavior will occur unless it is holding an owning pointer -- so this answer is not accurate. Many types simply view objects through pointers, and these don't require custom copy-constructor logic. Just think of implementing span, string_view, reference_wrapper, etc.Sphericity
M
-4

More often than not, if YOU need to write a copy constructor or assignment operator you're doing something wrong. Leave the copy constructors and assignment operators to the implementers of the standard library. Compose your classes of already-copyable and assignable elements and you won't have to write your own.

For example, maybe that int * member should be a std::vector instead.

If you can't make the class default copyable/assignable, maybe you can make it non-copyable/assignable by declaring, but not implementing, a private copy constructor and assignment operator.

Only if none of the above are feasible should you implement your own copy constructor or assignment operator.

Marcelina answered 23/4, 2009 at 17:17 Comment(1)
OMG please steer away from programming and definitely stop giving advices of any kind on the matter, you clearly know nothingCrossness

© 2022 - 2024 — McMap. All rights reserved.