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 ?"
Friday, February 03, 2006
the best C++ reference
i dont think there is a better reference on C++ then these:
Effective & More Effective C++
C++ Programming Language
C++ Faqs
Effective STL
and... MSDN C++ reference
MSDN yes, its really good; it has many C++ little language stuff you forget over time; although its short in description, if you have the time and patience, its good to read from time to time to refresh the concepts and things, but read it from start to end; it has many gritty things I (you) at time pass by, forget;
also, it has many things you find in C++ books (but explained more better in books).
btw, many of the stuff from my blog can be found in the MSDN C++ reference :D so why am I still writing here ? because its a good practice to actually explain in writing for myself different stuff
Effective & More Effective C++
C++ Programming Language
C++ Faqs
Effective STL
and... MSDN C++ reference
MSDN yes, its really good; it has many C++ little language stuff you forget over time; although its short in description, if you have the time and patience, its good to read from time to time to refresh the concepts and things, but read it from start to end; it has many gritty things I (you) at time pass by, forget;
also, it has many things you find in C++ books (but explained more better in books).
btw, many of the stuff from my blog can be found in the MSDN C++ reference :D so why am I still writing here ? because its a good practice to actually explain in writing for myself different stuff
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.
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'
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'
Thursday, February 02, 2006
Data alignment
Data alignment is (very?) important; or at least is very important to be aware of it when you write your code.
Consider this:
struct Broadcast
{
char timezone; // 1 byte data
int frequency; // 4 byte data
short timeofday; // 2 byte data
};
applying the sizeof operator on the Broadcast (sizeof can be applied to a class) the result is 12 on a 32 bit operating system.
this is because compilers usually round up a data structure's size to make it divisible by 2, 4, or 8—depending on the hardware's memory alignment requirements.
Most CPUs require that objects and variables reside at particular offsets in the system's memory. For example, 32-bit processors require a 4-byte integer to reside at a memory address that is evenly divisible by 4. This requirement is called "memory alignment". Thus, a 4-byte int can be located at memory address 0x2000 or 0x2004, but not at 0x2001. On most Unix systems, an attempt to use misaligned data results in a bus error, which terminates the program altogether. On Intel processors, the use of misaligned data is supported but at a substantial performance penalty.
Secondly, compilers insert padding bytes between data members to ensure that each member's address is properly aligned. The problem is that when members are declared in a random order, the compiler may need to insert more padding bytes between them, thereby inflating the data structure's size.
The padding which compiler is doing is because each data type must be in memory at memory addressed which are divisible by their own size.
for example:
an int object should be always at a 4 divisible memory address
a short object should be always at a 2 divisible memory address
an double object should be always at a 8 divisible memory address
Hence Broadcast after compilation will be like this:
struct Broadcast
{
char timezone; // 4 byte data (padded with 3 bytes)
int frequency; // 4 byte data
short timeofday; // 4 byte data (padded with 2 bytes)
};
without padding the data looks like this:
0 1 5 7 8 12
1 1111 11 1 1111 1 1
this is taken from this:
http://www.devx.com/cplus/10MinuteSolution/20676/1954?pf=true
also look here
http://www.cs.unt.edu/~srt/2010.s99/alignorder.html
you can think that even if you try to arrange in this optimized way the structures, the processor might take some time for example for this:
struct
{
char timezone; // 1 byte data //gets 1 padding byte
short timeofday; // 2 byte data -> these 2 will get into 4 byte memory location
int frequency; // 4 byte data
}
which is properly aligned (each member is on a good memory boundary)
you can think that, on a 32 bit processor for example, reading the short variable within the 4 byte address( the 4 byte memory location will contain the char, a padding byte and the 2 bytes from short ) might take some time, but it doesnt.
the main point of this alignament is this: the goal is to have a structure where member data does not get bad aligned:
when a variable value needs to be read, the CPU should not be forced (due to bad alignament) to read the value from 2 memory locations (on the 32 bit CPU).
for example, if an int (4bytes) variable is not on a memory address divisible by 4, this means that the variable 'stay' in 2 memory addresses.
the CPU in this case needs to fetch 2 memory addresses, extract the parts from each other and concatenate them in order to get the value.
the compiler by default (if you dont change the default #pragma) will try to aligned the structs on a 2, 4 or 8 respectively boundary in order to avoid the CPU to get into the above described overhead; what it does not do is to arrange for you the structs members such that to take less size (which you should do).
Consider this:
struct Broadcast
{
char timezone; // 1 byte data
int frequency; // 4 byte data
short timeofday; // 2 byte data
};
applying the sizeof operator on the Broadcast (sizeof can be applied to a class) the result is 12 on a 32 bit operating system.
this is because compilers usually round up a data structure's size to make it divisible by 2, 4, or 8—depending on the hardware's memory alignment requirements.
Most CPUs require that objects and variables reside at particular offsets in the system's memory. For example, 32-bit processors require a 4-byte integer to reside at a memory address that is evenly divisible by 4. This requirement is called "memory alignment". Thus, a 4-byte int can be located at memory address 0x2000 or 0x2004, but not at 0x2001. On most Unix systems, an attempt to use misaligned data results in a bus error, which terminates the program altogether. On Intel processors, the use of misaligned data is supported but at a substantial performance penalty.
Secondly, compilers insert padding bytes between data members to ensure that each member's address is properly aligned. The problem is that when members are declared in a random order, the compiler may need to insert more padding bytes between them, thereby inflating the data structure's size.
The padding which compiler is doing is because each data type must be in memory at memory addressed which are divisible by their own size.
for example:
an int object should be always at a 4 divisible memory address
a short object should be always at a 2 divisible memory address
an double object should be always at a 8 divisible memory address
Hence Broadcast after compilation will be like this:
struct Broadcast
{
char timezone; // 4 byte data (padded with 3 bytes)
int frequency; // 4 byte data
short timeofday; // 4 byte data (padded with 2 bytes)
};
without padding the data looks like this:
0 1 5 7 8 12
1 1111 11 1 1111 1 1
this is taken from this:
http://www.devx.com/cplus/10MinuteSolution/20676/1954?pf=true
also look here
http://www.cs.unt.edu/~srt/2010.s99/alignorder.html
you can think that even if you try to arrange in this optimized way the structures, the processor might take some time for example for this:
struct
{
char timezone; // 1 byte data //gets 1 padding byte
short timeofday; // 2 byte data -> these 2 will get into 4 byte memory location
int frequency; // 4 byte data
}
which is properly aligned (each member is on a good memory boundary)
you can think that, on a 32 bit processor for example, reading the short variable within the 4 byte address( the 4 byte memory location will contain the char, a padding byte and the 2 bytes from short ) might take some time, but it doesnt.
the main point of this alignament is this: the goal is to have a structure where member data does not get bad aligned:
when a variable value needs to be read, the CPU should not be forced (due to bad alignament) to read the value from 2 memory locations (on the 32 bit CPU).
for example, if an int (4bytes) variable is not on a memory address divisible by 4, this means that the variable 'stay' in 2 memory addresses.
the CPU in this case needs to fetch 2 memory addresses, extract the parts from each other and concatenate them in order to get the value.
the compiler by default (if you dont change the default #pragma) will try to aligned the structs on a 2, 4 or 8 respectively boundary in order to avoid the CPU to get into the above described overhead; what it does not do is to arrange for you the structs members such that to take less size (which you should do).
Monday, January 16, 2006
C++ articles
here i will keep all interesting C++ articles:
article#1
--------------------------------------------------------------------------
C++: Under the Hood
Jan Gray
March 1994
http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarvc/html/jangrayhood.asp
it talks about the C++ object model and layout in memory; good introduction;
article#2
--------------------------------------------------------------------------
here is a nice article about how vtables workits 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.
article#3
--------------------------------------------------------------------------
virtual functions explained:
http://www.parashift.com/c++-faq-lite/virtual-functions.html
article#4
--------------------------------------------------------------------------
a good summerize of the classes size.
when applying the sizeof operator on an object you need to have in mind:
1Size of all non-static data members
2Order of data members
3Byte alignment or byte padding
4Size of its immediate base class
5The existence of virtual function(s) (Dynamic polymorphism using virtual functions).
6Compiler being used
7Mode of inheritance (virtual inheritance)
http://www.cprogramming.com/tutorial/size_of_class_object.html
article #5
--------------------------------------------------------------------------
very good C++ guidelines; yet, im not sure if all of them are applicable
http://www.doc.ic.ac.uk/lab/cplus/c++.rules/
article#1
--------------------------------------------------------------------------
C++: Under the Hood
Jan Gray
March 1994
http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarvc/html/jangrayhood.asp
it talks about the C++ object model and layout in memory; good introduction;
article#2
--------------------------------------------------------------------------
here is a nice article about how vtables workits 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.
article#3
--------------------------------------------------------------------------
virtual functions explained:
http://www.parashift.com/c++-faq-lite/virtual-functions.html
article#4
--------------------------------------------------------------------------
a good summerize of the classes size.
when applying the sizeof operator on an object you need to have in mind:
1Size of all non-static data members
2Order of data members
3Byte alignment or byte padding
4Size of its immediate base class
5The existence of virtual function(s) (Dynamic polymorphism using virtual functions).
6Compiler being used
7Mode of inheritance (virtual inheritance)
http://www.cprogramming.com/tutorial/size_of_class_object.html
article #5
--------------------------------------------------------------------------
very good C++ guidelines; yet, im not sure if all of them are applicable
http://www.doc.ic.ac.uk/lab/cplus/c++.rules/
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.
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.
Subscribe to:
Posts (Atom)