Tuesday, June 1, 2010

Effective C++

Scott Meyers' Effective C++ (1997)

    Shifting from C to C++

  1. Prefer const and inline to #define
    - Value by #define does not go to symbol table, but processed by preprocessor.
    - For const pointer, needs to be like const char * const a = "zzz";
    - Class-specific constants: in class declaration: "static const int a;", need to define outside class.

  2. Prefer <iostream> to <stdio.h>
    - stdio.h is not type-safe and not extensible.
    - e.g. friend ostream& operator<<(ostream& s, const Rational& r);
    - iostream is in std (preferred), iostream.h is in global range.

  3. Prefer new/delete to malloc/free
    - Reason: malloc/delete do not know constructor/destructor

  4. Prefer C++ style comments (// ...) over C'(/* ... */)
    - Note some preprocessors only recognize /* ... */

  5. Memory management

  6. Use the same form in corresponding uses of new and delete.
    - Example:
    string *stringPtr1 = new string;
    string *stringPtr2 = new string[100];
    ...
    delete stringPtr1; // delete an object
    delete [] stringPtr2; // delete an array of

  7. Use delete on pointer members in destructors
    - Delete a NULL pointer does no harm (free a NULL pointer causes error though)
    - One way to avoid using delete is to use smart pointers (e.g. auto_ptr in STL)

  8. Be prepared for out-of-memory conditions (more reading needed..)
    - When new fails, it throws an exception std::bad_alloc (in old compilers, it may return NULL)
    - assert is a macro. It does not work when NDEBUG is defined.

  9. Adhere to convention when writing operator new and operator delete. (more reading needed..)

  10. Avoid hiding the "normal" form of new.
    - Declare a function called "operator new" inside the class would block access to the "normal" form of new.
    - Two solutions: 1) overload operator new, 2) provide default value for additional parameters.
    - Example:
    class X {
    public:
    void f();
    static void * operator new(size_t size, new_handler p);
    static void * operator new(size_t size)
    { return ::operator new(size); }
    };
    X *px1 = new (specialErrorHandler) X; // calls X::operator new(size_t, new_handler)
    X* px2 = new X; // calls X::operator new(size_t)

  11. Write operator delete if you write operator new. (new/delete should be paired) (more reading needed..)

  12. Constructors, Destructors and Assignment Operators

    Almost every class has one or more constructors, a destructor, and an assignment operator.
  13. Declare a copy constructor and an assignment operator for classes with dynamically allocated memory.

  14. Prefer initialization to assignment in constructors. (more reading needed..)

  15. List members in an initialization list in the order in which they are declared.
    - Otherwise there is overhead for compiler to track information.

  16. Make sure base classes have virtual destructors.
    - Base class virtual destructor should be used when base class has virtual fxns.
    - No virtual fxns in a base class often means it's not suitable to be a base class.
    - When need an abstract class, it may be convenient to declare destructor as pure virtual destructor, but then a definition of it should also be defined.

  17. Have operator= return a reference to *this. (more reading needed..)
    - So as to be able to chain assignments together (assignment is right associative)

  18. Assign to all data members in operator=. (more reading needed..)

  19. Check for assignment to self in operator=. (more reading needed..)

  20. Classes and Functions: Design and Declaration
  21. Strive for class interfaces that are complete and minimal.

  22. Differentiate among member functions, non-member functions, and friend functions.
    - Member fxns can be virtual, non-member fxns can't.
    - Operator>> and operator<< are never members.
    - Only non-member functions get type conversions on their left-most argument.
    - Everything else should be a member function.

  23. Avoid data members in the public interface.
    - Use access/inline functions instead of data members.

  24. Use const whenever possible. (more reading needed)
    - const value and const pointer
    - mutable
    - const_cast()

  25. Prefer pass-by-reference to pass-by-value.
    - The meaning of passing an object by value is defined by the copy constructor of that object's class. This can be expensive.
    - Bad use: Student returnStudent(Student s) { return s; }
    - Good use: const Student& returnStudent(const Student& s) { return s; }
    - Slicing problem. (Pass by base class type will cut off derived class members.)
    - Aliasing.
    - Reference is implemented by pointer.

  26. Don't try to return a reference when you must return an object.
    - E.g., operator* should return an object instead of reference.

  27. Choose carefully between function overloading and parameter defaulting.
    - 2 questions: 1) is there a value you can use for a default? 2) how many algorithms do you want to use?

  28. Avoid overloading on a pointer and a numerical type.

  29. Guard against potential ambiguity. (more reading needed)

  30. Explicitly disallow use of implicitly generated member functions you don't want.
    - E.g. operator=

  31. Partition the global namespace. (more reading needed)

  32. Classes and Functions: Implementation
  33. Avoid returning "handles" to internal data.

  34. Avoid member functions that return non-const pointers or references to members less accessible than themselves.

  35. Never return a reference to a local object or to a dereferenced pointer initialized by new within the function.
    - e.g. this is bad because the difficulty of applying delete:
    inline const Rational& operator*(const Rational& lhs, const Rational& rhs)
    { Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); return *result; }

  36. Postpone variable definitions as long as possible.
    - instead of "string encrypted; encrypted = password;", use "string encrypted = password;", this avoids calling default constructor on string.

  37. Item 33: Use inlining judiciously.
    - inline function used extensively may increase code size a lot.
    - inline, like "register", is only a hint to compiler.
    - uninlined inline function, in old rule, is treated as static and included into every translation unit.
    - library needs careful consideration on inline function, inline functions make it impossible to provide binary upgrades to the inline functions in a library - all clients have to recompile.
    - inline function should avoid using static variable.
    - most debuggers have problem with inline function.

  38. Minimize compilation dependencies between files.
    - should do: replacement of dependencies on class definitions with dependencies on class declarations
    - Avoid using objects when object references and pointers will do.
    - Use class declarations instead of class definitions whenever you can.
    - Don't #include header files in your header files unless your headers won't compile without them.
    - Handle/body class, envelope/letter class.

  39. Inheritance and Object-Oriented Design
  40. Make sure public inheritance models "isa."
    - isa == public inheritance
    - Two other common inter-class relationships are "has-a" and "is-implemented-in-terms-of."

  41. Differentiate between inheritance of interface and inheritance of implementation. (more reading needed)

  42. Never redefine an inherited nonvirtual function.
    - nonvirtual - statically bound
    - virtual - dynamically bound

  43. Never redefine an inherited default parameter value.
    - default parameters are statically bound!

  44. Avoid casts down the inheritance hierarchy.
    - down cast: from a base class pointer to a derived class pointer
    - down cast leads to a maintenance nightmare
    - safe downcasting: by using dynamic_cast

  45. Model "has-a" or "is-implemented-in-terms-of" through layering.
    - difference between isa and is-implemented-in-terms-of

  46. Differentiate between inheritance and templates. (more reading needed)

  47. Use private inheritance judiciously. (more reading needed)
    - compilers will generally not convert a derived class object into a base class object if the inheritance relationship between the classes is private.
    - members inherited from a private base class become private members of the derived class, even if they were protected or public in the base class.
    - private inheritance means is-implemented-in-terms-of.
    - use layering whenever you can, use private inheritance whenever you must.
    - template-induced code bloat. It is not a good thing.

  48. Use multiple inheritance (MI) judiciously. (more reading needed)
    - MI leads to many problems, one is ambiguity (diamond inheritance).

  49. Say what you mean; understand what you're saying.

  50. Miscellaneous
  51. Know what functions C++ silently writes and calls.
    - default constructor, destructor, assignment/copy constructor, address-of operator, const address-of operator.

  52. Prefer compile-time and link-time errors to runtime errors.

  53. Ensure that non-local static objects are initialized before they're used. (more reading needed)

  54. Pay attention to compiler warnings.

  55. Familiarize yourself with the standard library.

  56. Improve your understanding of C++.

No comments:

Blog Archive

Followers