some questions about multiple inheritance

J

Jess

Hello,

I've been reading Effective C++ about multiple inheritance, but I
still have a few questions. Can someone give me some help please?

First, it is said that if virtual inheritance is used, then "the
responsibility for initializing a virtual base is borne by the most
derived class in the hierarchy". What does it mean? Initializing
base class is usually done automatically by the compiler, but a
derived class can invoke the base class' constructor. What special
initialization work has to be done by the most derived class? What
about the intermediate classes (those derived classes that aren't at
the end of the hierarchy)? Do they need to do anything to init the
virtual base class and does the most derived class need to do anything
to init the intermediate classes?

Second, it is said that we should try to avoid putting data in a
virtual base class. If we use non-virtual inheritance then there will
be duplicated data members if the base class has data members.
However, once we declare virtual inheritance, then the compiler will
ensure no duplication, is this right? Therefore, it seems ok to me to
put data members into virtual base class. I guess the reason why we
should avoid putting data into a virtual base class is to make
compiler's life easier. In addition, the book also says if we don't
put data into virtual base class, then we "won't have to worry about
oddities in the initialization and assignment rules for such classes".
Could someone tell me what the author means exactly?

Third, if a class D privately inherits from class A and B, do we still
need to use virtual inheritance? If we use non-virtual inheritance,
would there be any data duplication?

Finally, I'm still not sure when multiple inheritance should be used,
any insight will be highly appreciated!

Thanks!
Jess
 
V

Victor Bazarov

Jess said:
I've been reading Effective C++ about multiple inheritance, but I
still have a few questions. Can someone give me some help please?

We can try...
First, it is said that if virtual inheritance is used, then "the
responsibility for initializing a virtual base is borne by the most
derived class in the hierarchy". What does it mean?

It means that the compiler generates code such that the virtual base
class' constructor is invoked *not* by the constructor of the class
that directly inherits the virtual base but the most derived one.
Initializing
base class is usually done automatically by the compiler, but a
derived class can invoke the base class' constructor. What special
initialization work has to be done by the most derived class?

Here is an example:

struct vbase {
int i;
vbase(int ii) : i(ii) {}
};

struct a : virtual vbase {
a() : vbase(1) {}
};

struct b : virtual vbase {
b() : vbase(2) {}
};

struct mostderived : a, b {
mostderived() : vbase(42) {} // note 'vbase' initialisation
};

#include <iostream>
int main() {
mostderived m;
std::cout << m.i << std::endl;
}

What do you expect the program to output? The default c-tor of 'a'
makes the 'i' member of 'vbase' 1, 'b' makes it 2. But when the 'm'
object is created in the 'main' function, the 'i' member will be
initialised to 42 because the most derived class is responsible for
initialising the virtual base class.
What
about the intermediate classes (those derived classes that aren't at
the end of the hierarchy)?

What about them? Do you expect to instantiate them independently?
Do they need to do anything to init the
virtual base class and does the most derived class need to do anything
to init the intermediate classes?

Yes, in case you _do_ instantiate them.
Second, it is said that we should try to avoid putting data in a
virtual base class.

Is it said, why?
If we use non-virtual inheritance then there will
be duplicated data members if the base class has data members.
Yes.

However, once we declare virtual inheritance, then the compiler will
ensure no duplication, is this right?

The merging happens between two branches in which *both* base classes
are derived from virtually, like in my example above. Had 'b' not
derived virtually from 'vbase', the duplication would still occur.
Therefore, it seems ok to me to
put data members into virtual base class. I guess the reason why we
should avoid putting data into a virtual base class is to make
compiler's life easier. In addition, the book also says if we don't
put data into virtual base class, then we "won't have to worry about
oddities in the initialization and assignment rules for such classes".
Could someone tell me what the author means exactly?

I believe you could ask the author directly. Isn't there an e-mail
address which you could use?

Anyway, if the hierarchy is more complex, it is generally possible that
somebody makes a mistake and doesn't derive virtually, which means the
duplication can still occur, and have very unpleasant effects. To avoid
the unpleasantness, the author recommends not having any data at all in
the class intended to be the very virtual base.
Third, if a class D privately inherits from class A and B, do we still
need to use virtual inheritance?

To accomplish what?
If we use non-virtual inheritance,
would there be any data duplication?

Data duplication of what?
Finally, I'm still not sure when multiple inheritance should be used,
any insight will be highly appreciated!

Get a hold of a copy of "Modern C++ Design" by Andrei Alexandrescu.
Multiple inheritance is the cornerstone of the "policy-based design".

V
 
J

Jess

Thanks a lot!

We can try...


It means that the compiler generates code such that the virtual base
class' constructor is invoked *not* by the constructor of the class
that directly inherits the virtual base but the most derived one.


Here is an example:

struct vbase {
int i;
vbase(int ii) : i(ii) {}
};

struct a : virtual vbase {
a() : vbase(1) {}
};

struct b : virtual vbase {
b() : vbase(2) {}
};

struct mostderived : a, b {
mostderived() : vbase(42) {} // note 'vbase' initialisation
};

#include <iostream>
int main() {
mostderived m;
std::cout << m.i << std::endl;
}

What do you expect the program to output? The default c-tor of 'a'
makes the 'i' member of 'vbase' 1, 'b' makes it 2. But when the 'm'
object is created in the 'main' function, the 'i' member will be
initialised to 42 because the most derived class is responsible for
initialising the virtual base class.


What about them? Do you expect to instantiate them independently?

In your example, if "mostderived" wants to init "a" and "b" explicitly
by calling their constructors, I think "mostderived" can do so, right?
If so, would "a" and "b"'s constructors iniit "vbase" by calling
vbase's constructor?
Yes, in case you _do_ instantiate them.

I think you mean I can call "a" and "b"'s constructors?
Is it said, why?

I don't know why, just mentioned in the book.
The merging happens between two branches in which *both* base classes
are derived from virtually, like in my example above. Had 'b' not
derived virtually from 'vbase', the duplication would still occur.


I believe you could ask the author directly. Isn't there an e-mail
address which you could use?

Anyway, if the hierarchy is more complex, it is generally possible that
somebody makes a mistake and doesn't derive virtually, which means the
duplication can still occur, and have very unpleasant effects. To avoid
the unpleasantness, the author recommends not having any data at all in
the class intended to be the very virtual base.

I see, thanks.
To accomplish what?

To inherit the implemenations.
Data duplication of what?

If A and B derive from one base class (vbase) and vbase has data
members. Without inheriting virtually, it seems D can still get
duplicated data.

Get a hold of a copy of "Modern C++ Design" by Andrei Alexandrescu.
Multiple inheritance is the cornerstone of the "policy-based design".

Will try. :)

Thanks,
Jess
 
V

Victor Bazarov

Jess said:
Thanks a lot!



In your example, if "mostderived" wants to init "a" and "b" explicitly
by calling their constructors, I think "mostderived" can do so, right?

Yes, any class deriving from another class initialises the base class
subobject in the constructor initialiser list.
If so, would "a" and "b"'s constructors iniit "vbase" by calling
vbase's constructor?

No, they would not. That's the trick. The virtual base class object
(subobject) is initialised by the most derived class.
I think you mean I can call "a" and "b"'s constructors?

No, I don't mean you can call them. I mean if you define your own
separate, stand-alone, objects of type 'a' and/or 'b', then *their*
'vbase' subobjects would be initialised in the respective c-tors.

a aa; // aa.vbase::i is 1
b bb; // bb.vbase::i is 2
mostderived m; // m.vbase::i is NEITHER 1 NOR 2
[..]
Third, if a class D privately inherits from class A and B, do we
still need to use virtual inheritance?

To accomplish what?

To inherit the implemenations.

Uh... Right. Access specifiers have no effect on who has to init
what virtual base class.
If A and B derive from one base class (vbase) and vbase has data
members. Without inheriting virtually, it seems D can still get
duplicated data.

Yes. I thought you were talking of something else.

V
 
J

James Kanze

In your example, if "mostderived" wants to init "a" and "b" explicitly
by calling their constructors, I think "mostderived" can do so, right?
If so, would "a" and "b"'s constructors iniit "vbase" by calling
vbase's constructor?

In this case, mostderived will call the constructors for a and
b. An important point to remember: every class type has a
constructor (if you don't provide one, the compiler does), and
that constructor will always be called, any time an instance of
the class exists, be it a sub-object or a complete object.
Given that, the pseudo code for what a compiler generates
automatically at the head of a constructor is more or less:

if ( this_is_most_derived_class ) {
call constructors for all virtual bases
}
call constructors for all direct non-virtual bases
call constructors for all objects

If you provide initializers in the initializer list of the
constructor, the compiler will use these when (and if) it calls
the constructor for the sub-object. The initializer list,
however, does not have any influence on when, or even if, the
constructor is called.

That's the point of Victor's example: the initializers for vbase
in a and in b are not used, since a and b are not the most
derived class. (Note that if the initialization expression has
side effects, these will not occur either.)
I think you mean I can call "a" and "b"'s constructors?

I don't think he does. I'm not sure, but I think he means
"initialize", rather than "instantiate". All of the sub-objects
of a type will be instantiated whenever an object of the type is
instantiated, and the constructor will be called for all of the
sub-objects of class type. The only question is when, in what
context and with what initializer list. For non-virtual bases,
the answer is always from the immediately derived class; for
virtual bases, the answer is always from the most derived class.
I don't know why, just mentioned in the book.

Part of the reason, doubtlessly, is that data must be
initialized, and that initialization supposes an initial value,
probably provided by an initializer list in a constructor.
Consider once again Victor's example: imagine now that
mostderived needs vbase to be initialized with 42 in order to
work, for some internal reason. Now create an even more derived
class. That new class will have to initialize vbase with 42, or
mostderived will not work. That means that that new class has
to know what should be internal details of the class it is
deriving from.

Even worse, of course, is if a needs vbase to be initialized
with 1 to work, and b needs it to be initialized with 2. I
wouldn't go so far as Scott, and ban all data members, but I
would definitly ensure that any class from which I inherit
virtually has a default constructor; you don't want people
deriving some layers down to have to worry about what
initialization is needed.

In practice, most uses of virtual inheritance are of interfaces,
which naturally don't have any data, nor user defined
constructors, so there's no problem. Often, in mixin's, the
base class has no data either, or if it does, the base class
itself knows how to initialize it, so the default constructor is
all you need as well; if this is not the case, the mixin
hierarchy must be closed, so that there is no risk of someone
providing an incorrect initialization list. If the virtual base
class does require an initializer, and one particular derived
class must provide it, then you have to provide a no-op default
constructor, and an initialize function which will be called by
that class. (This is how iostream handles the initialization of
std::basic_ios.)

[...]
Isn't policy-based design just a fancy repackaging of mixins:)?
(Seriously, I think Andrei also uses templates, so that the
policy decisions are made at compile time, not at runtime. In
which case, you don't really need inheritance at all.)
 
J

Jess

In this case, mostderived will call the constructors for a and
b. An important point to remember: every class type has a
constructor (if you don't provide one, the compiler does), and
that constructor will always be called, any time an instance of
the class exists, be it a sub-object or a complete object.
Given that, the pseudo code for what a compiler generates
automatically at the head of a constructor is more or less:

if ( this_is_most_derived_class ) {
call constructors for all virtual bases
}
call constructors for all direct non-virtual bases
call constructors for all objects

If you provide initializers in the initializer list of the
constructor, the compiler will use these when (and if) it calls
the constructor for the sub-object. The initializer list,
however, does not have any influence on when, or even if, the
constructor is called.

Many thanks. In Victor's example during the initialization of
"mostderived", the compiler has to call "a" and "b"'s constructors,
Are their default constructors called, with initializer list ignored?
What if their default constructors have non-empty bodies such as:

a::a(){i = 1;} //vbase.i = 1
b::b(){i = 2;} //vbase i = 2

Would the bodies be ignored too? If not, we would get conflicting
values of "i".

That's the point of Victor's example: the initializers for vbase
in a and in b are not used, since a and b are not the most
derived class. (Note that if the initialization expression has
side effects, these will not occur either.)


I don't think he does. I'm not sure, but I think he means
"initialize", rather than "instantiate". All of the sub-objects
of a type will be instantiated whenever an object of the type is
instantiated, and the constructor will be called for all of the
sub-objects of class type. The only question is when, in what
context and with what initializer list. For non-virtual bases,
the answer is always from the immediately derived class; for
virtual bases, the answer is always from the most derived class.


Part of the reason, doubtlessly, is that data must be
initialized, and that initialization supposes an initial value,
probably provided by an initializer list in a constructor.
Consider once again Victor's example: imagine now that
mostderived needs vbase to be initialized with 42 in order to
work, for some internal reason. Now create an even more derived
class. That new class will have to initialize vbase with 42, or
mostderived will not work. That means that that new class has
to know what should be internal details of the class it is
deriving from.

Even worse, of course, is if a needs vbase to be initialized
with 1 to work, and b needs it to be initialized with 2. I
wouldn't go so far as Scott, and ban all data members, but I
would definitly ensure that any class from which I inherit
virtually has a default constructor; you don't want people
deriving some layers down to have to worry about what
initialization is needed.

What kind of default constructor? One with no initializer list and
with empty body?
In practice, most uses of virtual inheritance are of interfaces,
which naturally don't have any data, nor user defined
constructors, so there's no problem. Often, in mixin's, the
base class has no data either, or if it does, the base class
itself knows how to initialize it, so the default constructor is
all you need as well; if this is not the case, the mixin
hierarchy must be closed, so that there is no risk of someone
providing an incorrect initialization list.

To *close* a hierarchy, do we need to forbid all classes to inherit
from the currently most-derived-class?
If the virtual base
class does require an initializer, and one particular derived
class must provide it, then you have to provide a no-op default
constructor, and an initialize function which will be called by
that class. (This is how iostream handles the initialization of
std::basic_ios.)

Do you mean if "vbase" needs to have "i" initialized, then "vbase"
should provide a function to init "i" and "vbase" default constructor
is no-op? In other words, would vbase look something like:

class vbase{
public:
int i;
vbase(){}
void init(){i = 42;}
};

Then every class (e.g. mostderived, a or b) that derives from it would
need to call "init()" in their initializer list? something like this?

class a:virtual public vbase{
public:
a():init(){}
};

class b: virtual public vbase{
public:
b():init(){}
};

class mostderived: public a, public b{
public:
mostderived():init(){}
};

In addition, if "a" needs some particular initialization, can I write
it's inita() function that does that and call this function in
mostderived init list?

Thanks,
Jess
 
J

James Kanze

Many thanks. In Victor's example during the initialization of
"mostderived", the compiler has to call "a" and "b"'s constructors,
Are their default constructors called, with initializer list ignored?

In Victor's example, there is no initializer list to ignore, and
the only constructors are the default constructor and the copy
constructor. If he'd have written:

mostderived::mostderived()
: a( 1 )
, b( 2 )
{
}

the compiler would have complained, since there are no
corresponding constructors for a and b. But initializer list or
no, the constructor of mostderived will call the constructors of
a and of b, *after* having called the constructor of vbase.
What if their default constructors have non-empty bodies such as:
a::a(){i = 1;} //vbase.i = 1
b::b(){i = 2;} //vbase i = 2
Would the bodies be ignored too?

No. Nothing is being ignored. The constructors are being
called (or the call is being optimized out because the compiler
can see that it is a no-op).
If not, we would get conflicting
values of "i".

Not conflicting, successive. The order of initialization is
precisely specified: vbase::i will first be set to 42, by the
call to the constructor of vbase, then to 1, and then to 2, by
the constructors for a and for b. In that order.

Why don't you instrument the constructors, and try it?

[...]
What kind of default constructor? One with no initializer list and
with empty body?

Any kind of "default constructor". The standard says that a
default constructor is one that can be called with no arguments;
any constructor that can be called with no arguments fits the
bill.

Afterwards, it's up to you to decide what it should (or should
not) do.
To *close* a hierarchy, do we need to forbid all classes to inherit
from the currently most-derived-class?

More or less. It's not a formal term. I use it when a hierarchy
is more or less an implementation detail; all of the classes in
the hierarchy are designed and implemented at the same time, by
the same team, and the hierarchy is not designed for derivation
in client code. (Such cases occur more often than one might
think. In particular, I've never had a case of mixin's where
the hierarchy wasn't closed.)
Do you mean if "vbase" needs to have "i" initialized, then "vbase"
should provide a function to init "i" and "vbase" default constructor
is no-op? In other words, would vbase look something like:
class vbase{
public:
int i;
vbase(){}
void init(){i = 42;}
};

More or less. I'd set the variable to some "neutral" value in
the default constructor, just to avoid an uninitialized
variable, then provide an init() function that overrides it.
(But std::basic_ios doesn't do this. And calling the destructor
of a std::basic_ios before the init() function has been invoked
is undefined behavior.)
Then every class (e.g. mostderived, a or b) that derives from it would
need to call "init()" in their initializer list? something like this?

More or less. The whole point, of course, is that only low
level derived classes would need to call it; further derived
classes can ignore it.

It's also something that I would avoid if at all possible.
class a:virtual public vbase{
public:
a():init(){}
};

That would be:
class a:virtual public vbase{
public:
a() { init() }
};

Except that there is absolutely no reason to do this unless
init needs arguments which can only be known to the classes
immediately deriving from it.

Remember, this is an exceptional case. Something you want to
avoid if at all possible, and which is rarely, if ever,
necessary. I only mentionned it because there is one well known
example where it is necessary, and is used: the iostream
hierarchy.
class b: virtual public vbase{
public:
b():init(){}
};
class mostderived: public a, public b{
public:
mostderived():init(){}
};
In addition, if "a" needs some particular initialization, can I write
it's inita() function that does that and call this function in
mostderived init list?

You can't call a function in the initialization list (at least
not as you are doing it)---only in the body of the constructor.
And you don't want to depend on calling a function for
initialization unless there is no other real choice.
 
J

Jess

Many thanks. In Victor's example during the initialization of
"mostderived", the compiler has to call "a" and "b"'s constructors,
Are their default constructors called, with initializer list ignored?

In Victor's example, there is no initializer list to ignore, and
the only constructors are the default constructor and the copy
constructor. If he'd have written:

mostderived::mostderived()
: a( 1 )
, b( 2 )
{
}

the compiler would have complained, since there are no
corresponding constructors for a and b. But initializer list or
no, the constructor of mostderived will call the constructors of
a and of b, *after* having called the constructor of vbase.
What if their default constructors have non-empty bodies such as:
a::a(){i = 1;} //vbase.i = 1
b::b(){i = 2;} //vbase i = 2
Would the bodies be ignored too?

No. Nothing is being ignored. The constructors are being
called (or the call is being optimized out because the compiler
can see that it is a no-op).
If not, we would get conflicting
values of "i".

Not conflicting, successive. The order of initialization is
precisely specified: vbase::i will first be set to 42, by the
call to the constructor of vbase, then to 1, and then to 2, by
the constructors for a and for b. In that order.

Why don't you instrument the constructors, and try it?

[...]
What kind of default constructor? One with no initializer list and
with empty body?

Any kind of "default constructor". The standard says that a
default constructor is one that can be called with no arguments;
any constructor that can be called with no arguments fits the
bill.

Afterwards, it's up to you to decide what it should (or should
not) do.
To *close* a hierarchy, do we need to forbid all classes to inherit
from the currently most-derived-class?

More or less. It's not a formal term. I use it when a hierarchy
is more or less an implementation detail; all of the classes in
the hierarchy are designed and implemented at the same time, by
the same team, and the hierarchy is not designed for derivation
in client code. (Such cases occur more often than one might
think. In particular, I've never had a case of mixin's where
the hierarchy wasn't closed.)

Thanks a lot! A related question that I forgot to ask is to prevent
derivation of a class, do we need to make the base class' constructors
private?

More or less. I'd set the variable to some "neutral" value in
the default constructor, just to avoid an uninitialized
variable, then provide an init() function that overrides it.
(But std::basic_ios doesn't do this. And calling the destructor
of a std::basic_ios before the init() function has been invoked
is undefined behavior.)


More or less. The whole point, of course, is that only low
level derived classes would need to call it; further derived
classes can ignore it.


Do you mean only "a" and "b" need to call "init" while "mostderived"
doesn't have to?
It's also something that I would avoid if at all possible.

I'll try to avoid. On the other hand, it could happen that this init
function requires an input from a derived class to initialize a value
in vbase like this:

void init(int k){i = k;}

In this case, I'm not sure whether I should let "a", "b" or
"mostderived" call it. Suppose init requires an input from the most
derived class, then "init" should be called by "mostderived". However,
it seems "a" and "b" should also call "init" because they may have
been designed to be most derived classes of "vbase".

Thanks,
Jess
 
J

James Kanze

On Jun 30, 6:23 pm, James Kanze <[email protected]> wrote:
Thanks a lot! A related question that I forgot to ask is to prevent
derivation of a class, do we need to make the base class' constructors
private?

Unless the derivation is virtual, a further derived class
doesn't need to access the constructors of the base classes.
The classical solution to prevent further derivation is:

class ToBeBlocked ;

class BlockDerivation
{
private:
BlockDerivation() {}
friend class ToBeBlocked ;
}

class ToBeBlocked : private virtual BlockDerivation
{
// ...
} ;

Because the derivation is virtual, any class deriving from
ToBeBlocked must call the constructor of BlockDerivation from
its constructor. Which it can't.

In practice, I've found people deriving from classes they
shouldn't not to be a problem. If I really want to block
derivation, I'll explicitly document the fact that you're not
supposed to derive from this class, but typically, the semantics
of the class are such that it doesn't occur to anyone to derive
from it anyway. I've never bothered with anything like the
above, although a fair proportion of my classes are not designed
to be base classes.

[...]
Do you mean only "a" and "b" need to call "init" while "mostderived"
doesn't have to?

I mean that by putting the real initialization in separate
function, you control who calls it. And who calls it depends
very much on the design semantics; there is no general rule.
I'll try to avoid. On the other hand, it could happen that this init
function requires an input from a derived class to initialize a value
in vbase like this:
void init(int k){i = k;}
In this case, I'm not sure whether I should let "a", "b" or
"mostderived" call it. Suppose init requires an input from the most
derived class, then "init" should be called by "mostderived". However,
it seems "a" and "b" should also call "init" because they may have
been designed to be most derived classes of "vbase".

Exactly:).

What you've just described is the design of the classical
iostream hierarchy. Both istream and ostream had two
constructors, one which took a streambuf*, and the other
(protected) which was a default constructor. The one which took
a streambuf* called init with the streambuf*; the other didn't,
supposing that a further derived class (e.g. iostream or
ofstream) would call init. (The standard iostream changed this,
and is designed so that init can be called multiple times.) A
class like ifstream might, for example, use the no-op
constructor of istream, and then call init itself, in order to
ensure that the filebuf it managed was completely constructed
before the base classes got a pointer to it. (There is still a
problem with the destructor, of course, and at least one
implementation of iostream I'm familiar with accesses the
streambuf in the destructor, which means that the destructor of
a derived class cannot destruct it.)

(The case of the iostream is a bit special, in that it was never
really well documented, and different implementations did
different things.)
 
J

Jess

Thanks a lot! A related question that I forgot to ask is to prevent
derivation of a class, do we need to make the base class' constructors
private?

Unless the derivation is virtual, a further derived class
doesn't need to access the constructors of the base classes.
The classical solution to prevent further derivation is:

class ToBeBlocked ;

class BlockDerivation
{
private:
BlockDerivation() {}
friend class ToBeBlocked ;
}

class ToBeBlocked : private virtual BlockDerivation
{
// ...
} ;

Because the derivation is virtual, any class deriving from
ToBeBlocked must call the constructor of BlockDerivation from
its constructor. Which it can't.

In practice, I've found people deriving from classes they
shouldn't not to be a problem. If I really want to block
derivation, I'll explicitly document the fact that you're not
supposed to derive from this class, but typically, the semantics
of the class are such that it doesn't occur to anyone to derive
from it anyway. I've never bothered with anything like the
above, although a fair proportion of my classes are not designed
to be base classes.

[...]
Do you mean only "a" and "b" need to call "init" while "mostderived"
doesn't have to?

I mean that by putting the real initialization in separate
function, you control who calls it. And who calls it depends
very much on the design semantics; there is no general rule.
I'll try to avoid. On the other hand, it could happen that this init
function requires an input from a derived class to initialize a value
in vbase like this:
void init(int k){i = k;}
In this case, I'm not sure whether I should let "a", "b" or
"mostderived" call it. Suppose init requires an input from the most
derived class, then "init" should be called by "mostderived". However,
it seems "a" and "b" should also call "init" because they may have
been designed to be most derived classes of "vbase".

Exactly:).

What you've just described is the design of the classical
iostream hierarchy. Both istream and ostream had two
constructors, one which took a streambuf*, and the other
(protected) which was a default constructor. The one which took
a streambuf* called init with the streambuf*; the other didn't,
supposing that a further derived class (e.g. iostream or
ofstream) would call init. (The standard iostream changed this,
and is designed so that init can be called multiple times.) A
class like ifstream might, for example, use the no-op
constructor of istream, and then call init itself, in order to
ensure that the filebuf it managed was completely constructed
before the base classes got a pointer to it. (There is still a
problem with the destructor, of course, and at least one
implementation of iostream I'm familiar with accesses the
streambuf in the destructor, which means that the destructor of
a derived class cannot destruct it.)

Thanks. :) I thought iostream would be complicated, and it is indeed.
Jess
 

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
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top