1.
Once the right types are in place, it can sometimes be reasonable to restrict the values of those types. For example, there are only 12 valid month values, so theMonth type should reflect that. One way to do this would be to use an enum to represent the month, but enums are not as type-safe as we might like. For example, enums can be used likeints (seeItem 2). A safer solution is to predefine the set of all validMonths:
class Month { public: static Month Jan() { return Month(1); } // functions returning all valid static Month Feb() { return Month(2); } // Month values; see below for ... // why these are functions, not static Month Dec() { return Month(12); } // objects ... // other member functions private: explicit Month(int m); // prevent creation of new // Month values ... // month-specific data }; Date d(Month::Mar(), Day(30), Year(1995));
C++ constructors that have just one parameter automatically perform implicit type conversion. For example, if you pass an int when the constructor expects a string pointer parameter, the compiler will add the code it must have to convert the int to a string pointer. However, you might not always want this automatic behavior.
Implicit Type Conversion:
1. operator T1()
2. constructor with only one parameter and without the keyword explict(Constructor with multi parameters but only one of them doesn't have default value also works as implicit constructor)
You can add explicit to the constructor declaration to prevent implicit conversions. This forces the code to either use a parameter of the correct type, or cast the parameter to the correct type. That is, if the cast is not visibly expressed in code, an error will result.
The explicit keyword can only be applied to in-class constructor declarations to explicitly construct an object.
2.slicing problem. When a derived class object is passed (by value) as a base class object, the base class copy constructor is called, and the specialized features that make the object behave like a derived class object are "sliced" off. You're left with a simple base class object — little surprise, since a base class constructor created it. We can use reference to avoid that problem because:
If you peek under the hood of a C++ compiler, you'll find that references are typically implemented as pointers, so passing something by reference usually means really passing a pointer.
3.
-
Prefer pass-by-reference-to-const over pass-by-value. It's typically more efficient and it avoids the slicing problem.
-
The rule doesn't apply to built-in types and STL iterator and function object types. For them, pass-by-value is usually appropriate
Never return a pointer or reference to a local stack object, a reference to a heap-allocated object, or a pointer or reference to a local static object if there is a chance that more than one such object will be needed(because there is only one copy of the static object).
4.
-
Prefer non-member non-friend functions to member functions. Doing so increases encapsulation, packaging flexibility, and functional extensibility
Encapsulation:non-member, non-friend function will have no access to class's private data.
Packaging Flexibility:
It can be divided to different namespace in different header files rather than in a single class.Such as <vector> <list>
functional extensibility:
Client can create their own function set.
-
If you need type conversions on all parameters to a function (including the one pointed to by thethis pointer), the function must be a non-member.
class Rational { ... // contains no operator* }; const Rational operator*(const Rational& lhs, // now a non-member const Rational& rhs) // function { return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator()); }If operator* is in member function, it can only have one parameter.
6.
class Widget { // same as above, except for the public: // addition of the swap mem func ... void swap(Widget& other) { using std::swap; // the need for this declaration // is explained later in this Item swap(pImpl, other.pImpl); // to swap Widgets, swap their } // pImpl pointers ... }; namespace std { template<> // revised specialization of void swap<Widget>(Widget& a, // std::swap Widget& b) { a.swap(b); // to swap Widgets, call their } // swap member function }
The "template<>" at the beginning of this function says that this is a total template specialization for std::swap, and the "<Widget>" after the name of the function says that the specialization is for when T is Widget. In other words, when the generalswap template is applied to Widgets, this is the implementation that should be used.
In general, we're not permitted to alter the contents of the std namespace, but we are allowed to totally specialize standard templates (likeswap) for types of our own creation (such as Widget). The code below is not legal because we add a new type into std.
namespace std { template<typename T> // an overloading of std::swap void swap(Widget<T>& a, // (note the lack of "<...>" after Widget<T>& b) // "swap"), but see below for { a.swap(b); } // why this isn't valid code }
7.
if the default implementation of swap isn't efficient enough (which almost always means that your class or template is using some variation of the pimpl idiom), do the following:
-
Offer a public swap member function that efficiently swaps the value of two objects of your type. this function should never throw an exception.
class Widget { // same as above, except for the public: // addition of the swap mem func ... void swap(Widget& other) { using std::swap; // the need for this declaration // is explained later in this Item swap(pImpl, other.pImpl); // to swap Widgets, swap their } // pImpl pointers ... };
-
Offer a non-member swap in the same namespace as your class or template. Have it call your swap member function.
namespace WidgetStuff { ... // templatized WidgetImpl, etc. template<typename T> // as before, including the swap class Widget { ... }; // member function ... template<typename T> // non-member swap function; void swap(Widget<T>& a, // not part of the std namespace Widget<T>& b) { a.swap(b); } }
-
If you're writing a class (not a class template), specialize std::swap for your class. Have it also call your swap member function.
namespace std { template<> // revised specialization of void swap<Widget>(Widget& a, // std::swap Widget& b) { a.swap(b); // to swap Widgets, call their } // swap member function }
Finally, if you're calling swap, be sure to include ausing declaration to makestd::swap visible in your function, then callswap without any namespace qualification.