Initialization list and inheritance

N

NT

Hi there!

I am puzzled by what I think must be a subtlety of C++ when
list-initializing member variables in derived classes...

Here's what I mean...

class A { // warning l337proggy alert! ;-)
public:
A(int v=9812); // default value provided since this base constructor
// is called by derived classes' constructors
~A();
int getValue();
public:
int value; // it's public, but I don't care!
};
A::A(int v) : value(v) {
cout<<"A() , "<<value<<endl;
}
A::~A() {
cout<<"~A()"<<endl;
}
int A::getValue() {
return value;
}
class B : public A {
public:
B(int v=1);
~B();
};
B::B(int v) {
cout<<"B() , "<<value<<endl; // value is still 9812
}
B::~B() {
cout<<"~B()"<<endl;
}
class C : public B {
public:
C(int v);
~C();
int setValue(int v);
};
C::C(int v) {
cout<<"C() , "<<value<<endl; // 9812 because of A::A()
value=v;
cout<<" "<<value<<endl;
}
C::~C() {
cout<<"~C()"<<endl;
}
int C::setValue(int v) {
value=v;
}
int main(void) {
B *b = new B(5);
C *c = new C(10);
cout<<b->getValue()<<endl;
cout<<c->getValue()<<endl;
c->setValue(32);
cout<<c->getValue()<<endl;
delete b;
delete c;
return 0;
}

Well, so far, so good... But, something weird's happening when I change
C::C() and use an initialization list instead of an assignment in the
body...

C::C(int v) : value(v) {
cout<<"C() , "<<value<<endl;
}

GCC (version 3.3) is really not happy with that!:

proggy.cpp:41: error: class `C' does not have any field named `value'

Ok, _now_ I'm confused! I thought derived classes had access to all
non-private data of their base class?

I guess there is something absolutely trivial that I'm missing here...
Yeah, hard to believe I'm been using C++ for years, isn'it? (albeit on a
_very_ on and -mostly- off basis...)

Is there someone to help?

NT
 
H

Heinz Ozwirk

NT said:
Hi there!

I am puzzled by what I think must be a subtlety of C++ when
list-initializing member variables in derived classes...

Here's what I mean...

class A { // warning l337proggy alert! ;-)
public:
A(int v=9812); // default value provided since this base constructor
// is called by derived classes' constructors
~A();
int getValue();
public:
int value; // it's public, but I don't care!
};
A::A(int v) : value(v) {
cout<<"A() , "<<value<<endl;
}

Ok, value is set to the value specified when A is constructed or 9812 as a
default value.
A::~A() {
cout<<"~A()"<<endl;
}
int A::getValue() {
return value;
}
class B : public A {
public:
B(int v=1);
~B();
};
B::B(int v) {
cout<<"B() , "<<value<<endl; // value is still 9812
}

You don't pass the value used to initialize B down to A, so value will
always get the default value. Use

B::B(int v): A(v) ...

do pass v to A.
B::~B() {
cout<<"~B()"<<endl;
}
class C : public B {
public:
C(int v);
~C();
int setValue(int v);
};
C::C(int v) {
cout<<"C() , "<<value<<endl; // 9812 because of A::A()
value=v;
cout<<" "<<value<<endl;
}

This constructor initializes A::value twice - once with A's default value
and then it is set to the value passed to C::C(int).
C::~C() {
cout<<"~C()"<<endl;
}
int C::setValue(int v) {
value=v;
}
int main(void) {
B *b = new B(5);
C *c = new C(10);
cout<<b->getValue()<<endl;
cout<<c->getValue()<<endl;
c->setValue(32);
cout<<c->getValue()<<endl;
delete b;
delete c;
return 0;
}

Well, so far, so good... But, something weird's happening when I change
C::C() and use an initialization list instead of an assignment in the
body...

C::C(int v) : value(v) {
cout<<"C() , "<<value<<endl;
}

GCC (version 3.3) is really not happy with that!:

proggy.cpp:41: error: class `C' does not have any field named `value'

And right it is. C does not have a member named 'value'. A has, but not C.
In an initialization list, you can only initialize members of a class, and
you can initialize direct base classes. So it should be

C::C(int v): B(v) ...

and of cause, B should take care that A is initialized properly, too.

HTH
Heinz
 
E

Earl Purple

B::B(int v) {
cout<<"B() , "<<value<<endl; // value is still 9812
}

because it initalises A with its default constructor having failed to
specify otherwise. The fact that A also has a constructor that takes an
int doesn't mean it will be automatically used.
C::C(int v) : value(v) {
cout<<"C() , "<<value<<endl;
}

GCC (version 3.3) is really not happy with that!:

and so it shouldn't be as you can only initialise your own members and
your own immediate base class. There is one exception to the latter
part of that rule: if a class uses virtual inheritance then its derived
classes are still responsible for initialising it, eg

class A
{
public:
A( int );
};

class B : public virtual A
{
public:
B( int x ) : A( x )
{
};
};

class C : public B
{
C( int y ) : B ( y )
{
}
};

will fail because here C has to also initialise A. In fact one trick
that can be used to make B final is to use virtual inheritance for a
class that has only a private constructor, and declares your class as a
friend. You can use a template to achieve this.

template < typename T >
class Final
{
Final();
friend class T;
};

class MyClass : public virtual Final< MyClass >
{
// rest of class
};

(It would probably work just as well with private virtual inheritance).
 
A

Aris

Hi all!
this is the code:
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void MyFunction(char *B[])
{
cout<<sizeof(B)<<endl; //(1)
}

int main()
{
char* B[]={"1","2","we"};
MyFunction(B);
cout<<sizeof(B)<<endl;//(2)

system("PAUSE");
return 0;
}

and this is the problem :
in the (1) cout I get the answer :4
and in the (2) cout I get 12!
How can I handle this please?
 
N

Niklas Norrthon

Aris said:
Hi all!
this is the code:
#include <iostream.h>

No such header, change to:
#include <iostream>
using std::cout;
using std::endl;
#include <stdlib.h>

Only needed for the call to system(), which shouldn't be there
anyway.
#include <stdio.h>

Remove said:
#include <string.h>

Remove said:
void MyFunction(char *B[])

Same as:
void MyFunction(char** B)
(don't use all uppercase names, as they can easily be mistaken
for macros).
{
cout<<sizeof(B)<<endl; //(1)

Please indent your code. Parentheses aren't necessary, change to:
cout << sizeof B << endl;

sizeof B == sizeof(char**), which seems to be 4 on your system.

Please, please, please indent. (I do it for you this time).
And your code will be much easier to read if you use the
space bar wisely.
int main()
{
char* B[]={"1","2","we"};

should be const char* B[].
MyFunction(B);
cout << sizeof(B) << endl; // (2)

Parentheses not needed, remove:
cout << sizeof B << endl; // (2)

Here the type of B is an array of three char pointers, so
sizeof B == 3 * sizeof(char*). (In your case 3 * 4 = 12).
system("PAUSE");

Don't call system, unless you really must. The argument to system()
is system specific, which means it's more difficult to help you for
people not running the same operating system as you do.

My guess is that the purpose of the system("PAUSE") is to prevent
the program window from disappering when the progam is finished, but
there are better ways to do that. If you are using MSVC I think you
can start the program with some combination of shift, control, and
alt, with the function key that runs the program. (Try it or read the
documentation). Or you could open a command line window, and start
the program from there. If you really insist on pausing the program
return 0;
}

and this is the problem :
in the (1) cout I get the answer :4
and in the (2) cout I get 12!
How can I handle this please?

The reason for this behavior is that array type function arguments are
implicitly converted to a pointer to the first element of the array.

So when you declare a function as:

void func(char* array[]);

it means exactly the same thing as:

void func(char** array);

If the called function needs to know the size of the array, it must be
passed separatly to the function, since that information is lost when
the array is converted to a pointer to the first element.

The obvious workaround is to not use arrays at all, but the standard
library containers (vector, deque, or list).

Using vector your program becomes:

#include <vector>
#include <iostream>
#include <string>

using std::vector;
using std::cout;

char* init[] = { "1", "2", "we" };

void MyFunction(const vector<string>& b)
{
cout << "The vector b has " << b.size() << " elements\n";
}

int main()
{
vector<string> a(init, init + sizeof initial_values / sizeof *init);
MyFunction(a);
cout << "The vector a has " << a.size() >> " elements\n";
return 0;
}

/Niklas Norrthon
 

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,780
Messages
2,569,611
Members
45,278
Latest member
BuzzDefenderpro

Latest Threads

Top