Interview questions

S

Siemel Naran

pembed2003 said:
possible function definition. The lookup (not sure how the actual
virtual table is implemented but if it's something like a binary tree
or hash table then...) thus takes appr. the same amount of time.

I think the virtual table is implemented as an array of pointers to
function. It works well whenever a pointer to a function of any signature
has the same size, which is normally the case. If you have a class as
follows.

Note what follows is a typical implementation, not what the standard
mandates.

class Foo {
virtual ~Foo();
virtual int f(int, int);
virtual double g(int) const;
};

then the virtual table is an array of length 3. Each element in the array
is a function pointer, but each function pointer is a different signature!
Sure, the compiler can do that, but we can't!

Thus, there are 3 C style functions.

void __Foo_destructor(Foo *);
int __Foo_f(Foo *, int, int);
double __Foo_g(const Foo *, int);

and the virtual table is

typedef void (* __FunctionPtr)(); // pointer to non-member function

typedef __FunctionPtr[3] __Foo_vtable_type; // typedef for array of 3
function pointers
__Foo_vtable_type __Foo_vtable = {
(__FunctionPtr)(&Foo_destructor),
(__FunctionPtr)(&Foo_f),
(__FunctionPtr)(&Foo_g)
};

When you say foo->g(3) the compiler knows you mean the element
__Foo_vtable[2] and that it's real signature is double (*)(const Foo *,
int). Thus the call translates to

{
typedef double (* _Foo_vtable2)(const Foo *, int);
const __Foo_vtable_type& __vtable = foo->__vpointer;
_Foo_vtable2 __tmp = (_Foo_vtable2)(__vtable[2]);
(*__tmp)(foo, 3);
}
 
R

Richard Herring

pembed2003 said:
Hi,
As a new C++ programmer, I found your question very interesting. I
think it takes about the same time to find and invoke
base78::something() and base999999::something(), right? Because each
object carries a virtual table pointer (if the class has at least one
virtual function) with an bunch of virutal function pointers to the
possible function definition. The lookup (not sure how the actual
virtual table is implemented but if it's something like a binary tree
or hash table then...)

Unless you have virtual inheritance, there's no need for anything so
complicated - it's probably just a linear array, since the offset of
each function's entry in it is known at compile time.
 
R

Rolf Magnus

Ioannis said:
Here is one I would ask:


Is the following code guaranteed to be safe and portable?

You should expect a "no", even from someone who doesn't actually know
the answer ;-)
 
J

JKop

pembed2003 posted:
JKop said:
JKop posted:
Peter Koch Larsen posted:

I do believe that that question is a very bad one. The code above
is obviously portable, but these casts do confuse. What on earth
are you going to do with v? Any programmers instinct is that code
must have "a use", and it is quite difficult to see what you could
portable do with v.

What ever happened to just having fun?

int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23 ,3 };

char jack reinterpret_cast<char>(monkeys[5]);
TYPO



cout << jack; }

Hi,
I am trying to compile your code using g++ in FreeBSD. g++ -v gives:

gcc version 2.95.3 20010125 (prerelease)

the whole program looks like:

#include<iostream>
using namespace std;
void main(void){
int monkeys[8] = {3,4,3,2,2,34,23,3};
char jack = reinterpret_cast<char>(monkeys[5]);
cout<<jack<<endl;
}

but it won't compile:

g++ tmp.cpp -o tmp
tmp.cpp: In function `int main(...)':
tmp.cpp:5: reinterpret_cast from `int' to `char'

I am trying to understand your program but failed. Can you tell me
what you are trying to demonstrate and what the program is supposed to
do? I am trying to learn.

Thanks!

I myself amn't too familiar with casts.

For example, at the moment I only use:

int j = (int)some_float;

I'm not fully certain about:

reinterpret_cast
dynamic_cast
static_cast


I've gone to msdn.microsoft.com looking for an explanation but I find it
hard to understand.


-JKop
 
I

Ioannis Vranos

JKop said:
I myself amn't too familiar with casts.

For example, at the moment I only use:

int j = (int)some_float;

I'm not fully certain about:

reinterpret_cast
dynamic_cast
static_cast



static_cast converts between related types, such as one pointer to
another in the same class hierarchy. The conversion is compile-time checked.

reinterpret_cast handles conversions between unrelated types.

dynamic_cast is a form of run-time checked conversion, and the type of
the object must be *polymorphic*, that is to have virtual functions.


So if in an hierarchy and you want to perform (compile-time checked)
related type conversion use static_cast.

If you want to check at runtime if the types are related use
dynamic_cast provided that the object is polymorphic.

If you want to convert between oranges and potatoes use reinterpret_cast.






Regards,

Ioannis Vranos
 
I

Ioannis Vranos

JKop said:
What ever happened to just having fun?

int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23 ,3 };

char jack reinterpret_cast<char>(monkeys[5]);

cout << jack;
}



Think calmly what are you doing.


#include <iostream>

int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23};

char jack=monkeys[5];

std::cout << jack;
}


Of course this will work only for the values of int that can be
represented in char.


There are people who want to mark that the above is performing an ugly
operation (which it does, implicit type conversions are a C inheritance)
you can use static_cast:

char jack=static_cast<char>(monkeys[5]);






Regards,

Ioannis Vranos
 
J

JKop

Ioannis Vranos posted:
static_cast converts between related types, such as one pointer to
another in the same class hierarchy. The conversion is compile-time
checked.

reinterpret_cast handles conversions between unrelated types.

dynamic_cast is a form of run-time checked conversion, and the type of
the object must be *polymorphic*, that is to have virtual functions.


So if in an hierarchy and you want to perform (compile-time checked)
related type conversion use static_cast.

If you want to check at runtime if the types are related use
dynamic_cast provided that the object is polymorphic.

If you want to convert between oranges and potatoes use
reinterpret_cast.






Regards,

Ioannis Vranos

Is reinterpret_cast stupid, by which I mean does it do any processing at
all? For example, take the following:

unsigned char jk;

reinterpret_cast<unsigned long&>(jk) = 4000000000UL;


Will it do any truncation or anything? Or will it just try to stuff a
possibly 32-Bit value into a possibly 8-Bit space, effectively writing into
unallocated memory? If so...

Then what exactly is a "variable". Is it essentially just a chuck of
allocated memory of the size of the type which is used to define it. In my
previous example, does the compiler just take j as an address, and then just
work with in a way which is defined by the type which is used to
access/alter it?

Imagine there was no such thing as unions in C++, would the following code
work. Firstly, here's the union form of it:

union {

double double_data;
int int_data;
char char_data;
unsigned long unsigned_long_data;
void* void_pointer_data;
float* float_pointer_data;

}

//Now some funky code:

double recpoo;

reinterpret_cast<float*&>(recpoo) = &some_global_float;

reinterpret_cast<unsigned long&>(recpoo) = 4000000000UL;

reinterpret_cast<char&>(recpoo) = 'g';



Next topic, what kind of cast is the following:

int j;

void* t = (void*)&j;


Thanks for the help!


-JKop
 
J

JKop

Ioannis Vranos posted:
JKop said:
What ever happened to just having fun?

int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23 ,3 };

char jack reinterpret_cast<char>(monkeys[5]);

cout << jack; }



Think calmly what are you doing.


#include <iostream>

int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23};

char jack=monkeys[5];

std::cout << jack;
}


Of course this will work only for the values of int that can be
represented in char.


There are people who want to mark that the above is performing an ugly
operation (which it does, implicit type conversions are a C inheritance)
you can use static_cast:

char jack=static_cast<char>(monkeys[5]);


Opps, that wasn't my intention, this was my intention:


int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23};

std::cout << reinterpret_cast<char&>(monkeys[5]);
}


-JKop
 
I

Ioannis Vranos

JKop said:
Is reinterpret_cast stupid, by which I mean does it do any processing at
all? For example, take the following:

unsigned char jk;

reinterpret_cast<unsigned long&>(jk) = 4000000000UL;




Reinterpret_cast performs a value conversion. It doesn't modify anything
on the implementation of jk. So the above has essentially no effect. It
is as if you had written


reinterpret_cast<unsigned long&>(jk);


Imagine there was no such thing as unions in C++, would the following code
work. Firstly, here's the union form of it:

union {

double double_data;
int int_data;
char char_data;
unsigned long unsigned_long_data;
void* void_pointer_data;
float* float_pointer_data;

}

//Now some funky code:

double recpoo;

reinterpret_cast<float*&>(recpoo) = &some_global_float;

reinterpret_cast<unsigned long&>(recpoo) = 4000000000UL;

reinterpret_cast<char&>(recpoo) = 'g';



No, reinterpret_cast has no effect in the above.


Next topic, what kind of cast is the following:

int j;

void* t = (void*)&j;



C-style. The most unsafe.






Regards,

Ioannis Vranos
 
M

Mike Wahler

E. Robert Tisdale said:
I am serious. No one guarantees that
C++ programs which comply with ANSI/ISO standards are portable --
not compiler developers
and certainly not the ANSI/ISO C++ standard itself.

Yes, the standard does guarantee that compliant code
is portable to a compliant implementation. Of course
compliant implementations do not exist for all platforms.
If you write an ANSI/ISO standard compliant C++ program
and claim that is ports to *any* platform,

"Any platform" would be a an impossible to make guarantee.
"Any compliant implementation" is entirely possible.
then *you*
and no one else will be held legally liable if it doesn't.
If you decide to make such a claim,

I've never seen such a claim.
then you had better test it on the target platform first.


Of course testing is always a good idea.

But 'porting' in the context of standard C++ means
among implementations, not 'platforms'.

-Mike
 
I

Ioannis Vranos

JKop said:
Opps, that wasn't my intention, this was my intention:


int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23};

std::cout << reinterpret_cast<char&>(monkeys[5]);
}




That's dangerous, you are passing to ostream::eek:perator<<() a char & to a
temporary.


What you probably meant to do is:



#include <iostream>


int main(void)
{
int monkeys[7] = { 3, 4 ,3, 2 ,2 ,34, 23};

std::cout << static_cast<char>(monkeys[5]);
}



Also reinterpret_cast is the *last* of castings you should use, and
except of that, always try to avoid using castings at all.






Regards,

Ioannis Vranos
 
J

JKop

Ioannis Vranos posted:


Opps!! static_cast was what I was after:


unsigned char jk;

static_cast<unsigned long&>(jk) = 4000000000UL;


Here's an excerpt from http://msdn.microsoft.com/library/default.asp?
url=/library/en-us/vccelng/htm/express_74.asp :

This behavior also applies to types other than class types. For instance,
static_cast can be used to convert from an int to a char. However, the
resulting char may not have enough bits to hold the entire int value. Again,
it is left to the programmer to ensure that the results of a static_cast
conversion are safe.



union {

double double_data;
int int_data;
char char_data;
unsigned long unsigned_long_data;
void* void_pointer_data;
float* float_pointer_data;

} monkey;

//Now some funky code:

double recpoo;

static_cast<float*&>(recpoo) = &some_global_float;

static_cast<unsigned long&>(recpoo) = 4000000000UL;

static_cast<char&>(recpoo) = 'g';


-JKop
 
E

E. Robert Tisdale

Siemel said:
I think [that]
the virtual table is implemented as an array of pointers to functions.

A jump table.
It works well whenever a pointer to a function
of any signature has the same size, which is normally the case.
If you have a class as follows.


Note what follows is a typical implementation,
not what the standard mandates.

class Foo { public:
virtual ~Foo(void);
virtual int f(int, int);
virtual double g(int) const;
};

then the virtual table is an array of length 3.
Each element in the array is a function pointer,
but each function pointer is a different signature!
Sure, the compiler can do that, but we can't!
cat main.cc
class Foo {
private:
int F;
public:
virtual ~Foo(void) { }
virtual int f(int i, int j) {
int t = F;
F = i + j;
return t; }
virtual double g(int k) const { return F + k; }
};

int funny(Foo& foo) {
return foo.f(13, 23);
}

int main(int argc, char* argv[]) {
Foo foo;
return funny(foo);
}
 
I

Ioannis Vranos

JKop said:
Opps!! static_cast was what I was after:


unsigned char jk;

static_cast<unsigned long&>(jk) = 4000000000UL;




That doesn't compile. However as I said in another message, casts are to
be used on the assigned value, not the object that gets that value.






Regards,

Ioannis Vranos
 
I

Ioannis Vranos

Ioannis said:
Reinterpret_cast performs a value conversion. It doesn't modify anything
on the implementation of jk. So the above has essentially no effect. It
is as if you had written



unsigned char jk;

reinterpret_cast<unsigned long&>(jk);

jk=4000000000UL;






Regards,

Ioannis Vranos
 
E

E. Robert Tisdale

Mike said:
Yes, the standard does guarantee that
compliant code is portable to a compliant implementation.

Please cite and quote the passage in the standard
that "guarantees" this.
Of course,
compliant implementations do not exist for all platforms.

In fact, no compliant implementations exist for any platform.
The claim is vacuous.

The license that came with my GNU C++ compiler
includes the following disclaimer:

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

When you installed your compiler,
you were obliged to accept "similar terms and conditions".
Compliance with ANSI/ISO C++ standards is voluntary.
Programmers have no legal recourse against compiler developers
if their compliant programs will not port.
 
J

JKop

JKop posted:
This behavior also applies to types other than class types. For
instance, static_cast can be used to convert from an int to a char.
However, the resulting char may not have enough bits to hold the entire
int value. Again, it is left to the programmer to ensure that the
results of a static_cast conversion are safe.

This excerpt seems to be bullshit. Try running the following on your system.
If the above was true, then the end output should be all 255, assuming that
your system is 8-Bit char and 32-Bit long.


#include <iostream>

int main(void)
{
unsigned char jk[4] = { 0, 0, 0, 0 };

std::cout << "jk[0] == " << (int)jk[0]
<< "\r\njk[1] == " << (int)jk[1]
<< "\r\njk[2] == " << (int)jk[2]
<< "\r\njk[3] == " << (int)jk[3]
<< std::endl;

std::system("PAUSE");

static_cast<unsigned long>(jk[0]) = 0xFFFFFFFF;

std::cout << "jk[0] == " << (int)jk[0]
<< "\r\njk[1] == " << (int)jk[1]
<< "\r\njk[2] == " << (int)jk[2]
<< "\r\njk[3] == " << (int)jk[3]
<< std::endl;

std::system("PAUSE");
}


static_cast isn't as special as I thought. I thought it would've been able
to achieve what unions do. Ah well!


-JKop
 
N

Niklas Borson

Siemel Naran said:
I think the virtual table is implemented as an array of pointers to
function. It works well whenever a pointer to a function of any signature
has the same size, which is normally the case. If you have a class as
follows.

Note what follows is a typical implementation, not what the standard
mandates.

class Foo {
virtual ~Foo();
virtual int f(int, int);
virtual double g(int) const;
};

then the virtual table is an array of length 3. Each element in the array
is a function pointer, but each function pointer is a different signature!
Sure, the compiler can do that, but we can't!

Sure we can. Here's how you might do something very similar in C. I haven't
tried compiling it, but I've done similar things in the past. (For example,
for anyone masochistic enough to do COM programming in C, the code usually
looks pretty similar to this once you strip away all the macros.)

/*
* Declaration of Foo in foo.h
*/
struct Foo_VTable;

typedef struct tagFoo {
Foo_VTable* _vptr;
} Foo;

struct Foo_VTable {
void (*dtor)(Foo* _this);
int (*f)(Foo* _this, int, int);
double (*g)(Foo const* _this, int);
};

void Foo_ctor(Foo* _this);
void Foo_dtor(Foo* _this);
int Foo_f(Foo* _this, int, int);
double Foo_g(Foo const* _this, int);

/*
* Implementation of Foo in foo.c.
*/
struct Foo_VTable const g_Foo_vtable = {
&Foo_dtor,
&Foo_f,
&Foo_g
};

void Foo_ctor(Foo* _this)
{
_this->_vfptr = &g_Foo_vtable;
}

/* etc. for other member functions */

/*
* The following is essentially equivalent to what
* in C++ would be written:
* Foo foo;
* foo.f(0, 0);
* foo.g(0);
*/
Foo foo;
Foo_ctor(&foo);
foo._vfptr->f(&foo, 0, 0);
foo._vfptr->g(&foo, 0);
foo._vfptr->dtor();

/*
* The following is essentially equivalent to what
* in C++ would be written:
* Foo* foo = new Foo();
* foo->f(0, 0);
* foo->g(0);
* delete foo;
*/
Foo* foo = malloc(sizeof(Foo));
Foo_ctor(foo);
foo->_vfptr->f(foo, 0, 0);
foo->_vfptr->g(foo, 0);
foo->_vfptr->dtor();
free(foo);
 
E

E. Robert Tisdale

Niklas said:
Sure we can.
Here's how you might do something very similar in C.
I haven't tried compiling it,
but I've done similar things in the past. (For example,
for anyone masochistic enough to do COM programming in C,
the code usually looks pretty similar to this
once you strip away all the macros.)

// Declaration of Foo in foo.h

struct Foo_VTable;

typedef struct tagFoo {
Foo_VTable* _vptr;

Should be

const void* _vptr;
} Foo;

struct Foo_VTable {
void (*dtor)(Foo* _this);

Should be

void (*dtor)(const Foo*);

You need to be able to destroy const objects
as well as variable objects with the same destructor!
int (*f)(Foo* _this, int, int);
double (*g)(Foo const* _this, int);
};

void Foo_ctor(Foo* _this);
void Foo_dtor(Foo* _this);

Should be

void Foo_dtor(const Foo*);
int Foo_f(Foo* _this, int, int);
double Foo_g(Foo const* _this, int);


// Implementation of Foo in foo.c.

#include "foo.h"

// Actually, the struct Foo_VTable definition
// belongs here and *not* in the public header file.
struct Foo_VTable const g_Foo_vtable = {
&Foo_dtor,
&Foo_f,
&Foo_g
};

void Foo_ctor(Foo* _this) {

_this->_vfptr = &g_Foo_vtable;
}

// etc. for other member functions


// The following is essentially equivalent to what
// what in C++ would be written:
// Foo foo;
// foo.f(0, 0);
// foo.g(0);

Foo foo;
Foo_ctor(&foo);
foo._vfptr->f(&foo, 0, 0);
foo._vfptr->g(&foo, 0);
foo._vfptr->dtor();

Should be

foo._vfptr->dtor(foo);
// The following is essentially equivalent to what
// in C++ would be written:
// Foo* foo = new Foo();
// foo->f(0, 0);
// foo->g(0);
// delete foo;

Foo* foo = malloc(sizeof(Foo));
Foo_ctor(foo);
foo->_vfptr->f(foo, 0, 0);
foo->_vfptr->g(foo, 0);
foo->_vfptr->dtor();

Should be

foo->_vfptr->dtor(foo);
free(foo);

Now suppose that you want to *derive* Bar from Foo

// interface
#include "foo.h"

typedef struct Bar {
Foo foo;
} Bar;

Bar Bar_ctor(void);
void Bar_dtor(const Bar*);
int Bar_f(Bar*, int, int);
double Bar_g(Foo const* _this, int);
char* Bar_hello(const Bar*);

// implementation
#include "bar.h"

struct Bar_VTable {
void (*dtor)(const Bar*);
int (*f)(Bar*, int, int);
double (*g)(Bar const*, int);
char* hello(const Bar*);
} Bar_VTable;

const Bar_VTable g_Foo_vtable = {
Bar_dtor,
Bar_f,
Bar_g,
Bar_hello
};

Bar Bar_ctor(void) {
Bar bar;
bar.foo._vptr = (const void*)(&g_Foo_vtable);
return Bar;
}

// etc.

// usage
Bar bar = Bar_ctor();
Foo* pBar = (Foo*)(&bar);
((const struct Foo_VTable*)(pBar->foo._vfptr))->f(pBar, 0, 0);
((const struct Foo_VTable*)(pBar->foo._vfptr))->g(pBar, 0);
((const struct Foo_VTable*)(pBar->foo._vfptr))->dtor(pBar);
 
P

Peter Koch Larsen

Ioannis Vranos said:
But I forgot to give you a reference. "The C++ Programming Language" 3rd
Edition or Special Edition, page 324.






Regards,

Ioannis Vranos

Well.... however brilliant "The C++ Programming Language" might be, it can
not answer your question.
You better consult the C++ standard for for answers to questions of that
caliber.


/Peter
 

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,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top