destructor invoked without constructor.

S

sam_cit

Hi Everyone,

I have the following unit to explain the problem that i have,

class sample
{
public : sample()
{
printf("in sample...\n");
}
~sample()
{
printf("out sample...\n");
}
void invoke(sample obj)
{
printf("in invoke...\n");
}
};

int main()

{

sample obj;
sample obj1;
obj1.invoke(obj);
printf("...end...\n");
return 0;

}


The following is the output,

in sample...
in sample...
in invoke...
out sample...
....end...
out sample...
out sample...

i think the first "out sample..." is for the object obj passed by
value, which indicates that a new temp object has been destroyed,
which means that it should have been created, if so, shouldn't the "in
sample" have been printed once more before "in invoke"...?
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

Hi Everyone,

I have the following unit to explain the problem that i have,

class sample
{
public : sample()
{
printf("in sample...\n");
}
~sample()
{
printf("out sample...\n");
}
void invoke(sample obj)
{
printf("in invoke...\n");
}
};

int main()

{

sample obj;
sample obj1;
obj1.invoke(obj);
printf("...end...\n");
return 0;

}

The following is the output,

in sample...
in sample...
in invoke...
out sample...
...end...
out sample...
out sample...

i think the first "out sample..." is for the object obj passed by
value, which indicates that a new temp object has been destroyed,
which means that it should have been created, if so, shouldn't the "in
sample" have been printed once more before "in invoke"...?

It is created, but using the copy-constructor, which you have not
implemented so the compiler-generated one is used, and that one does
not print anything. Add

sample(const sample&) { std::cout << "copy\n"; }

to your class and see what happens.
 
S

Sylvester Hesp

Hi Everyone,

I have the following unit to explain the problem that i have,

class sample
{
public : sample()
{
printf("in sample...\n");
}
~sample()
{
printf("out sample...\n");
}
void invoke(sample obj)
{
printf("in invoke...\n");
}
};

int main()

{

sample obj;
sample obj1;
obj1.invoke(obj);
printf("...end...\n");
return 0;

}


The following is the output,

in sample...
in sample...
in invoke...
out sample...
...end...
out sample...
out sample...

i think the first "out sample..." is for the object obj passed by
value, which indicates that a new temp object has been destroyed,
which means that it should have been created, if so, shouldn't the "in
sample" have been printed once more before "in invoke"...?

No. The temporary is created using the copy constructor, and since you
didn't supply any it was automatically generated (and obviously not with the
printf statement in it). Add the following constructor and it should work as
you expect:

sample(const sample & s)
{
printf("in sample... (copy ctor)\n");
}

Btw, it might be handy to output the this pointer as well so you can exactly
see what ctor-dtor pairs belong to eachother :)

- Sylvester
 
W

WittyGuy

Hi Everyone,

I have the following unit to explain the problem that i have,

class sample
{
public : sample()
{
printf("in sample...\n");
}
~sample()
{
printf("out sample...\n");
}
void invoke(sample obj)
{
printf("in invoke...\n");
}
};

int main()

{

sample obj;
sample obj1;
obj1.invoke(obj);
printf("...end...\n");
return 0;

}

The following is the output,

in sample...
in sample...
in invoke...
out sample...
...end...
out sample...
out sample...

i think the first "out sample..." is for the object obj passed by
value, which indicates that a new temp object has been destroyed,
which means that it should have been created, if so, shouldn't the "in
sample" have been printed once more before "in invoke"...?

Invoking a function with pass by value argument would definitely call
copy constructor, whereas pass the argument by reference wouldn't
create an object.
So If you would've defined a copy constructor for the class sample,
within which a print statement your doubt would've got cleared.

I hope this helps.

-
Sukumar R
 
S

sam_cit

Invoking a function with pass by value argument would definitely call
copy constructor, whereas pass the argument by reference wouldn't
create an object.
So If you would've defined a copy constructor for the class sample,
within which a print statement your doubt would've got cleared.

I hope this helps.

-
Sukumar R- Hide quoted text -

- Show quoted text -

So it is correct to understand that the default copy constructor would
be invoked in all calls to member functions where the object of the
same class's type is passed by value?
....

Thanks in advance!!!
 
S

sam_cit

It is created, but using the copy-constructor, which you have not
implemented so the compiler-generated one is used, and that one does
not print anything. Add

sample(const sample&) { std::cout << "copy\n"; }

to your class and see what happens.

And what is the reason that only copy constructor is invoked in this
case and why not the regular constructor?
 
G

Gianni Mariani

So it is correct to understand that the default copy constructor would
be invoked in all calls to member functions where the object of the
same class's type is passed by value?

Remove the word "default" and then you are correct.

The compiler provides a "default" copy constructor (if one is needed).
The programmer can provide a copy constructor, in which case the
compiler does not create a "default" one. Either way, if you pass by
value, the copy constructor is invoked.
 
G

Gianni Mariani

And what is the reason that only copy constructor is invoked in this
case and why not the regular constructor?

Because it is defined that way in the standard. Passing by value by
definition requires the compiler to (appear to) create a temporary copy
of the object being passed. New copies of objects are created using the
copy constructor, almost by definition.
 
S

Sylvester Hesp

And what is the reason that only copy constructor is invoked in this
case and why not the regular constructor?

Because you're creating a copy, obviously. That's what the copy constructor
is for. You're not creating a new object using no arguments. How else would
this work:


#include <iostream>

struct sample
{
int i;

sample() : i(0) { }
sample(int _i) : i(_i) { }
};

void foo(sample s)
{
std::cout << s.i << std::endl;
}

int main()
{
sample s(23);
foo(s);
}

If the temporary passed to foo() were to be default-constructed, you are
never able to pass the value 23 to foo. The copy-ctor is invoked to
construct a new sample that is a copy from the sample passed to foo(), so
you want the value 23 to be copied. This is the responsibility from the copy
constructor.

- Sylvester Hesp
 
S

sam_cit

Because you're creating a copy, obviously. That's what the copy constructor
is for. You're not creating a new object using no arguments. How else would
this work:

#include <iostream>

struct sample
{
int i;

sample() : i(0) { }
sample(int _i) : i(_i) { }

};

void foo(sample s)
{
std::cout << s.i << std::endl;

}

int main()
{
sample s(23);
foo(s);

}

If the temporary passed to foo() were to be default-constructed, you are
never able to pass the value 23 to foo. The copy-ctor is invoked to
construct a new sample that is a copy from the sample passed to foo(), so
you want the value 23 to be copied. This is the responsibility from the copy
constructor.

- Sylvester Hesp

Thanks you very much. I understand the purpose of copy constructors
being used i would assume they are invoked for the following case too,

foo(s(23));

isn't it?

I also tried to define a constructor like

sample(sample obj) // i get an error saying first parameter
shouldn't be of type sample but the following works fine
sample(int i, sample obj)

why doesn't the first work? I pass an already existing object to the
constructor, a new temp object would be created with the help of copy
constructor and i'm using its value to create my object, what is wrong
in this scenario?

Thanks in advance!!!
 
S

Sylvester Hesp

Thanks you very much. I understand the purpose of copy constructors
being used i would assume they are invoked for the following case too,

foo(s(23));

isn't it?

I also tried to define a constructor like

sample(sample obj) // i get an error saying first parameter
shouldn't be of type sample but the following works fine
sample(int i, sample obj)

why doesn't the first work?

Because the standard says so:

12.8/3: A declaration of a constructor for a class X is ill-formed if its
first parameter is of type (optionally cv-qualified) X and either there are
no other parameters or else all other parameters have default arguments.

I don't know the exact reasons, but I can imagine some: first of all, it
can't be a copy ctor because the value would need to be copied itself,
resulting in an infinite recursion. If it isn't a copy ctor, that means
there exists a (auto-generated) copy ctor that is compatible with the same
parameter list, causing a call to either of the constructors to be
ambiguous, just as the following call is ambiguous:

void foo(int);
void foo(const int &);

int main()
{
foo(3); // error, call to foo() is ambiguous
}

Which effectively means that such an object can never be copy constructed.

- Sylvester Hesp
 
S

sam_cit

Because the standard says so:

12.8/3: A declaration of a constructor for a class X is ill-formed if its
first parameter is of type (optionally cv-qualified) X and either there are
no other parameters or else all other parameters have default arguments.

I don't know the exact reasons, but I can imagine some: first of all, it
can't be a copy ctor because the value would need to be copied itself,
resulting in an infinite recursion. If it isn't a copy ctor, that means
there exists a (auto-generated) copy ctor that is compatible with the same
parameter list, causing a call to either of the constructors to be
ambiguous, just as the following call is ambiguous:

void foo(int);
void foo(const int &);

int main()
{
foo(3); // error, call to foo() is ambiguous

}

Which effectively means that such an object can never be copy constructed.

- Sylvester Hesp

Ok so you mean to say that the default copy constructor's prototype
would be

sample(sample&);

and my constructor's prototype would be

sample(sample)

and hence the compiler wouldn't know which call to resolve?
can you explain the recurssion part of it with an example?

Thanks in advance!!!
 
G

Gavin Deane

Ok so you mean to say that the default copy constructor's prototype
would be

You might want to consider dropping the term "default copy
constructor" to avoid the risk of confusion with the "default
constructor". In the context of constructors, "default" has a
precisely defined meaning which is not applicable here. "implicitly-
declared copy constructor" might be better (that appears to be what
the language standard uses). "Compiler-generated copy constructor" is
another fairly popular and uncontroversial name for it. But "default
copy constructor" should probably be avoided.
sample(sample&);

and my constructor's prototype would be

sample(sample)

and hence the compiler wouldn't know which call to resolve?

Nearly. Generally, the implicitly-generated copy constructor has the
signature sample(const sample&). Only if a base class or member of
sample does *not* have the const in the signature of its own copy
constructor, will the signature of sample's copy constructor be
sample(sampl&) as you wrote.
can you explain the recurssion part of it with an example?

Think about it. Whenever an object is passed to a function by value,
the copy constructor is needed to initialise the function parameters.

void foo(T t1, T t2) {}

int main()
{
T a, b;
foo(a, b);
}

In the line foo(a, b) a and b (which are objects of type T) are passed
to function foo and used to initialise foo's parameters t1 and t2
respectively. t1 is a copy of a. t2 is a copy of b. Surprise, surprise
- to create t1 and t2 as copies of a and b, the copy constructor of
class T is used. That's how you make copies of objects.

Now what actually happens when the copy constructor of class T is
used? Imagine class T's copy constructor looks like this

T(const T& src);

and we are using that copy constructor to construct t1 from the source
object, a. t1 is the object under construction, and src is a reference
to the source object, a. That's important. src is not an object. It is
just a reference to a, an object that already exists. Whatever the
body of the copy constructor needs to do to initialise the internals
of the new object t1, it can do, because it can get at the internals
of the source object, a, through the reference, src.

Now, imagine we tried to use your signature for the copy constructor
here

T(T src);

This time src is an object, so in the process of calling this copy
constructor, the object src needs to be constructed. We are trying to
copy-construct t1 as a copy of a. But before we can start doing
whatever is in the body of the copy constructor, we need to construct
src, the parameter of the copy constructor. How do we construct src?
Well it needs to be a copy of a so I guess we need to use the copy
constructor.

That's the recursion. The very first thing that happens when we try
and use the copy constructor (to create t1 from a) is that we need to
use the copy constructor (to create the copy constructor's own
parameter src from a). But the very first thing that happens when we
try and use the copy constructor (this time to construct src from a)
is that we need to use the copy constructor .... and so, ad infinitum.
Put another way, the conversation goes like:

Me: "I need to know how to copy objects of type T because I have to
make t1 as a copy of a".
You: "OK. First of all, create yourself an object of type T called src
by copying a".
Me: "But I don't know how to copy objects of type T. That's what I'm
asking you".

Gavin Deane
 
J

James Kanze

Which seems a rather obvious reason. If pass by value invokes
the copy constructor (which it does), then the copy constructor
itself obviously cannot take its parameter by value.
Ok so you mean to say that the default copy constructor's prototype
would be
sample(sample&);

The usual prototype is "sample( sample const& )". You do want
to be able to copy temporaries and const objects, don't you?
and my constructor's prototype would be

and hence the compiler wouldn't know which call to resolve?
can you explain the recurssion part of it with an example?

Basically, if "sample( sample )" were a legal copy constructor,
to call it you'd first have to call it to pass the argument.

I'd strongly suggest you buy a copy of Scott Meyers' "Effective
C++", and study it. It covers all of these issues in a very
easy to understand way.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top