Solved

constructor called twice, destructor called 3 times

Posted on 2014-02-13
10
344 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
What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

 
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
 
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

Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
computer science syllabus 3 83
Unable to start eclipse ? 17 141
Header of docx file 17 103
Safe conversion? 4 67
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

773 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