Calling one constructor from another in VC++

  • Thread starter Peter E. Granger
  • Start date
C

Carl Daniel [VC++ MVP]

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

Me too. :)

I think what he's talking about is the situation where you have a class that
has several constructors, all of which have a common subset of parameters
(the 'standard argument list'), with each constructor adding one or more
additional arguments beyond that. Default parameters can allow you to
collapse that into fewer constructors.

-cd
 
H

Hendrik Schober

Carl Daniel said:
[...] 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.

Is this true? I didn't think so.
[...]
-cd


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]

Hendrik said:
Carl Daniel [VC++ MVP]
[...] 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.

Is this true? I didn't think so.

You're right - it's not. Destructors for fully constructed sub-objects will
be invoked.

Nonetheless, I believe that the awful thing I showed up above is technically
legal. Legal or not, it's done it real-world code that I've come across.

-cd
 
A

Arnold the Aardvark

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

Me too. :)

I think what he's talking about is the situation where you have a class
that has several constructors, all of which have a common subset of
parameters (the 'standard argument list'), with each constructor adding
one or more
additional arguments beyond that. Default parameters can allow you to
collapse that into fewer constructors.

Yes that makes sense, though it sort of contradicts the idea that
default arguments were pre-overloading: He says they were introduced
to curb overloading, but that overloading makes default arguments
redundant. Maybe he meant something else. Um... who cares anyway?


Arnold the Aardvark

'Failure of intelligence' or 'failure of democracy'? They
lied and thousands died, but corporations made money, and
that's all that matters, isn't it?
 
P

Peter E. Granger

[I see there have been some other replies to this message in the interim;
probably by now somebody else has hit on the same solution.]

With the help of an ingenious friend (thank you, Theo!), I was able to find
out how to make the constructor calls that I wanted. It's kind of an
odd-looking construct, and since no one here knew about it, I guess it's not
something that's commonly done in C++.

On the off-chance that it'll be useful to someone else, here's the code:

// From file Sphere.h

class Sphere
{
public:
Sphere(void);
Sphere(int x, int y, int z);
Sphere(int x, int y, int z, int r);
~Sphere(void);
System::String *ToString();

private:
int CenterX;
int CenterY;
int CenterZ;
int Radius;
};

// From file Sphere.cpp

Sphere::Sphere(void)
{
this->CenterX = 0;
this->CenterY = 0;
this->CenterZ = 0;
this->Radius = 0;
}

Sphere::Sphere(int x, int y, int z)
{
//Sphere(); // Wrong! Just makes and destroys temp object
this->Sphere::Sphere(); // Right! Initializes all members.
this->CenterX = x;
this->CenterY = y;
this->CenterZ = z;
}

Sphere::Sphere(int x, int y, int z, int r)
{
//Sphere(x, y, z); // Wrong! Just makes and destroys temp object
this->Sphere::Sphere(x, y, z); // Right! Initializes all members.
this->Radius = r;
}

Sphere::~Sphere(void)
{
}

String *Sphere::ToString()
{
String *sReturn = S"";

sReturn = String::Format(S"Sphere is centered at [X={0}, Y={1}, Z={2}]",
CenterX.ToString(), CenterY.ToString(), CenterZ.ToString());
sReturn = String::Concat(sReturn,
String::Format(S"\nSphere has a radius of {0}", Radius.ToString()));

return sReturn;
}

// From file ConstructorTest.cpp

int _tmain()
{
Sphere *sphere1 = new Sphere();
Sphere *sphere2 = new Sphere(10, 100, 1000);
Sphere *sphere3 = new Sphere(5, 50, 500, 5000);

Console::WriteLine(S"Sphere1: {0}", sphere1->ToString());
Console::WriteLine(S"Sphere2: {0}", sphere2->ToString());
Console::WriteLine(S"Sphere3: {0}", sphere3->ToString());
return 0;
}

With the incorrect constructor calls (commented out in the code above), the
result of executing ConstructorTest.exe is:

Sphere1: Sphere is centered at [X=0, Y=0, Z=0]
Sphere has a radius of 0
Sphere2: Sphere is centered at [X=10, Y=100, Z=1000]
Sphere has a radius of -842150451
Sphere3: Sphere is centered at [X=-842150451, Y=-842150451, Z=-842150451]
Sphere has a radius of 5000

But with the correct ones, the result is:

Sphere1: Sphere is centered at [X=0, Y=0, Z=0]
Sphere has a radius of 0
Sphere2: Sphere is centered at [X=10, Y=100, Z=1000]
Sphere has a radius of 0
Sphere3: Sphere is centered at [X=5, Y=50, Z=500]
Sphere has a radius of 5000
 
P

Peter E. Granger

Arnold the Aardvark said:
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.

Just exploring the capabilities of the language. It's a construct that I've
gotten used to, and since I didn't see any mention of it in C++ docs, I
wanted to know if it was available. This was more a curiosity than a crisis.
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)
{ }

Those look like they could be useful; not something I'd run across in my
reading yet.
FWIW I learnt Java and C++ the other way around. I find I miss little
things, too: templates, RAII, type safe containers... Ho hum.

C++ has been absent from my repertoire of languages for far too long; since
it still seems to be the dominant language for development, I figured it's
about time to correct that.

Thanks for all your help.

- Peter
 
D

Doug Harrison [MVP]

Peter said:
[I see there have been some other replies to this message in the interim;
probably by now somebody else has hit on the same solution.]

With the help of an ingenious friend (thank you, Theo!), I was able to find
out how to make the constructor calls that I wanted. It's kind of an
odd-looking construct, and since no one here knew about it, I guess it's not
something that's commonly done in C++.

On the off-chance that it'll be useful to someone else, here's the code:

Sphere::Sphere(int x, int y, int z, int r)
{
//Sphere(x, y, z); // Wrong! Just makes and destroys temp object
this->Sphere::Sphere(x, y, z); // Right! Initializes all members.
this->Radius = r;
}

The above is bogus, both from a Standard C++ point of view and conceptually,
because even if it were legal, it would be equivalent to using placement new
on a partially constructed object and would be wrong for all the reasons
that method is wrong. It's a non-standard leftover from the dark ages of
VC++. Don't use it. You've been given correct answers by myself and others
in this thread.
 
R

Ronald Laeremans [MSFT]

Yes, do not use this. It is not guaranteed to continue working. And most
likely will indeed stop working in some future version.

If it is any consolation "delegating constructors" are on the C++ Standards
committee list of the possible feature additions.

Ronald Laeremans
Visual C++ team

Doug Harrison said:
Peter said:
[I see there have been some other replies to this message in the interim;
probably by now somebody else has hit on the same solution.]

With the help of an ingenious friend (thank you, Theo!), I was able to find
out how to make the constructor calls that I wanted. It's kind of an
odd-looking construct, and since no one here knew about it, I guess it's not
something that's commonly done in C++.

On the off-chance that it'll be useful to someone else, here's the code:

Sphere::Sphere(int x, int y, int z, int r)
{
//Sphere(x, y, z); // Wrong! Just makes and destroys temp object
this->Sphere::Sphere(x, y, z); // Right! Initializes all members.
this->Radius = r;
}

The above is bogus, both from a Standard C++ point of view and conceptually,
because even if it were legal, it would be equivalent to using placement new
on a partially constructed object and would be wrong for all the reasons
that method is wrong. It's a non-standard leftover from the dark ages of
VC++. Don't use it. You've been given correct answers by myself and others
in this thread.
 
H

Hendrik Schober

Peter E. Granger said:
Just exploring the capabilities of the language. It's a construct that I've
gotten used to, and since I didn't see any mention of it in C++ docs, I
wanted to know if it was available. This was more a curiosity than a crisis.

FWIW, when I started to learn C++,
I tried that, too. Unfortunately,
the compiler I used back then did
support it, forcing me to change
the code later.
You should definitely prefer member initialiser lists:
[...]

Those look like they could be useful; not something I'd run across in my
reading yet.

Uh. What are you reading???
[...]
C++ has been absent from my repertoire of languages for far too long; since
it still seems to be the dominant language for development, I figured it's
about time to correct that.

Good idea. :)
Thanks for all your help.

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

Hendrik Schober

Peter E. Granger said:
[I see there have been some other replies to this message in the interim;
probably by now somebody else has hit on the same solution.]

Yes. And hopefully they have abandoned it by now.
With the help of an ingenious friend (thank you, Theo!), I was able to find
out how to make the constructor calls that I wanted. It's kind of an
odd-looking construct, and since no one here knew about it, I guess it's not
something that's commonly done in C++.

1. No, people do know about it.
2. Yes, it isn't done commonly -- and that's
for good reasons.
On the off-chance that it'll be useful to someone else, here's the code:
[snipped code that attempts to call a ctor
on an already constructed object]


Once again, this is the simplest solution
to your problem:

Sphere(int x=0, int y=0, int z=0, int r=0)
: CenterX(x), CenterY(y), CenterZ(z), Radius(r)
{
}

What is tehre to miss?

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
 
B

Bern McCarty

Hmmmm. Why do you say that explicit qualification is bogus/illegal from a
Standard C++ point of view? I can buy that using it to call another
constructor on a partially constructed object might be a mistake, but I
question the "even it it were legal" and "its non-standard" assertions.
Explicit qualification appears to be perfectly legal and standard C++. Is
it not? From Section 10.2 "Member name lookup" of the C++ standard

12 Explicit qualification with the scope operator (5.1) suppresses the
virtual call mechanism. [Example:

class B { public: virtual void f(); };

class D : public B { public: void f(); };

void D::f() { /* ... */ B::f(); }

Here, the function call in D::f really does call B::f and not D::f. ]


And there are other places in the standard where explicit qualification is
used to make various points. I easily believe that using explicit
qualification to call one constructor from another is bad, but I would like
to better understand why. My own guess is that it would be bad because it
could cause double invocation of constructors for all nodes of the
inheritance graph and their data members. If that were not anticipated
throughout the entire graph it could result in leaks and hard to find bugs.
Is that why?

The constructor scenario aside, suppose that I want to augment a regular
non-pure virtual method that I am overriding rather than replace it
wholesale. Isn't it perfectly fine to use explicit qualification to supress
the virtual call mechanism in order to invoke base class method
implementation and then add my other additional logic?

-Bern McCarty

Doug Harrison said:
Peter said:
[I see there have been some other replies to this message in the interim;
probably by now somebody else has hit on the same solution.]

With the help of an ingenious friend (thank you, Theo!), I was able to find
out how to make the constructor calls that I wanted. It's kind of an
odd-looking construct, and since no one here knew about it, I guess it's not
something that's commonly done in C++.

On the off-chance that it'll be useful to someone else, here's the code:

Sphere::Sphere(int x, int y, int z, int r)
{
//Sphere(x, y, z); // Wrong! Just makes and destroys temp object
this->Sphere::Sphere(x, y, z); // Right! Initializes all members.
this->Radius = r;
}

The above is bogus, both from a Standard C++ point of view and conceptually,
because even if it were legal, it would be equivalent to using placement new
on a partially constructed object and would be wrong for all the reasons
that method is wrong. It's a non-standard leftover from the dark ages of
VC++. Don't use it. You've been given correct answers by myself and others
in this thread.
 
D

Doug Harrison [MVP]

Bern said:
Hmmmm. Why do you say that explicit qualification is bogus/illegal from a
Standard C++ point of view?

I didn't say that.
I can buy that using it to call another
constructor on a partially constructed object might be a mistake, but I
question the "even it it were legal" and "its non-standard" assertions.
Explicit qualification appears to be perfectly legal and standard C++.

You can't directly call a constructor. Thus the following is illegal:
Is it not? From Section 10.2 "Member name lookup" of the C++ standard

See 12.1/2.
And there are other places in the standard where explicit qualification is
used to make various points. I easily believe that using explicit
qualification to call one constructor from another is bad, but I would like
to better understand why. My own guess is that it would be bad because it
could cause double invocation of constructors for all nodes of the
inheritance graph and their data members. If that were not anticipated
throughout the entire graph it could result in leaks and hard to find bugs.
Is that why?

That's certainly one very big reason. This being C++, there may be some more
obscure ones I haven't thought of. :)
The constructor scenario aside, suppose that I want to augment a regular
non-pure virtual method that I am overriding rather than replace it
wholesale. Isn't it perfectly fine to use explicit qualification to supress
the virtual call mechanism in order to invoke base class method
implementation and then add my other additional logic?

Yep.
 

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