some C++-specific interview questions

E

Earl Purple

werasm said:
Derived classes can of course static_cast their this pointer to that of
their base and then delete themselves, but I don't think bases have to
guard agains that kind of (absurd) behaviour :-0.

Not even sure they can. Now we know that you cannot call a protected
member of a base class other than on yourself, so this would not be
permitted:

Base * pbase = static_cast< Base * >(this);
delete pbase;

because at the point of call delete pbase, would it know that it was
really pointing to this? I don't know.

Perhaps

delete static_cast< Base * >(this) would be permitted because the
"this" is in the same statement but even then I'm not sure. Tried this
in Comeau:
__________________________

class A
{
protected:
~A() {}
};

class B : protected A
{
public:
void delete_myself_through_A()
{
delete static_cast< A* >( this );
}
};

int main()
{
B * b = new B;
b->delete_myself_through_A();
return 0;
}

_____________________________

and it failed as I expected it might via:

Your Comeau C/C++ test results are as follows:

Comeau C/C++ 4.3.8 (Aug 19 2006 13:36:48) for ONLINE_EVALUATION_Alpha1
Copyright 1988-2006 Comeau Computing. All rights reserved.
MODE:strict errors C++

"ComeauTest.c", line 12: error: protected function "A::~A" (declared at
line 4) is
not accessible through a "A" pointer or object
delete static_cast< A* >( this );
^

1 error detected in the compilation of "ComeauTest.c".

In strict mode, with -tused, Compile failed
Hit the Back Button to review your code and compile options.
 
M

ma740988

Phlip said:
It returns a reference to a destructed local, so that's undefined.

Next, vector<node> is not typedeffed.

Next, the default_string has nothing to do with the for-loop, which could be
just a decoy. Regardless, you should announce the function does two
independent things and should be split in two.

Next, cout << iter is poorly defined to produce something useless, possibly
a pointer address. If operator<< is defined for 'node', then cout << * iter
might be more useful.

Next, all iterators should always be called 'it'. ;-)

Plus, the preferred form of 'it' should be ++it :)
 
J

Jerry Coffin

[ ... ]
3. Comment on the following function. What would you change? State
your assumptions:

string & Foo()
{
string default_string = "default answer";
for (vector<node>::iterator iter = mynodes.begin();
iter != mynodes.end();
iter++)
{
cout << iter;
}
return default_string;
}

Nobody seems to have commented on what struck me as the most fundamental
point about this function: it's not really A function at all -- to all
appearances, it's two entirely unrelated functions shoved together. One
function seems to have been intended to copy a collection to cout. The
other appears intended to return a string. There appears to be no
relationship between the two at all. You should almost certainly
separate the two:

string get_some_string() {
return std::string("default answer");
}

std::eek:stream &show_nodes(std::eek:stream &os) {
std::copy(mynodes.begin(), mynodes.end(),
std::eek:stream_iterator<node>(os));
return os;
}

There's probably rooom for legitimate debate that the former is
pointless and the latter open to further improvement, such as possibly
becoming an operator<< -- but right now we don't really have enough
information to say much more about either.
4. When would you use private inheritence? (I have never personally
used this.)

Primarily when you'd otherwise use composition, but the class has one or
more virtual functions you need/want to override. Since you can only
override a virtual function via inheritance, that's what you do -- but
inheritance is a tighter form of coupling than composition, so you
generally want to avoid it when reasonable.
5. Where in memory is the virtual function lookup table stored?
(I found this one to be way too detailed for me.)

There really is no answer to this. In fact, there's not even a
requirement that there BE a vtable. It's a common method of
implementation, but it's not required. Probably the primary point worth
keeping mind is that there's NOT normally a vtable for each object of a
class -- there's only one vtable for all objects of that class, and each
object contains only a pointer to the vtable they all share. The actual
location will typically be somewhere inside of the code segment, but
from there it varies wildly.

In a typical implementation, the compiler will synthesize some sort of
name for the vtable. Some linkers can arrange things in order
alphabetically, in which case its location will depend on how the name
is synthesized. For a couple of obvious examples, it could synthesize
something like $vtable$class_name, or $class_name$vtable. At least some
linkers can order things by name, in which case differences like these
would obviously change locations.

Object code often also supports producing a number of different segments
in the executable, each with its own attributes. In this case, the
compiler might put the vtable into a segment of constant data instead of
with the code at all -- this could prevent (for one example) any attempt
at executing the pointers as if they were code.
6. Consider member initialisation lists. Why are they needed? Why are
they considered more efficient than initialising members in the body of
the constructor?

Necessity: some kinds of things (e.g. references, const variables) MUST
be provided with initializers rather than being created with default
initialization and then assigned to after the fact.

Efficiency: assignment in the body means creating an object with default
intialization, and then assigning a different value. The assignment will
often involve creating a temporary object of the correct type, and then
copying its value to the object in question.

For bonus points, go into a little bit about handling exceptions in
initializer lists.
 
P

Phlip

Kai-Uwe Bux said:
It does not prove your point:

No post can contain so much blinding detail that it covers every conceivable
objection.
You claimed: "you should not inherit from a
class with no virtual methods".

And C++ is multi-paradigm, so within the template paradigm, you should not
inherit except to specialize a member.
Your claim would imply, one should not code something like the class
fourier. Yet the standard provides templates like unary_function exactly
for this kind of use.

Your example "overrode" operator(). I am aware it was not virtual, and might
not exist in the parent. Yet what you did was very similar to an override,
from the caller's point of view.

Templates are another form of polymorphism, such that a call to an object
with a method, such as foo->bar(), or foo->operator(), will dispatch to any
number of different implementations.
Could you please provide a definition of the term "convenience class"?

A class that exists only to provide a minor tweak to an existing class,
regardless where or how an override occurs. Brainless example: Inherit
std::string, and add a method ToUTF_16(). This overrides nothing
(syntactically or semantically), it simply makes the string class easier to
use in an application that likes UTF-16. I'm also not saying one should do
that. Only do it if it's convenient.
Huh? What is a "compilable comment"?

This great post by Tim Ottinger says it best:

----8<-------------------------------
But I find it amusing and odd that you so frequently beat the drum about
the need to "generalize" as if most programmers don't learn this in
Programming 101. Generalization is like mother's milk to most programmers.

No, though it ought to be. We're trying to make it so. But in most shops
where I've worked before OMA, most shops my friends work at, many
places where we're brought in to teach, and the majority of shops where
other teachers I know teach, abstraction is not understood in the light of
dependency management and risk management. It's just a way of taking a wild
swag at the structure of a system.

Inheritance is typically used too much, and usually wrongly. It's most
frequently used to denote inappropriate dynamic set memberships, create
inappropriate couplings of orthogonal aspects of objects, or even worse as
"compilable comments".

If most programmers are taught how to do this in Intro To Programming 101,
then 101 needs a serious reworking. For details of how people typically do
this incorrectly, read "What Every Programmer Should Know About OOD",
chapters 8-12.

....

Tim

----8<-------------------------------

He is talking about high-end shops using Big Design Up Front to draw lots of
diagrams of their domain objects, and then linking these by inheritance,
such that Truck inherits Vehicle, regardless if your software is for a
carwash, a skateboard designer, or a wind-tunnel simulator. Then Truck might
not override any methods in Vehicle, and nobody cares. The code matches the
design, so progress is being made!
 
W

werasm

Earl said:
Not even sure they can. Now we know that you cannot call a protected
member of a base class other than on yourself, so this would not be
permitted:

No, sorry, of course you can't :). Can't call a protected member, and
the member is protected from the caller perpective, even though the
caller is derived, as we are not accessing through the derived
interface. That makes a protected and non-virtual (destructor) a
perfect option for a base.

Regards,

W
 
P

Phlip

Nobody seems to have commented on what struck me as the most fundamental
point about this function: it's not really A function at all -- to all
appearances, it's two entirely unrelated functions shoved together.

I did. I just didn't hit it first.

Interview tip: Go in order from most to least risk. Then don't bore the
interviewer with style details. They are most interested you won't cause
fires.
 
E

Earl Purple

Phlip said:
Interview tip: Go in order from most to least risk. Then don't bore the
interviewer with style details. They are most interested you won't cause
fires.

Particularly that you won't cause them to be fired or have you made
their boss if you appear to know more than them.

Welcome to the real world.
 
P

Phlip

Earl said:
Particularly that you won't cause them to be fired or have you made
their boss if you appear to know more than them.

Welcome to the real world.

You... you... mean there are companies out there that promote by merit?

Heaven!
 
K

Kai-Uwe Bux

Phlip said:
Kai-Uwe Bux wrote:
[snip]
You claimed: "you should not inherit from a
class with no virtual methods".

And C++ is multi-paradigm, so within the template paradigm, you should not
inherit except to specialize a member.

The fourier example did not do that. Generally, when inheriting from
unary_function you will not specialize any of the members of
unary_function.

Your example "overrode" operator(). I am aware it was not virtual, and
might not exist in the parent.

You are using the term "override" strangely. In my book you cannot override
something that isn't there to begin with. I think the standard uses the
word in the way I do. BTW, since you say operator() "might" not exist in
the parent: There is no doubt as to what exists in std::unary_function.
It's defined by the standard, and operator() is not a member.

Yet what you did was very similar to an override, from the caller's point
of view.

I see, you generalized your position to: one should not inherit unless one
overrides something (virtual or not) or adds something (which is what you
call overriding with quotation marks). In other words, one should not do

class X : public y {
...
}

unless X behaves at least a little different from Y. Well, I might grant you
that. However, In my eyes,

a) your advice has moved quote a bit from the original and
b) is more or less vacuous now.


I think you are trying to rephrase a piece of sound advice from the OO
paradigm in very abstract terms in order to make it applicable in other
contexts and styles of programming. I think you are carrying this to far
and it would be better to just qualify the original advice roughly like so:
when programming the OO style, you should rarely have a need for inheriting
from a class unless that class has virtual methods that you want to
override in derived classes. In that case, it is good policy to make the
destructor virtual.

Templates are another form of polymorphism, such that a call to an object
with a method, such as foo->bar(), or foo->operator(), will dispatch to
any number of different implementations.

This does not work in the fourier class example: if you have a pointer

unary_function<double,double>* f_ptr;

a call f_ptr->operator() will not compile. Also, your use of pointers and
your focus on the operator() indicates that you may have misunderstood the
point of the fourier class inheriting from std::unary_function. You will
almost never have a pointer of type unary_function<double,double>* or even
of type fourier*. These classes are meant to have value semantics. The main
purpose of doing

class fourier : public std::unary_function<double,double> {

unsigned long frequency;

public:

fourier ( unsigned long f )
: frequency ( f )
{}

double operator() ( double x ) const {
return ( std::sin( x * frequency ) );
}

};

instead of

class fourier {

unsigned long frequency;

public:

fourier ( unsigned long f )
: frequency ( f )
{}

double operator() ( double x ) const {
return ( std::sin( x * frequency ) );
}

};

is not to do anything about operator(). It's just so that you can do
something like:

fourier s3 ( 3 );
std::transform( v.begin(), v.end(),
std::eek:stream_iterator< fourier::result_type >( std::cout, " " ),
s3 );

The gain for maintainance is that you avoid magic types as in

std::transform( v.begin(), v.end(),
std::eek:stream_iterator< double >( std::cout, " " ),
s3 );

(and of course, there are some other uses of the result and argument types
as pertaining to template programming).

Similar remarks apply to inheritance from std::iterator. Here the main
purpose is to ease template deduction, e.g., based upon the iterator_tag so
that specialized version of algorithms can be chosen for random access
iterators. This is not really a polymorphism thing as the specialized
algorithms are mere optimizations of the generic versions obeying the same
semantics. Again, no overriding of members of the std::iterator class will
take place in the client code.

To use the word polymorphism as though these techniques are parallel to the
use of virtual methods, is misleading at best.

A class that exists only to provide a minor tweak to an existing class,
regardless where or how an override occurs. Brainless example: Inherit
std::string, and add a method ToUTF_16(). This overrides nothing
(syntactically or semantically), it simply makes the string class easier
to use in an application that likes UTF-16. I'm also not saying one should
do that. Only do it if it's convenient.

Thanks. Note that the fourier class from my example is no such thing.

This great post by Tim Ottinger says it best:

----8<-------------------------------


No, though it ought to be. We're trying to make it so. But in most shops
where I've worked before OMA, most shops my friends work at, many
places where we're brought in to teach, and the majority of shops where
other teachers I know teach, abstraction is not understood in the light of
dependency management and risk management. It's just a way of taking a
wild swag at the structure of a system.

Inheritance is typically used too much, and usually wrongly. It's most
frequently used to denote inappropriate dynamic set memberships, create
inappropriate couplings of orthogonal aspects of objects, or even worse as
"compilable comments".

If most programmers are taught how to do this in Intro To Programming 101,
then 101 needs a serious reworking. For details of how people typically do
this incorrectly, read "What Every Programmer Should Know About OOD",
chapters 8-12.

...

Tim

----8<-------------------------------

He is talking about high-end shops using Big Design Up Front to draw lots
of diagrams of their domain objects, and then linking these by
inheritance, such that Truck inherits Vehicle, regardless if your software
is for a carwash, a skateboard designer, or a wind-tunnel simulator. Then
Truck might not override any methods in Vehicle, and nobody cares. The
code matches the design, so progress is being made!

I see. However, these issues seem to be unrelated to the points that I
raised about policy based design where your original advice "one should not
inherit from classes that have no virtual methods" simply does not apply and
where your modified advice "one should not inherit without
overriding/specializing members" is equally misguided.


Best

Kai-Uwe Bux
 
E

Earl Purple

Kai-Uwe Bux said:
I see, you generalized your position to: one should not inherit unless one
overrides something (virtual or not) or adds something (which is what you
call overriding with quotation marks). In other words, one should not do

class X : public Y {
...
}

unless X behaves at least a little different from Y. Well, I might grant you
that. However, In my eyes,

You might do that actually if Y is a template and you want to forwardly
declare X without exposing the template. Of course if we were allowed
to forwardly declare typedefs (possibly with typename) that wouldn't be
a problem. Then we could just typedef the template to X and forwardly
declare that.
 
P

Phlip

Kai-Uwe Bux said:
The fourier example did not do that. Generally, when inheriting from
unary_function you will not specialize any of the members of
unary_function.

That's why I wrote "operator()...might not exist in the parent":
You are using the term "override" strangely.

And that's why I wrote "very similar to an override":
 
N

Noah Roberts

Kai-Uwe Bux said:
I see, you generalized your position to: one should not inherit unless one
overrides something (virtual or not) or adds something (which is what you
call overriding with quotation marks). In other words, one should not do

class X : public y {
...
}

unless X behaves at least a little different from Y. Well, I might grant you
that. However, In my eyes,

Well, that would not exactly help with regard to template programming.
For instance one couldn't do something like this:

template < typename D, typename U >
struct unit : dimension_type<D>, convertable<U>...
{
};

struct seconds : unit<time_d, seconds> {};
double const seconds::cvf::value = 1.0;

"seconds" doesn't add any behavior to the unit type (unless you
consider different conversion values an extension) and the unit type
adds nothing to its parents beyond the conglomeration of various
parents' features into one type. However, a decent syntax for creating
new unit types has been created and these static types can be used in
template programming in unit based dimensional analysis and custom
printing:

template < typename U >
struct unit_printer {}

template <>
struct unit_printer<seconds>
{
std::string operator () () { return "s"; }
};

So there can be numerous reasons to subclass something but not extend
it and with MI you can be extending simply through inheritance.

And it's Friday and I'm particularly out of it today or I'd give a
better argument but I think that saying, "never inherit without
extending or changing behavior," as a rule can be very limiting in many
aspects of C++ programming. It also seems to be less than accurate as
you can extend at least without inheriting. The more narrow guideline
given by Sutter and Myers (don't inherit unless you need to override
some polymorphic function) is more to the point but still fails in
light of certain idioms in generic programming where inheritance isn't
necissarily used according to the standard OO definition. For
instance, such a guideline would say never do the above, and maybe it
really isn't the best way to do what I want, but there is no reason not
to when your parents exist solely for the purpose of generating new
static types without having to type the same things repeatedly and/or
provide special compile time functionality to test types and compile in
different behavior based on the inherited metafunction members.
 
P

Phlip

Noah said:
And it's Friday and I'm particularly out of it today or I'd give a
better argument but I think that saying, "never inherit without
extending or changing behavior," as a rule can be very limiting in many
aspects of C++ programming. It also seems to be less than accurate as
you can extend at least without inheriting. The more narrow guideline
given by Sutter and Myers (don't inherit unless you need to override
some polymorphic function) is more to the point but still fails in
light of certain idioms in generic programming where inheritance isn't
necissarily used according to the standard OO definition.

The most general way to say it is "prefer weak coupling to strong coupling".
Delegation is weak, and the various forms of inheriting and specializing are
strong. Don't do it unless you get a benefit, and that benefit often appears
as an "override" of some kind.
For
instance, such a guideline would say never do the above, and maybe it
really isn't the best way to do what I want, but there is no reason not
to when your parents exist solely for the purpose of generating new
static types without having to type the same things repeatedly

If that is a "convenience class", then at least you _know_ that it is. You
are not going down the path of inheritance as an arbitrary form of
categorization, without any design value.
and/or
provide special compile time functionality to test types and compile in
different behavior based on the inherited metafunction members.

"different behavior" is the meaning of the "override" in the original
admonition.
 
N

Noah Roberts

Phlip said:
Noah Roberts wrote:

"different behavior" is the meaning of the "override" in the original
admonition.

However, in this case the "different behavior" is based on members that
are inherited, not overridden. For instance one might have the
following:

struct seconds : unit<time_d, seconds>, unit_is_static<seconds, true>
{};

template < typename U, bool is_static >
struct unit_is_static;

template <typename U>
struct unit_is_static<U, false> { struct is_static :
boost::mpl::bool_<false> {}; };

template <typename U>
struct unit_is_static<U, true> {
struct is_static : boost::mpl::bool_<true> {};
static U const value;
};

template <typename U>
U const unit_is_static<U, true>::value = U(); // necissary to access
double conversion

Now later you could very concievably use mpl::if_ based on the
"is_static" metafunction member. That member is an inherited member so
behavior doesn't change based on differences in the derived class but
by which class it inherits from.
 
P

Phlip

Noah said:
Now later you could very concievably use mpl::if_ based on the
"is_static" metafunction member. That member is an inherited member so
behavior doesn't change based on differences in the derived class but
by which class it inherits from.

Another important point is we are all discussing design at a level much
higher than the newbies who need to be taught "don't inherit unless you then
override a virtual method"... ;-)
 
N

Noah Roberts

Phlip said:
Another important point is we are all discussing design at a level much
higher than the newbies who need to be taught "don't inherit unless you then
override a virtual method"... ;-)

Actually I think that some of the paradigms and idioms beyond OO should
be taught to new C++ programmers. Concepts like policies are very
useful but unfortunately not taught in any class I am aware of. Hell,
the concept of a concept was never taught to me and this seems rather
fundamental in anything remotely interesting in C++.
 
K

Kai-Uwe Bux

Phlip said:
Another important point is we are all discussing design at a level much
higher than the newbies who need to be taught "don't inherit unless you
then override a virtual method"... ;-)

Newbies do not need to be lied to :) Your sound piece of advice would be
more helpful to newbies if it came with appropriate qualification: "in OO
design, don't inherit unless you then override a virtual method." You do
not help newbies by giving advice that is implicitly biased toward one of
the various paradigms supported by C++. That way you would be narrowing the
perspective of the newbies on C++ instead of opening up their view.


Best

Kai-Uwe Bux
 
P

Phlip

Kai-Uwe Bux said:
Newbies do not need to be lied to :) Your sound piece of advice would be
more helpful to newbies if...

You have a typo there. You were trying to write "Scott Meyers's, Herb
Sutter's, and Andrei Alexandrescu's advice would be helpful to newbies..."
 
K

Kai-Uwe Bux

Phlip said:
You have a typo there. You were trying to write "Scott Meyers's, Herb
Sutter's, and Andrei Alexandrescu's advice would be helpful to newbies..."

Your appeal to authority is well taken. However, should you mean to imply
that, say, Alexandrescu has given that advice of yours without
qualification somewhere in his writings, I would (a) ask for an actual
quote, and (b) should you be able to provide one, maintain, that he slipped
there. However, from his writings I have read, I would be very surprised if
he actually stated an unqualified oversimplification like "do not inherit
unless you override a virtual function".


Best

Kai-Uwe Bux
 
D

Digital Puer

Stuart said:
The comma operator isn't involved in member initialization lists, that's
just part of C++ syntax (like the semi-colon at the end of each
statement). Member initializations is generally to be prefered, since
members that are initialized in the member initialization list are
initialized by their constructors. If a member is not mentioned in the
member initialization list, it's default constructor will be invoked
(which would assign values that will most likely get overwritten in the
constructor anyway).
Consider the following program and its output:

#include <iostream>
class A
{
int m_a;
public:
A ()
: m_a (0)
{ std::cout << "default constructor of A invoked.";
std::cout << std::endl;}
A (int p_a)
: m_a (p_a)
{ std::cout << "initialization constructor of A invoked.";
std::cout << std::endl;}
A& operator= (int p_a)
{ m_a = p_a;
std::cout << "Assignment operator for A invoked.";
std::cout << std::endl;
return *this;}
};

class B
{
A m_Aobj;
public:
B (int p_b)
: m_Aobj (p_b)
{}
};

class C
{
A m_Aobj;
public:
C (int p_b)
{ m_Aobj = p_b; }
};

int main ()
{
B b (0);
C c (0);
return 0;
}

In those cases where you have no default constructor available, you
cannot get around member initialization lists.


Thank you for the excellent example above. The output I
get is that for B, the initialisation contructor is called,
whereas for C, the default constructor is called, followed
by a call to the assignment operator function.

This is a bit puzzling for the case of B. I thought that before
B's constructor is entered, all of B's member variables must
have already been constructed, meaning that A's
default constructor should have already been called.
Is this not the case when an initialisation list is
present that uses a member variable?
 

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,774
Messages
2,569,598
Members
45,156
Latest member
KetoBurnSupplement
Top