Solved

constructor called twice, destructor called 3 times

Posted on 2014-02-13
10
334 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 33

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 33

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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 33

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 33

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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

863 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

24 Experts available now in Live!

Get 1:1 Help Now