- 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. - 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. - Prefer new/delete to malloc/free
- Reason: malloc/delete do not know constructor/destructor - Prefer C++ style comments (// ...) over C'(/* ... */)
- Note some preprocessors only recognize /* ... */ - 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 - 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) - 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. - Adhere to convention when writing operator new and operator delete. (more reading needed..)
- 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) - Write operator delete if you write operator new. (new/delete should be paired) (more reading needed..)
- Declare a copy constructor and an assignment operator for classes with dynamically allocated memory.
- Prefer initialization to assignment in constructors. (more reading needed..)
- List members in an initialization list in the order in which they are declared.
- Otherwise there is overhead for compiler to track information. - 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. - Have operator= return a reference to *this. (more reading needed..)
- So as to be able to chain assignments together (assignment is right associative) - Assign to all data members in operator=. (more reading needed..)
- Check for assignment to self in operator=. (more reading needed..)
- Strive for class interfaces that are complete and minimal.
- 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. - Avoid data members in the public interface.
- Use access/inline functions instead of data members. - Use const whenever possible. (more reading needed)
- const value and const pointer
- mutable
- const_cast() - 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. - Don't try to return a reference when you must return an object.
- E.g., operator* should return an object instead of reference. - 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? - Avoid overloading on a pointer and a numerical type.
- Guard against potential ambiguity. (more reading needed)
- Explicitly disallow use of implicitly generated member functions you don't want.
- E.g. operator= - Partition the global namespace. (more reading needed)
- Avoid returning "handles" to internal data.
- Avoid member functions that return non-const pointers or references to members less accessible than themselves.
- 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; } - Postpone variable definitions as long as possible.
- instead of "string encrypted; encrypted = password;", use "string encrypted = password;", this avoids calling default constructor on string. - 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. - 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. - Make sure public inheritance models "isa."
- isa == public inheritance
- Two other common inter-class relationships are "has-a" and "is-implemented-in-terms-of." - Differentiate between inheritance of interface and inheritance of implementation. (more reading needed)
- Never redefine an inherited nonvirtual function.
- nonvirtual - statically bound
- virtual - dynamically bound - Never redefine an inherited default parameter value.
- default parameters are statically bound! - 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 - Model "has-a" or "is-implemented-in-terms-of" through layering.
- difference between isa and is-implemented-in-terms-of - Differentiate between inheritance and templates. (more reading needed)
- 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. - Use multiple inheritance (MI) judiciously. (more reading needed)
- MI leads to many problems, one is ambiguity (diamond inheritance). - Say what you mean; understand what you're saying.
- Know what functions C++ silently writes and calls.
- default constructor, destructor, assignment/copy constructor, address-of operator, const address-of operator. - Prefer compile-time and link-time errors to runtime errors.
- Ensure that non-local static objects are initialized before they're used. (more reading needed)
- Pay attention to compiler warnings.
- Familiarize yourself with the standard library.
- Improve your understanding of C++.
Shifting from C to C++
Memory management
Constructors, Destructors and Assignment Operators
Almost every class has one or more constructors, a destructor, and an assignment operator.
Classes and Functions: Design and Declaration
Classes and Functions: Implementation
Inheritance and Object-Oriented Design
Miscellaneous
No comments:
Post a Comment