Solved

constructor called twice, destructor called 3 times

Posted on 2014-02-13
10
329 Views
Last Modified: 2014-02-13
I set breakpoints on the constructor and destructor. The constructor is called 2 times. The destructor is called 3 times. What am I doing wrong (besides choosing C++ as a language to program in)?

ClassC.cpp line 17 below, can I do that return?

(My ClassD holds an int which I set and change to help me keep track of which class instance is which.)

ClassD.h
#pragma once
class ClassD
{
public:
  ClassD(int j);
  ~ClassD(void);
  void Test(int j);
private:
  int i;
};

Open in new window

ClassD.cpp
#include "StdAfx.h"
#include "ClassD.h"


ClassD::ClassD(int j) : i(j)
{
}


ClassD::~ClassD(void)
{
  int j = i;
}


void ClassD::Test(int j)
{
  i = j;
}

Open in new window

ClassC.h
#pragma once
#include "ClassA.h"
#include "ClassD.h"

class ClassC : public ClassA
{
public:
  ClassC(void);
  ~ClassC(void);
  ClassD ReturnClassInstance(void);
};

Open in new window

ClassC.cpp
#include "StdAfx.h"
#include "ClassC.h"
#include "ClassD.h"

ClassC::ClassC(void)
{
}

ClassC::~ClassC(void)
{
}

ClassD ClassC::ReturnClassInstance(void)
{
  ClassD dd(10);
  dd.Test(3);
  return dd;  //can I do this?
}

Open in new window

main code
#include "stdafx.h"
#include "ClassC.h"
#include "ClassD.h"

int _tmain(int argc, _TCHAR* argv[])
{
  ClassC c;
  ClassD d(5);

  d.Test(2);
  d = c.ReturnClassInstance();
  d.Test(7);

  return 0;
}

Open in new window

0
Comment
Question by:deleyd
  • 4
  • 4
  • 2
10 Comments
 
LVL 86

Accepted Solution

by:
jkr earned 400 total points
ID: 39856592
This is because you aren't providing a cusom copy constructor, but are relying on the compiler-generated one, which you won't 'see' (or encountern) when debugging your code. The copy ctor is invoked implicitly when you call 'ReturnClassInstance()'. You can see the correct behaviour when creating your own copy constructor, e.g.

#pragma once
class ClassD
{
public:
  ClassD(int j);
  ClassD(const ClassD& r); // copy constructor
  ~ClassD(void);
  void Test(int j);
private:
  int i;
};
                                  

Open in new window

#include "StdAfx.h"
#include "ClassD.h"


ClassD::ClassD(int j) : i(j)
{
}

ClassD::ClassD(const ClassD& r)  // copy constructor
{
  i = r.i;
}

ClassD::~ClassD(void)
{
  int j = i;
}


void ClassD::Test(int j)
{
  i = j;
}
                                  

Open in new window

0
 
LVL 86

Expert Comment

by:jkr
ID: 39856608
BTW, to elaborate: Usually, a copy constructor (http://en.wikipedia.org/wiki/Copy_constructor) is not necessary as in simple cases as yours, only in more complicated setups where complex members and/or pointers are involved. Nevertheless, the compiler will create one implicitly for temporary objects, as in your case. And the destructors for these temp. objects will of course be invoked automatically, which leads to observations like theone you have seen, since you are explicitly providing a destructor - which, as well, is not necessary in your case, since there is nothing to explicitly deallocate or clean up.
0
 
LVL 32

Expert Comment

by:sarabande
ID: 39856622
in the main function you create a ClassD d and the constructor was called.

in function ClassC::ReturnClassInstance you create dd an instance of ClassD where the constructor was called.

At the return statement you return the dd, so the compiler creates a copy of the dd. as you don't have implemented a copy constructor the compiler created a default copy constructor which you couldn't step into as there is no source code available. when the scope of the function was left the destructor was called first time for the local dd.

in main function after return both d and the temporary copy of the dd were deleted, so the destructor was called two times.

Sara
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 400 total points
ID: 39856627
I'm getting old - also important in this context: The assignment operator - see http://en.wikipedia.org/wiki/Assignment_operator_in_C%2B%2B

This one basically would be quite similar like

#pragma once
class ClassD
{
public:
  ClassD(int j);
  ClassD(const ClassD& r); // copy constructor
  ClassD& operator=(const ClassD& r); // assignment operator
  ~ClassD(void);
  void Test(int j);
private:
  int i;
};
                                  
                          

Open in new window


#include "StdAfx.h"
#include "ClassD.h"


ClassD::ClassD(int j) : i(j)
{
}

ClassD::ClassD(const ClassD& r)  // copy constructor
{
  i = r.i;
}

ClassD& ClassD::operator=(const ClassD& r)  // assignment operator
{
  i = r.i;

 return *this; // return reference to self
}
ClassD::~ClassD(void)
{
  int j = i;
}


void ClassD::Test(int j)
{
  i = j;
}
                                  
                          

Open in new window

0
 
LVL 32

Expert Comment

by:sarabande
ID: 39856653
sorry. I didn't refresh before posting.

see my comment as an add-on to the explanations jkr has made.

Sara
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 32

Assisted Solution

by:sarabande
sarabande earned 100 total points
ID: 39856784
to add:

the copy constructor was called when the dd was returned in ReturnClassInstance.

the assignment operator was called when the return value of ReturnClassInstance was assigned to the already constructed d object.

note, if you would have a statement like

ClassD ddd = c.ReturnClassInstance(); 

Open in new window


the copy constructor would be called and not the operator=. that is different to your case.

Usually, a copy constructor <...> is not necessary as in simple cases as yours, only in more complicated setups where complex members and/or pointers are involved.


Actually, a copy constructor and an operator= should only be added if pointers are involved. complex members should/must have a proper copy constructor/operator= themselves and therefore a default member-wise copy of your class would call them automatically. providing your own copy functionality if not necessary is error-prone as you have to care for them whenever you do a change. on the other hand, if there are member pointers or member containers with pointers you have to provide a copy functionality, as the default implementation would only copy the pointer's value, the address of the object and not the object itself (flat copy). that is bad as you now have two instances using the same pointer and none of them could delete it without making it invalid for the other. because of that you should avoid pointer members. if that is not possible, you also could prevent your class being copied at all. that could be done by providing a private copy constructor and a private operator=.

class ClassD
{
private:
     ClassD(const ClassD &) { }
     ClassD & operator=(const ClassD &) { return this; }
     ...

Open in new window

then, the compiler would complain for each attempt to make a copy of your class from a global function or a non-member function of ClassD.

Sara
0
 

Author Comment

by:deleyd
ID: 39856936
Sara line 5
ClassD & operator=(const ClassD &) { return this; }

Open in new window

would that be return *this; ?

Oh, and is there a way in Visual Studio to get the address of a variable such as dd holding a class?
0
 
LVL 86

Expert Comment

by:jkr
ID: 39856961
>>would that be return *this; ?

Yes,

ClassD & operator=(const ClassD &) { return this; }
                                            

Open in new window


would just result in a compile error.

>>Oh, and is there a way in Visual Studio to get the address of a variable such as dd
>>holding a class?

Since 'dd' is a local variable, it is located on the stack, so the actual address won't help much. The address will not be valid any more after 'ReturnClassInstance()' exits, a copy is created that is placed on the stack of the calling function, in your case 'main()'.
0
 

Author Closing Comment

by:deleyd
ID: 39856988
I think I understand what's happening now.

ClassD d(5); //create 1st instance

Then a lot happens at this line:
d = c.ReturnClassInstance();

  ClassD dd(10);   //create 2nd instance
  dd.Test(3);
  return dd;    //copy constructor creates 3rd instance

3rd instance returned. 2nd instance deleted when dd goes out of scope on return.

Then the assignment:
d = c.ReturnClassInstance();

3rd instance copied to d (assignment operator =)

3rd instance deleted.

and we're finally done with that call.

d deleted on program exit

(I think to prevent class not being copied at all, in addition to making the copy constructor and = operator private, also don't include an implementation for either, so compiler with catch if class itself tries to make a copy.)
0
 
LVL 32

Expert Comment

by:sarabande
ID: 39858367
I think to prevent class not being copied at all, in addition to making the copy constructor and = operator private, also don't include an implementation for either, so compiler with catch if class itself tries to make a copy
you could add an assertion into the body of the implementation such that an accidental call of the copy functionality from a member function could be detected. but you are right, if you do just nothing in the copy constructor beside of initializing all members with defaults, the copy is just empty and could cause much less harm as it would do by copying pointers.

Sara
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now