Calling one constructor from another in VC++

  • Thread starter Peter E. Granger
  • Start date
P

Peter E. Granger

I'm fairly new to C++ and VC++, but for the most part it seems to do most of
the same things that can be done in Java, with just some syntactic and
structural adjustments. However, one thing I haven't been able to figure out
is how to call one constructor from another within a class. It's easy enough
to call the base class's constructor from the derived class, but that's not
what I'm trying to do.

For example, in Java (or J#) it's easy to do this:

public class Circle
{
private int PointX;
private int PointY;
private int Radius;

public Circle()
{
this.PointX = 0;
this.PointY = 0;
this.Radius = 0;
}

public Circle(int x, int y)
{
this();
this.PointX = x;
this.PointY = y;
}

public Circle(int x, int y, int r)
{
this(x, y);
this.Radius = r;
}
}

However, trying to do the same thing in VC++ doesn't work:

// Sphere.h (partial file)
class Sphere
{
public:
Sphere(void);
Sphere(int x, int y);
Sphere(int x, int y, int r);
~Sphere(void);

private:
int PointX;
int PointY;
int Radius;
};

// Circle.cpp (partial file)
#include ".\circle.h"

Circle::Circle(void)
{
this->PointX = 0;
this->PointY = 0;
this->Radius = 0;
}

Circle::Circle(int x, int y)
{
Circle();
this->PointX = x;
this->PointY = y;
}

Circle::Circle(int x, int y, int r)
{
Circle(x, y);
this->Radius = r;
}

In the C++ version, I also tried using this() instead of Circle(), which
caused a compiler error. I also tried doing it without the this-> specifier,
which made no difference. The end result if you call the second constructor
is that the X and Y values have been set, and the Radius is uninitialized.
If you use the third constructor, you get exactly the opposite results. (And
I don't mean they have values of zero; looking at them with the debugger, I
see values of -842150451 for the uninitialized members.)

So is there any way in C++ to let one constructor build on another like
this?

Thanks for any help.
 
C

Carl Daniel [VC++ MVP]

Peter said:
I'm fairly new to C++ and VC++, but for the most part it seems to do
most of the same things that can be done in Java, with just some
syntactic and structural adjustments.

Actually, there are many things you can do in C++ that have no counterpart
in Java at all. You'll discover them in time.
However, one thing I haven't
been able to figure out is how to call one constructor from another
within a class. It's easy enough to call the base class's constructor
from the derived class, but that's not what I'm trying to do.

There's no way to do this in C++. Unlike Java, in C++ at the start of the
first statement of a constructor's body all of the members have been
initialized (their constructors have been run if they have them). In Java
everything gets default initialized before the constructor body is run, and
then the constructor body (perhaps redundantly) initializes everything.

To get the same "constructor re-use" effect in C++, you have to put the
initialization code into a (private/protected) member function and call it
from the constructor(s).

-cd
 
D

Doug Harrison [MVP]

Peter said:
I'm fairly new to C++ and VC++, but for the most part it seems to do most of
the same things that can be done in Java, with just some syntactic and
structural adjustments. However, one thing I haven't been able to figure out
is how to call one constructor from another within a class. It's easy enough
to call the base class's constructor from the derived class, but that's not
what I'm trying to do.

Circle::Circle(int x, int y)
{
Circle();

The statement above creates a temporary Circle object, which is immediately
destroyed at the end of the statement.
this->PointX = x;
this->PointY = y;
}

Circle::Circle(int x, int y, int r)
{
Circle(x, y);

The statement above creates a temporary Circle object, which is immediately
destroyed at the end of the statement.
this->Radius = r;
}

In the C++ version, I also tried using this() instead of Circle(), which
caused a compiler error. I also tried doing it without the this-> specifier,
which made no difference. The end result if you call the second constructor
is that the X and Y values have been set, and the Radius is uninitialized.
If you use the third constructor, you get exactly the opposite results. (And
I don't mean they have values of zero; looking at them with the debugger, I
see values of -842150451 for the uninitialized members.)

So is there any way in C++ to let one constructor build on another like
this?

Sorry, no. Sometimes you can factor the common initialization out into a
separate init() function, but of course members you don't mention in the
member-initialization list will have been default-constructed by the time
you can call init(). If you have const members, reference members, or
members that don't have default ctors, you must initialize them in each
ctor's member initialization list.
 
P

Peter E. Granger

Doug Harrison said:
The statement above creates a temporary Circle object, which is immediately
destroyed at the end of the statement.

Right -- that's what I expected would happen. In fact, I thought I'd said
that in my original message, but I see I omitted it.
Sorry, no. Sometimes you can factor the common initialization out into a
separate init() function, but of course members you don't mention in the
member-initialization list will have been default-constructed by the time
you can call init(). If you have const members, reference members, or
members that don't have default ctors, you must initialize them in each
ctor's member initialization list.

That's unfortunate. It's really a handy feature. But you can't get
everything in one, language, I guess.

Thanks for the reply.

- Peter
 
P

Peter E. Granger

Carl Daniel said:
Actually, there are many things you can do in C++ that have no counterpart
in Java at all. You'll discover them in time.

And vice-versa, presumably (like this case).
There's no way to do this in C++. Unlike Java, in C++ at the start of the
first statement of a constructor's body all of the members have been
initialized (their constructors have been run if they have them). In Java
everything gets default initialized before the constructor body is run, and
then the constructor body (perhaps redundantly) initializes everything.

To get the same "constructor re-use" effect in C++, you have to put the
initialization code into a (private/protected) member function and call it
from the constructor(s).

Thanks, I figured I might end up having to do that. Bit of a nuisance, but
manageable.

Thanks for the reply.

- Peter
 
H

Hendrik Schober

Peter E. Granger said:
[...]
Actually, there are many things you can do in C++ that have no counterpart
in Java at all. You'll discover them in time.

And vice-versa, presumably (like this case).

I wouldn't expect that much, though.
[...]
To get the same "constructor re-use" effect in C++, you have to put the
initialization code into a (private/protected) member function and call it
from the constructor(s).

Thanks, I figured I might end up having to do that. Bit of a nuisance, but
manageable.

Note that C++ has default parameters which,
AFAIK, where introduced especially to solve
this problem:

class Circle {
public:
Circle(int x=0, int y=0, int r=0)
: PointX(x), PointY(y), Radius(r)
{
}
// ...
};
Thanks for the reply.
HTH,

- Peter


Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
M

Mihajlo Cvetanovic

Peter said:
And vice-versa, presumably (like this case).

There is something called "placement new" which in this context
behaves exactly like calling one c-tor from another, but programmers
are discouraged from using it.
 
K

Ken Alverson

Hendrik Schober said:
Note that C++ has default parameters which,
AFAIK, where introduced especially to solve
this problem:

Default parameters were introduced because, at the time, there was no such
thing as method overloading. They just happen to also solve some of the
simpler cases of redundant constructors.

Ken
 
K

Ken Alverson

Mihajlo Cvetanovic said:
There is something called "placement new" which in this context
behaves exactly like calling one c-tor from another, but programmers
are discouraged from using it.

Placement new is only designed to be used on uninitialized memory. By the
time you get to the first line of a constructor, you have a semi-valid object
in that memory that would be wiped out.

Ken
 
H

Hendrik Schober

Ken Alverson said:
Default parameters were introduced because, at the time, there was no such
thing as method overloading. They just happen to also solve some of the
simpler cases of redundant constructors.

"The original motivation for default parameters
was exactly because a ctor cannot call another
ctor for the same type." (Francis Glassborow)
www.google.de/groups?selm=pM5fflAM5%24b2Ewsj%40robinton.demon.co.uk
I don't have D&E handy here, but I believe that
function overloading was among the very first
features of C++. Wasn't there even an 'overload'
keyword (considered?) ones but later dismissed
because it wasn't needed?

OTOH, I know the first one only from Francis'
posting and the second only from my memory, so
i might be wrong.

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
K

Ken Alverson

Hendrik Schober said:
"The original motivation for default parameters
was exactly because a ctor cannot call another
ctor for the same type." (Francis Glassborow)
www.google.de/groups?selm=pM5fflAM5%24b2Ewsj%40robinton.demon.co.uk
I don't have D&E handy here, but I believe that
function overloading was among the very first
features of C++. Wasn't there even an 'overload'
keyword (considered?) ones but later dismissed
because it wasn't needed?

OTOH, I know the first one only from Francis'
posting and the second only from my memory, so
i might be wrong.

I could be wrong, and I'm more than willing to admit if I am, but I remember
pretty clearly reading somewhere that the reason we still had default
parameters was backward compatibility with pre-overload code. If overloads
had existed from the get-go, it was argued that default parameters would never
have been created. I'll see if I can find where I read that and see if it has
any credibility.

Ken
 
A

Arnold the Aardvark

This is something that kinda sorta might work, but it's horribly
inefficient:
Circle::Circle(int x, int y, int r)
{

//> Circle(x, y);

Circle temp(x, y);
std::swap(*this, temp); // temp now contains our unitialised values
// and will quietly disappear at the end of
// this scope.
this->Radius = r;
}

I don't recommend this at all. It is cheaper and more readable to hand off
to an init() function. And more efficient than your example as you only set
values once [if we ignore the fact that the members have already been
default initialised to garbage in every case]:

Circle::Circle()
{ init(0, 0, 0); }

Circle::Circle(int x, int y)
{ init(x, y, 0); }

Circle::Circle(int x, int y, int r)
{ init(x, y, r); }

What you are attempting is a Java idiom not a C++ one. Don't be mislead by
superficial similarities in syntax; they are totally different animals. You
should definitely prefer member initialiser lists:

Circle::Circle()
: PointX(0), PointY(0), Radius(0)
{ }

Circle::Circle(int x, int y)
: PointX(x), PointY(y), Radius(0)
{ }

Circle::Circle(int x, int y, int r)
: PointX(x), PointY(y), Radius(r)
{ }

FWIW I learnt Java and C++ the other way around. I find I miss little
things, too: templates, RAII, type safe containers... Ho hum.


Arnold the Aardvark
http://www.codeproject.com/cpp/garbage_collect.asp
 
A

Arnold the Aardvark

Ken said:
I could be wrong, and I'm more than willing to admit if I am, but I
remember pretty clearly reading somewhere that the reason we still had
default
parameters was backward compatibility with pre-overload code.

"Given general function overloading, default arguments are logically
redundant and at best a minor notational convenience. However, C with
Classes had default argument lists for years before general overloading
became avilable in C++." - Stroustrup, D&E, S:2.12.2.


Arnold the Aardvark
http://www.codeproject.com/cpp/garbage_collect.asp
 
K

Ken Alverson

Arnold the Aardvark said:
"Given general function overloading, default arguments are logically
redundant and at best a minor notational convenience. However, C with
Classes had default argument lists for years before general overloading
became avilable in C++." - Stroustrup, D&E, S:2.12.2.

That's the quote I remember, thanks!

Ken
 
H

Hendrik Schober

Ken Alverson said:
[...]
"Given general function overloading, default arguments are logically
redundant and at best a minor notational convenience. However, C with
Classes had default argument lists for years before general overloading
became avilable in C++." - Stroustrup, D&E, S:2.12.2.

That's the quote I remember, thanks!

So I got it wrong.
Now, wait... No! Francis got it wrong! :)

Schobi

P.S.: Actually... No. This quote still doesn't
say they were not introduced to help with
the problem of redundant constructor code.

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
A

Arnold the Aardvark

Hendrik said:
Ken Alverson said:
[...]
"Given general function overloading, default arguments are logically
redundant and at best a minor notational convenience. However, C with
Classes had default argument lists for years before general overloading
became avilable in C++." - Stroustrup, D&E, S:2.12.2.

That's the quote I remember, thanks!

So I got it wrong.
Now, wait... No! Francis got it wrong! :)

Schobi

P.S.: Actually... No. This quote still doesn't
say they were not introduced to help with
the problem of redundant constructor code.

I didn't have time to skim the whole book, you understand,
but there was something about:

"The default argument list was a very late addition to
the class definition. It was added to curb the proliferation
of identical 'standard argument lists' for class objects
passed as function arguments, ... they can be used to
make class object declarations less verbose and more
similar to struct declarations." - same section.

He's lost me a bit there. :-( 'standard argument lists'?


Arnold the Aardvark
http://www.codeproject.com/cpp/garbage_collect.asp
 
C

Carl Daniel [VC++ MVP]

Ken said:
Placement new is only designed to be used on uninitialized memory.
By the time you get to the first line of a constructor, you have a
semi-valid object in that memory that would be wiped out.

You'll sometimes see people doing bizarre (but legal) things like:

this->~Foo();
new (this) Foo();

To "re-initialize" an object. You can use this same technique to "call" one
constructor from within another. But don't.

-cd
 
K

Ken Alverson

Carl Daniel said:
You'll sometimes see people doing bizarre (but legal) things like:

this->~Foo();
new (this) Foo();

Is that actually legal though? I thought about it, but I wasn't sure if it
was legal to call the destructor before the constructor completed. Either way
it fails if the type of this is really a derived class and not Foo.

Regardless of whether or not it is legal, it's a bad idea, as you noted.

Ken
 
H

Hendrik Schober

Arnold the Aardvark said:
[...]
He's lost me a bit there. :-( 'standard argument lists'?

Me too. :)
Arnold the Aardvark

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
C

Carl Daniel [VC++ MVP]

Ken said:
"Carl Daniel [VC++ MVP]"
You'll sometimes see people doing bizarre (but legal) things like:

this->~Foo();
new (this) Foo();

Is that actually legal though? I thought about it, but I wasn't sure
if it was legal to call the destructor before the constructor
completed.

Yes, it's legal - the object is considered constructed at the start of the
constructor body. Afterall, an exception thrown from within the constructor
body will invoke the destructor.
Either way it fails if the type of this is really a
derived class and not Foo.
Indeed.


Regardless of whether or not it is legal, it's a bad idea, as you
noted.

Yes - it's evil. I've seen it in clever "production" code written by a
fresh young code-jockey who liked to be too clever...

-cd
 

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

Top