Saturday, May 31, 2008

C++ #17

61. A Pointer to Member Cannot Refer to a Static Member Function
It is illegal to assign the address of a static class member to a pointer to member. However, you can take the address of a static member function of a class and treat it as if it were an external function:

class A {
public:
static void f();
};
void main() {
void (*p) () = &A::f; //OK, p is an ordinary pointer to function
}
The secret here is that a static member function is actually an ordinary global function, whose class serves as a namespace.
62. Why you shouldn't store auto_ptr objects in STL containers
Most C++ users already know that they shouldn't use auto_ptr objects as elements of STL containers. However, fewer users know exactly why this is so.
The C++ Standard says that an STL element must be "copy-constructible" and "assignable." These fancy words simply mean you can safely assign or copy one object to another and get two independent and logically identical copies. In particular, the state of the original object shouldn't change when it is copied to the target object.
This is not the case with auto_ptr, though: copying or assigning one auto_ptr to another makes changes to the original, in addition to the obvious changes to the copy. To be more specific, the original object transfers ownership of the pointer to the target, and the pointer in the original object becomes null. Imagine what would happen if you did something like this:
class Foo{};
vector < auto_ptr <Foo> > vf; //a vector of auto_ptr's
//..fill vf
int g()
{
auto_ptr <Foo> temp = vf[0]; // vf[0] becomes null
}
When temp is initialized, the member vf[0] is changed: its pointer becomes null. Any attempt to use that element will cause a runtime crash. This situation is likely to occur whenever you copy an element from the container. Remember that even if your code does not perform any explicit copy or assignment operations, many algorithms (swap(), random_shuffle(), sort(), and many others) create a temporary copy of one or more container elements. Furthermore, certain member functions of the container may create a temporary copy of one or more elements, thereby nullifying them. Any subsequent attempt to the container elements is therefore undefined.
Several Visual C++ users report that they have never encountered any problems with using auto_ptr in STL containers. This is because the auto_ptr implementation of Visual C++ (all versions thereof) is outdated and relies on an obsolete specification. When Microsoft decides to catch up with the current ANSI/ISO C++ Standard and change their Standard Library accordingly, code that uses auto_ptr in STL containers will manifest serious malfunctions.
To conclude, you shouldn't use auto_ptr in STL containers. Either use bare pointers or use other smart pointer classes instead of auto_ptr (such classes are available at Boost.org).
63. Iterators Aren't Pointers
Suppose you define a the following list object and you need the address of one of its elements:
std::list <int> li;
std::list <int>::iterator iter = li.begin();
In many contexts, iter functions like a pointer. However, when you need a real pointer to a container's element, you can't use an iterator:

int func(int * p);
int main()
{
func(iter); // error, iter is not a pointer to int
}
The problem is that in general, iterators aren't pointers. Rather, they are usually implemented as objects. To get a pointer to the element an iterator refers to, you need to "dereference" that iterator and take the address of the result. For example:

int main()
{
func( &*iter); // ok
}

Function
64. Change the Private Variable Outside of a Member Function Definition
Use the following code to change the private variable outside of a member function definition:

class test
{
int x;
public:
test():x(0){}
int& getx(){return x;}
};

int main()
{
test t(10);
cout<<t.getx()<<endl; //x = 10
int& i = t.getx();
i = 20;
cout<<t.getx(); //x = 20 (value of x changed)
return 0;
}
65. When does a virtual member function imposes runtime overhead?
Dynamic binding, for example, having a virtual member(s), is one of the basic principles of OO programming. If the compiler can resolve a call to a virtual member function statically, no extra overhead is incurred and it can even be inlined. In other words, it is as efficient as any ordinary function call, provided that the compiler can decide at compile time which object's virtual function is the one to be invoked. However, in certain circumstances it can't be done, and as a result, a virtual call imposes a slight run-time overhead, for example, when a virtual is called from a pointer or a reference to an object:

class V { //a polymorphic class
virtual void show() const { cout<<"I'm V<<endl; }
};

class W : public V {
void show() const { cout<<"I'm W<<endl; }
};

void f(V & v, V *pV) {

W w; //local object
w.show(); //call resolved statically to W::show(); no overhead.


v.show(); //v is a reference, can be bound to a V or object derived

//from V; call resolution is postponed to run-time.

pV->show(); //pV is a pointer to a base class, can point to a derived

//object as well, so call resolution is postponed to run-

//time.

}//end f

66. extern "C" linkage declaration
It is not uncommon for C++ code fragments to be called by non-C++ programs such as a C-based communication software, COBOL based TP monitors and the likes. However, global functions in C++ cannot be called by non-C++ programs since the function name is very different from the one supplied by the programmer. Why is this? By default, the C++ compiler uses a technique termed name mangling which incorporates the function name with its signature (list of arguments) in order to create a unique name for it, even in case of overloading (this technique is used both for external function and class member functions).
//the following global functions have distinct names generated by the compiler void f(int); //x__f_i this may be the name generated by the compiler for this f() void f(long, char* ); //x__f_pc this may be the name generated by the compiler for this f()
In order to enforce the compiler to avoid name mangling, a global function has to be declared as extern "C":
extern "C" void f(int); //now the generated name is identical to the one given by the programmer: f
Mind, however, that a function declared as extern "C" cannot be overloaded, and that extern "C" declaration can only be applied to global functions.

67. Unnamed Function Arguments
When an argument of a function is not used anymore in the function body (in an upgrade, for example), it needn't be removed from the function's argument list. It can rather become an unnamed argument. The following WriteData function used to support data streaming into various I/O devices, including 5.25" floppy disk. In its upgraded version, WriteData does not support 5.25" disk anymore. However, removal of the final argument would force users to change the prototype as well, or otherwise a link-time error would occur. Therefore, the final argument was not removed entirely. Instead, it was unnamed:

void WriteData(HardDisk &disk, Floppy3_5& diskette, floppy5_25& )
{/*..*/}
This can be useful in the following cases: when the function's prototype and its definition are located in separate files and the file containing the prototype can not be changed; when there are enormous occurrences of the prototype in several headers; or when the software is to be shipped to customers as a binary .lib or .obj file, still enabling them to use the old headers.
68. What Happens When an Inline Function Cannot be Inlined?
Not every function that is declared inline can actually be inlined by the compiler. A function that has local variables, loops, or an extensive amount of code cannot be inlined efficiently. In this case, the compiler may suppress the inlining request, and convert the function to a static function. Consequently, numerous copies of the same function will be generated (one copy per translation unit), resulting in a longer compilation time. Additionally, the linker has to compare these copies to ensure they are all identical, and then remove all but one of the excess copies--a time consuming process. The result is equivalent to a simple external function, only with the additional compile and link time. Therefore, you should use the inline specifier judiciously--in very short functions ("getters" and "setters"), or better still, avoid using it altogether, and let the compiler inline the suitable functions automatically.
69. Re-entrant Functions
A function that can be safely called while it's already executing is said to be re-entrant. The C++ standard doesn't state whether the Standard Library's functions are re-entrant; this is an implementation-dependent feature. What's the use of calling a function while it's already executing? There are two occasions in which this scenario can happen: when the function is invoked by two or more distinct threads of the same program, or when executing a signal handler that raises the same signal once again. In either case, remember that calling a non re-entrant function while it's already executing is a bad idea — the program's behavior is undefined in this case.
70. A Member Function Template may not be Virtual
A reader tried to declare a template as a virtual member function of a class. However, his compiler refused to compile the code. The reader wanted to know what he was doing wrong. The answer is simple: the ANSI/ISO Standard says (14.5.2 p 3): "A member function template shall not be virtual." For example:

template <class T> struct A
{
template <class C> virtual void f(C);

/* error, virtual template function */
virtual void g(); // OK, g is not a template
};

No comments: