Differences

This shows you the differences between two versions of the page.

Link to this comparison view

programming:improve-your-cpp [2006/12/14 14:41]
cyril
programming:improve-your-cpp [2013/09/19 16:40]
Line 1: Line 1:
-<php>$conf['maxtoclevel'] = 3;</php> 
- 
-\\ This page is based on the excellent books __50 ways to improve your C++__ and __35 more ways to improve your C++__. 
- 
-====== Effective C++ ====== 
- 
-===== Shifting from C to C++ ===== 
- 
- 
-==== Item 1 : Use const and inline instead of #define ==== 
-Because constants don't appear in the symbole table which is annoying for debugging : 
-<code c++> 
-const float ASPECT_RATIO = 1.653; 
-</code> 
-And because there are lot of traps with macros : 
-<code c++> 
-template<class T> inline T& MAX(T& a, T& b) { return a>b ? a : b; } 
-</code> 
- 
-==== Item 2 : Prefer iostream to stdio.h ==== 
-Because of type safety an extensiblity. To define stream operators in your class : 
-<code c++> 
-friend ostream& operator<<(ostream& s, const ComplexInt& c); 
-ostream& operator<<(ostream& s, const ComplexInt& c) { s << c.r << " " << c.i; return s; } 
-</code> 
- 
-==== Item 3 : Use new and delete instead of malloc and free ==== 
-Because they don't call constructors and destructors !  
- 
-==== Item 4 : Prefer C++-style comments ==== 
-Because you can't embed C-style comments in other comments ! 
- 
- 
-===== Memory management ===== 
- 
-==== Item 5 : Use the same form in corresponding calls to new and delete ==== 
-''new'' with ''delete'', and ''new[]'' with ''delete[]'', or memory leaks ! (don't call all destructors) 
- 
-==== Item 6 : Call delete on pointer members in destructors ==== 
-Initialize all pointer members in each of the constructors (at ''NULL'' if not allocated) ;\\ 
-Delete for all pointer members the existing memory and assign new memory in the assignment operator ; \\ 
-Delete the memory in the destructor 
- 
- 
-==== Item 7 : Check the return value of new ==== 
-Set an error-handling function, globally : 
-<code c++> 
-void noMoreMemory() { cerr << "Unable to satisfy request for memory\n"; abort(); } 
-main() { set_new_handler(noMoreMemory); ... } 
-</code> 
-Or for a specific class : 
-<code c++> 
-typedef void (*PEHF)(); //  PEHF = pointer to error handling function 
- 
-class X 
-{ 
- private: 
-  static PEHF currentPEHF; 
- public: 
- static PEHF set_new_handler(PEHF p); 
- void * operator new(size_t size); 
-}; 
- 
-PEHF X::currentPEHF; //sets currentPEHF to 0 by default 
- 
-PEHF X::set_new_handler(PEHF p) 
-{ 
- PEHF oldPEHF = currentPEHF; 
- currentPEHF = p; 
- return oldPEHF; 
-} 
- 
-void * X::operator new(size_t size) 
-{ 
- PEHF currentHandler = ::set_new_handler(currentPEHF); 
- void *memory = ::new char[size]; 
- ::set_new_handler(currentHandler); 
- return memory; 
-} 
-</code> 
- 
- 
- 
-==== Item 8 : Adhere to convention when writing new ==== 
-Means having the right return value (pointer or 0), and calling an error-handling function when insufficient memory is avalaible : 
-<code c++> 
-void * operator new(size_t size) // your operator new could take additional params 
-{ 
- while(true) 
- { 
- // HERE attempt to allocate size bytes 
- if (the allocation was sucessful) 
- return (a pointer to the memory); 
- PEHF currentHandler = set_new_handler(0); // get the current ... 
- set_new_handler(currentHandler); // ... error handling function 
- if (currentHandler) (*currentHandler)(); else return 0; 
- } 
-} 
-</code> 
-If the operator new is in a class X, you should add before to attempt to allocate memory : 
-<code c++> 
-if (size != sizeof(X)) return ::new char[size]; 
-</code> 
-This occur when you inheritate from the class without rewriting the new operator. 
- 
-==== Item 9 : Avoid hiding the global new ==== 
-If you add parameters to your ''new'' redefinition, it blocks access to the usual form of new, so rewrite also the classical form : 
-<code c++> 
-class X 
-{ 
- void * operator new(size_t size, PEHF pehf); 
- void * operator new(size_t size) { return ::new char[size]; } 
-}; 
- 
-void specialErrorHandler(); 
- 
-X *px1 = new(specialErrorHandler) X; 
-X *px2 = new X; // doesn't work if you don't define new(size_t) 
-</code> 
- 
-==== Item 10 : Write delete if you write new ==== 
-You can rewrite ''new'' to allocate small objects in a large memory zone (in order to speed up allocations and save memory), but you have also to write ''delete'' ! 
-<code c++> 
-class Airplane 
-{ 
- private: 
-  Airplane *rep; 
-  static Airplane *headOfFreeList; 
- public: 
-  void * operator new(size_t, size); 
-  void operator delete(void *deadObject, size_t size); 
-}; 
- 
-Airplane *Airplane::headOfFreeList; // initialized to 0 by default 
- 
-void * Airplane::operator new(size_t size) 
-{ 
- if (size != sizeof(Airplane)) return ::new char[size]; 
- Airplane *p = headOfFreeList; 
- if (p) headOfFreeList = (Airplane*) p->rep; 
- else 
- { 
- Airplane *newBlock = (Airplane*) ::new char[256 * sizeof(Airplane)]; // don't call constructor ! 
- if (newBlock == 0) return 0; 
- for(int i  0; i < 255; i++) // link the memory chunks together 
- newBlock[i].rep = (Airplane*) &newBlock[i+1]; 
- newBlock[255].rep = 0; 
- p = newBlock; 
- headOfFreeList = &newBlock[1]; 
- } 
- return p; 
-} 
- 
-void Airplane::operator delete(void *deadObject, size_t size) 
-{ 
- if (size != sizeof(Airplane)) { ::delete[] ((char*) deadObject); return; } 
- Airplane *carcass = (Airplane*) deadObject; 
- carcass->rep = (AirplaneRep*) headOfFreeList; 
- headOfFreeList = carcass; 
-} 
-</code> 
- 
-===== Constructors, Destructors, and Assignment operators ===== 
- 
-==== Item 11 : Define a copy constructor and an assignment operator for classes with dynamically allocated memory ==== 
- 
-==== Item 12 : Prefer initialization to assignment in constructors ==== 
-You don't have choice for constants or reference members, but in general it is more efficient (constructors are always called once, and with initialization all members are written in raw which is faster than assigning them one by one). 
- 
-==== Item 13 : List members in an initialization list in the order in which they are declared ==== 
-Because it is the order of the declarations which is followed by the compiler, not the order of the initialization list, so you can fall into traps if you don't have the same order. 
- 
-==== Item 14 : Make destructors virtual in base classes ==== 
-Because if you don't, if you create a derived object and put it in a pointer of the base class, when you will delete it it won't call the destructor of the derived class ! But only for base class (if you plan to inheritate from it), which generally corresponds to the fact that there is at least one virtual function, because you loose a little performance. Tip : if you want to have an abstract class, but you don't have any function to make pure virtual, declare a pure virtual destructor ! (but you have nevertheless to provide a definition for this pure virtual destructor, and do not declare it inline because it could have problems with the virtual thing). 
- 
-==== Item 15 : Have operator= return a reference to *this ==== 
-To allow chains assignments : 
-<code c++> 
-C& C::operator=(const C&); 
-</code> 
- 
-==== Item 16 : Assign to all data members in operator= ==== 
-Because if you want to assign some, the compiler won't anymore assign the other ones. Moreover if it is a derived class you have also to initialize the data members of the base class :  
-<code cpp>((A&) *this = rhs;</code> (if base class doesn't provide = operator) 
-<code cpp>A::operator=(rhs);</code> (if it does) 
- 
-==== Item 17 : Check for assignment to self in operator= ==== 
-That's to say, always begin by : ''if (this == &rhs) return *this;''. Not only because it saves time, but above all because it will create serious problems with freeing and reallocating resources ! But be careful this test won't work with multiple inheritance because one same object can have different addresses according to the type it is casted to. If so you could add an unique identifier for each object. 
- 
- 
-===== Classes and Functions : Design and Declaration ===== 
- 
- 
-==== Item 18 : Strive for class interfaces that are complete and minimal ==== 
- 
-==== Item 19 : Differentiate among member functions, global functions, and friend functions ==== 
- 
-==== Item 20 : Avoid data members in the public interface ==== 
- 
-==== Item 21 : Use const whenever possible ==== 
- 
-==== Item 22 :  ==== 
- 
-==== Item 23 :  ==== 
- 
-==== Item 24 :  ==== 
- 
-==== Item 25 :  ==== 
- 
-==== Item 26 :  ==== 
- 
-==== Item 27 :  ==== 
- 
-==== Item 28 :  ==== 
- 
-==== Item 29 :  ==== 
- 
-==== Item 30 :  ==== 
- 
-==== Item 31 :  ==== 
- 
-==== Item 32 :  ==== 
- 
-==== Item 33 :  ==== 
- 
-==== Item 34 :  ==== 
- 
-==== Item 35 :  ==== 
- 
-==== Item 36 :  ==== 
- 
-==== Item 37 :  ==== 
- 
-==== Item 38 :  ==== 
- 
-==== Item 39 :  ==== 
- 
-==== Item 40 :  ==== 
- 
-==== Item 41 :  ==== 
- 
-==== Item 42 :  ==== 
- 
-==== Item 43 :  ==== 
- 
-==== Item 44 :  ==== 
- 
-==== Item 45 :  ==== 
- 
-==== Item 46 :  ==== 
- 
-==== Item 47 :  ==== 
- 
-==== Item 48 :  ==== 
- 
-==== Item 49 :  ==== 
- 
-==== Item 50 :  ==== 
- 
- 
-====== More effective C++ ====== 
-===== Basics ===== 
- 
-==== Item 1 : Distinguish between pointers and references ==== 
-References must refer to an object (no null reference), and that's why they must be initialized. Indeed you don't have to check if a reference parameter is null or not, contrary to pointers. But references can't be reassigned to refer to different objects. 
- 
-==== Item 2 : Prefer C++-style casts ==== 
-Because they are easier to parse (both for humans and tools), and because it can avoid errors.\\ 
-''static_cast<type>(expression)'' : normal cast, for example ''double'' to ''int''.\\ 
-''const_cast'' to cast away the constness or volatileness of an expression :  
-<code c++> 
-void update(X *px); 
-const X x; 
-update(const_cast<X*>(&x)); 
-</code> 
-''dynamic_cast'' to perform safe casts down or across an inheritance hierarchy (base to derived objects), and it returns NULL if it fails (or throw an exception when casting references).\\ 
-''reinterpret_cast'' for implementation-defined casts (rarely portable), for example casting between function pointer types : 
-<code c++> 
-typedef void (*FuncPtr)(); // a FuncPtr is a pointer to "void foo()" 
-FuncPtr funcPtrArray[10]; 
-int doSomething(); 
-funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); // can work, but can yield incorrect results too ! 
-</code> 
- 
-==== Item 3 : Never treat arrays polymorphically ==== 
-That's to say don't place derived class objects in an array of base class pointers, if they don't have the same size, because indexing will use size of the base class. So problems when you write your loop, or when you delete the array (the compiler generates a loop, and it is said in the language specification that the result is undefined). 
- 
-==== Item 4 :  ==== 
- 
-===== Operators ===== 
- 
-==== Item 5 :  ==== 
- 
-==== Item 6 :  ==== 
- 
-==== Item 7 :  ==== 
- 
-==== Item 8 :  ==== 
- 
-===== Exceptions ===== 
-==== Item 9 :  ==== 
- 
-==== Item 10 :  ==== 
- 
-==== Item 11 :  ==== 
- 
-==== Item 12 :  ==== 
- 
-==== Item 13 :  ==== 
- 
-==== Item 14 :  ==== 
- 
-==== Item 15 :  ==== 
- 
-===== Efficiency ===== 
-==== Item 16 :  ==== 
- 
-==== Item 17 :  ==== 
- 
-==== Item 18 :  ==== 
- 
-==== Item 19 :  ==== 
- 
-==== Item 20 :  ==== 
- 
-==== Item 21 :  ==== 
- 
-==== Item 22 :  ==== 
- 
-==== Item 23 :  ==== 
- 
-==== Item 24 : Understand the costs of virtual functions, multiple inheritance, virtual base classes, and RTTI ==== 
- 
-Virtual functions works with virtual tables (one per class) containing pointers to functions, and virtual table pointers (one per object) pointing to the good virtual table. Hence it increases size of objects, per-class data, and reduce performance : because of indirections (but that's almost nothing), and mainly because it prevents inlining (except if the function is called from an object and not a pointer or a reference, but that's almost never the case). 
- 
-Multiple inheritance leads to more per-class data (special virtual tables must be generated for base classes), increases size of objects (multiple virtual table pointers within a single object), and the runtime invocation cost of virtual function grows slightly (offset of virtual table pointers are more complicated to calculate). 
- 
-Moreover if you declare base classes virtual (what you must do to avoid data replication if there are more than one inheritance path to a base class), it increases size of objects by adding several pointers to virtual base class. 
- 
-RTTI (RunTime Type Identification) stores a ''type_info'' object in the virtual table (so it only works if there are virtual functions in the class, and increases a little the per-class data), and the ''typeid'' operator let us discover informations about objects at runtime. 
- 
-To conclude it is important to understand the costs  of theses functionalities, but also to understand that if we need it we will pay for it, so there is no point in trying to emulate it by another way. But you can have legitimate reasons to bypass the compiler-generated services for example because pointers to virtual tables can make it difficult to store C++ objects in databases or to move them across process boundaries, but it will be less efficient !  
- 
-===== Techniques ===== 
  
programming/improve-your-cpp.txt ยท Last modified: 2013/09/19 16:40 (external edit)
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0