Showing posts with label class. Show all posts
Showing posts with label class. Show all posts

Tuesday, February 07, 2006

Final classes

i camed across a nice C++ question which shows the final class principle (as I read here: http://www.codeguru.com/Cpp/Cpp/cpp_mfc/stl/article.php/c4143/ it seems that the final class specifier exist in Java)

1. A class that is not derived from a class nor is intended to have any class derived from it is an example of what type of class?
A. A concrete class
B. An abstract class
C. A base class
D. A virtual class
E. A final class

A final class is a class from which you dont derive (if you try to, the objects of the derived classes cant be constructed since the compiler will complain about this).
So how can you design such class ?

In C++ there is no keyword (final) to declare a class as non-inheritable as in Java.
But then C++ has its own features which you may exploit to get the same behaviour.
Basically it uses concepts of private constructor and friend class.

For example, lets try to create a class called CFinal.
class CFinal
{
// member data ...
}

We want to be able to use this class, of course, to create objects from it but what we dont want is to be able to use this class as a base for other classes. Or, in other words, if anybody creates a class derived from CFinal, it will not be able to create objects of this derived class.

The first idea will be to make the CFinal constructors private but doing so will prevent us to create objects of it. We need to way to be able to make construction unavailable for the derived classes only.

The solution:
We use the fact that in C++, if a class has a friend class, the friendship is not inheritate in any way; if the friend class has a derived class, this does not mean that the derived class is a friend also for the initial class

class Temp
{
private: ~Temp() { };
friend class CFinal;
};

class CFinal : virtual public Temp
{. . .};

Again, if we have this:
class CDerived : CFinal
{. . .}
CDerived is NOT a friend of the Temp class.

So that now if some one tries to inherit from this CFinal class, compilation gives error as this class cannot call constructor of its super class i.e.
Yet, you can create CFinal objects, because as a friend of the Temp class, your class has access to the private constructor.

The whole idea i got it from this article:
http://www.codeguru.com/Cpp/Cpp/cpp_mfc/stl/article.php/c4143/
"Working with the Final Class in C++" by Zeeshan Amjad

Friday, February 03, 2006

name hidding again

the name hidding seems to be pretty compiler specific unfortunately.
for example:
struct A
{
int x;
};

struct B: A
{
int x;
};

struct C: A, B
{
void f() { x = 0; }
};

int main()
{
C i;
i.f();
}
here
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr138.htm

it says that "The assignment x = 0 in function C::f() is not ambiguous because the declaration B::x has hidden A::x."

when i see this I was confused, what the hell; indeed, B::x hiddens A::x but since i is of C type, both B::x and A::x are available to C.
i just put this code in Visual C++ 2003 and it gives (of course) the compile error:

e:\Projects\test2\test2\test2.cpp(15): error C2385: ambiguous access of 'x' in 'C' could be the 'x' in base 'A::x' or the 'x' in base 'B::x'

now, i bet that there are many things like this, compiler specific (the C++ ref from the link is for the IBM compiler);
and this makes life harder.

on the next interview, you can be very smart and write that this code works OK; if you get a mocking smile, you can ask politely "do you have an IBM C compiler ?"

dominance

there is a thing called name dominance in C++. consider this:

class A
{
public:
int f() { printf("\nA::f()"); return 1; }
};

class B : virtual public A
{
public:
void f() { printf("\nB::f()"); } // hides the int A::f()
};

class C : virtual public A
{
};

class D : public B, public C
{

};

the hierarchy is this:

A
B C
D

and D has only one copy of A in memory (beacuse B and C inherits virtual from A)
now:

D d;
d.f();

f() is not ambigous because void B::f() hides int A::f().
d.f() will get B:f() called because D derives from B which hides the A::f().
Note that this is not ambigous beacause the A is virtual inherited in B and C and this makes that the d object to contain only one instance of A within it. if, for example, B or C will not have virtual inherited from A (either one of them or both) this will mean that d will have 2 A objects within it.

now, consider this case:

C *d = new D;
d->f();

d->f() will get A:f() called.
Why is that stays in the mechanism of calling a function.
in the first case, when d is a D object:
D d;
when you call
d.f();
the function which is get called is determined at compile time; the function called is the D::f()

in the 2nd case, when
C *d = new D;
when you call
d->f();
the object d is really a D object but since the function called is not virtual (not taken from the vtable) the function called is from C object scope, which is A::f(), beacuse C inherits A::f().

hence, the rule is this:
if the function called is not virtual, the function called is the one from the type of the object type.

public inheritance broken

consider this:

class A
{
public:
void f();
void f(int x);
}

class B : public A
{
public:
void f(); // hides all A::f() overloads
}

you have an object
B b;
and you want to call the f(int x)
b.f(1);

normally you would think that since B inherits A, it inherits all the A functions, which is true, but not when for overloading.
the name f() declared in B class will override any base names. this includes the following case:

class A1
{
public:
void f();
}

class B1 : public A1
{
public:
int f; // hides all A1 'f'names
}

if you try to write the call:
b1.f();
you will get a compile error

as within a class, the names in a inheritance tree shoule be unique;

if you want to preserve the is-a relationship(a derived class should access all public inherited base class functionality) then you need to declare in the derived class the base functions by using 'using' declarator:

class B : public A
{public:
void f(); // hides all A 'f'names
using A::f; //OK, now we have the A::f(int x) within the B
}

b.f(1);

Note that 'using' works for the seeing in derived classes the overloads, it will not work for the 2nd case, when the base class is hiding a base class function by a member variale (because its not possible to have within the same class B 2 names f() and int f)

this is a problem described in Effective C++. Item 33. 'Avoid hiding inherited names'

Tuesday, January 10, 2006

how vtable work

here is a nice article about how vtables work
its good coz it goes into asm language and explains whats really goin on
http://msdn.microsoft.com/msdnmag/issues/0300/c/
if u find any similar article on this subject, pls reply here.