Strange initialization of member variable

U

Universe

Hello all. I have a problem about initialization of member variable.
The example code is below:

#include "iostream"

using namespace std;

class ParentsNode
{
public:
ParentsNode();
~ParentsNode();

private:
class Node;
Node* aTestNode;
};

class ParentsNode::Node
{
public:
Node() {
Node("");
};

Node(string s) : s(s), next(NULL)
{
cout << "The value of Node.next when Node is
initialing: " << next << endl;
};

public:
string s;
Node* next;
};

ParentsNode::parentsNode()
{
aTestNode = new Node;

cout << "The value of Node.next when ParentsNode is initialing: "
<< aTestNode->next << endl;

}

ParentsNode::~ParentsNode()
{
delete aTestNode;
aTestNode = NULL;
};


int main()
{
ParentsNode* pSimpleNode = new ParentsNode;
delete pSimpleNode;

return 0;
}

Output:
Debug mode,

The value of Node.next when Node is initialing: 00000000
The value of Node.next when ParentsNode is initialing: CDCDCDCD

Release mode,

The value of Node.next when Node is initialing: 00000000
The value of Node.next when ParentsNode is initialing: D50891A6

The environment is Win7+VC2008SP1.

I am confused with the value of Node.next. Why it's changed after
inialized in constructor Node(string s). It doesn't seem the inialized
value was successfully preserved when return to constructor Node().
Does it happen on other C++ compiler? Does C++ stardard define this
exactly?

Please give me some hints. Thank you very much.
 
A

Alf P. Steinbach /Usenet

* Universe, on 23.01.2011 15:56:
Hello all. I have a problem about initialization of member variable.
The example code is below:

#include "iostream"

using namespace std;

class ParentsNode
{
public:
ParentsNode();
~ParentsNode();

private:
class Node;
Node* aTestNode;
};

class ParentsNode::Node
{
public:
Node() {
Node("");

This doesn't do what you think it does.

It creates a temporary Node object, that's all.

It does call the constructor below, but it doesn't call it on the current object
(i.e., it doesn't call it on *this).

C++0x will, probably, support constructor chaining.

C++98 does not.

};

Node(string s) : s(s), next(NULL)

Simply provide a default value for s.

Like:

Node( string const& s = "" ): s_( s ), next_( 0 )

{
cout<< "The value of Node.next when Node is
initialing: "<< next<< endl;
};

public:
string s;
Node* next;
};


In your case above you do not need constructor chaining.

But where you do need it, in C++98 technique is to introduce an artificial base
class to hold the state that you want to initialize.

Another technique is to use an 'init' function called from the constructors.

It's like a choice between pestilence and cholera.

Happily it's quite rare situation, but in general I think I regard the
artificial base class as the lesser evil (note again: you don't need it here).


Cheers & hth.,

- Alf
 
G

Geoff

* Universe, on 23.01.2011 15:56:

This doesn't do what you think it does.

It creates a temporary Node object, that's all.

It does call the constructor below, but it doesn't call it on the current object
(i.e., it doesn't call it on *this).

C++0x will, probably, support constructor chaining.

C++98 does not.



Simply provide a default value for s.

Like:

Node( string const& s = "" ): s_( s ), next_( 0 )




In your case above you do not need constructor chaining.

But where you do need it, in C++98 technique is to introduce an artificial base
class to hold the state that you want to initialize.

Another technique is to use an 'init' function called from the constructors.

It's like a choice between pestilence and cholera.

Happily it's quite rare situation, but in general I think I regard the
artificial base class as the lesser evil (note again: you don't need it here).


Cheers & hth.,

- Alf


Also, in Visual Studio the CDCDCDCD... sequence is what the debugger
puts into heap variables. It is your clue that the variable was not
initialized. In release mode this doesn't occur so you get whatever
was in the memory when your object was instantiated but not
initialized.

These are features of your development environment and not the
standard.

http://en.wikipedia.org/wiki/Magic_number_(programming)
 
U

Universe

Thank you, Alf. I haven't known constructor chaining before, you
taught me. Can you tell me more details about why the artificial base
class is lesser evil than an 'init' function? I'm curious.

Geoff, I know the CDCDCDCD is filled by VC debugger in debug mode.
Pasting the two outputs of debug mode and release mode is in order to
show the value of Node.next changes in both modes. Thank you all the
same.
 
U

Ulrich Eckhardt

Alf said:
* Universe, on 23.01.2011 15:56:

Simply provide a default value for s.

Like:

Node( string const& s = "" ): s_( s ), next_( 0 )

For the record, a std::string that isn't explicitly initialized to
anything will become an empty string, so an initialisation with "" is just
redundant and perhaps even causes overhead.

That said, using a default-value is a good idea. Two things here though:
1. You might want to make the constructor "explicit" then, to avoid
implicit conversions.
2. Node(string const& s = string())
This syntax works just as well and avoids aforementioned overhead.

In your case above you do not need constructor chaining.

But where you do need it, in C++98 technique is to introduce an
artificial base class to hold the state that you want to initialize.

Another technique is to use an 'init' function called from the
constructors.

It's like a choice between pestilence and cholera.

There's a third one to consider as option, and that is to only use members
that "work" correctly when default-constructed. E.g. raw pointers don't.
Happily it's quite rare situation, but in general I think I regard the
artificial base class as the lesser evil (note again: you don't need it
here).

The OP asked why. I'd say the reason is convenience because putting
separate responsibilities into separate code makes things easier. I'd also
say it's easier to get exception safety, but I could be wrong there.

Uli
 
U

Universe

For the record, a std::string that isn't explicitly initialized to
anything will become an empty string, so an initialisation with "" is just
redundant and perhaps even causes overhead.

That said, using a default-value is a good idea. Two things here though:
1. You might want to make the constructor "explicit" then, to avoid
implicit conversions.
2. Node(string const& s = string())
This syntax works just as well and avoids aforementioned overhead.





There's a third one to consider as option, and that is to only use members
that "work" correctly when default-constructed. E.g. raw pointers don't.


The OP asked why. I'd say the reason is convenience because putting
separate responsibilities into separate code makes things easier. I'd also
say it's easier to get exception safety, but I could be wrong there.

Uli

Thank you, Uli. Your explanation is very helpful. The std::string's
initialization also makes me confused for a long time.
 

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

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top