Valid tive C ++ Reading Notes (terms 24-29), effective24-29
Article 4 of Objective C ++, there are a lot of extensions...
(4) design and Declaration
____________________________________________________________________________________________________________________________________
Clause 24: If all parameters require type conversion, use the non-member function for this purpose.
#1. If you need to perform all parameters for a function (including the metaphor parameter referred to by this pointer)
Type conversion, the function must be a non-member.
// Assume there is a Rational number class: class Rational {public: Rational (int numrator = 0, int denomination = 1); int numerator () const; int denomination () const ;...}; // If operator * is written as a Rational member function: class Rational {public :... const Rational operator * (const Rational & rhs) const ;...}; rational oneEigth (); // good Rational oneHalf (); // good Rational result = oneHalf * oneEighth; // very good result = result * oneEighth // very good // when you try a hybrid operation: result = oneHa Lf * 2; // result = oneHalf. operator * (2); good result = 2 * oneHalf; // result = 2. operator * (oneHalf); error, continue to try the non-member function // when the compiler tries to call the non-member function result = operator * (2, oneHalf ); the corresponding non-member function is still not found, which leads to an error. // [So we should implement a non-member function]: const Rational operator * (const Rational & lhs, const Rational & rhs) {return Rational (lhs. numerator () * rhs. numerator (), lhs. denomination () * rhs. denomination ());}
#2. The reason for deciding whether the non-member function is a non-friend function or a friend function is
"Whether to access private or protected members in the class ",
If needed, the non-member function should be a friend function,
If not, the non-member function must be a non-friend function to satisfy the maximum encapsulation principle.
____________________________________________________________________________________________________________________________________
Clause 25: Write A swap function that does not throw an exception.
#1. If the default implementation code of swap provides an acceptable efficiency for your class or class template,
Use this default implementation version:
Namespace std {template <typename T> void swap (T & a, T & B) // std: typical swap implementation {// Replace the values of a and B T temp (a); a = B; B = temp ;}}
Otherwise, if the pImp technique (pointer to implementation) is used for the class or class template ):
class WidgetImpl{public: ...private: int a, b, c; std::vector<double> v; ...};class Widget{public: Widget(const Widget& rhs); Widget& operator=(const Widget&rhs) { ... *pImv = *(rhs.pImpl); ... } ...private: WidgetImpl* pImv;};
As a result, the efficiency is insufficient. Please do the following to Improve the efficiency:
(1) provide a public swap member function:
// PImv is a private member variable, so swap should be used internally. // Of course, friend can also be used, but this write method is consistent with STL class Widget {public :... void swap (Widget & other) {using std: swap; swap (pImpl, other. pImpl );}...}; // This swap function ensures exception security and efficiency. Because swap replaces pointers, it will never throw an exception for pointer // and built-in replacement operations, and it is very efficient. If a swap function is executed for a custom type //, an exception is thrown because of the copy constructor and the copy assignment operator.
(2) provide
Non-member swap function, and make it call the above swap function, for example:
namespace WidgetStuff{ ... template<typename T> class Widget {...} ... template<typename T> void swap(Widget<T>& a, Widget<T>& b) { a.swap(b); }}
(3). If you are writing a class (instead of a class template), convert std: swap to your class,
And let it call your swap member function, for example:
Namespace std {template <> void swap <Widget> (Widget & a, Widget & B) {. swap (B );}} // ================================================ ==========================================================/// reason: // The advantage is that even if the customer accidentally calls std: swap directly, at least one fully-specialized swap function can be obtained. // In addition, C ++ only runs the class template with special features, however, the function template is not allowed to be biased. // Therefore, it cannot be compiled (although some compilers mistakenly accept it ). // ================================================ ======================================
(4). If you call swap, use the using declarative method to make std: swap visible in the function.
When swap directly writes the call, you can find the following efficiency:
<1>. Find the swap function in the same namespace as the class.
<2>. Find the swap function in the globe environment.
<3>. Find the fully-specialized swap function in namesapce std (if it is a class rather than a template, and implement it)
<4>. Search for Common swap functions in namesapce std.
#2. It is good to fully specialize std templates in std, but do not add some
Because the C ++ Standards Committee prohibits the inclusion of declared items, even if
Compilation and execution cannot be clarified.
____________________________________________________________________________________________________________________________________
(5). Implementation
____________________________________________________________________________________________________________________________________
Clause 26: try to delay the appearance of the variable definition
#1. Try to delay the appearance of the variable definition. If possible, it will be up to initialization.
Reason 1: Increase the readability of the program to make the program structure clearer.
Cause 2: for user (or system) defined types, early variables may cause the program to throw an exception,
The variables are not used when the function is returned. Therefore, additional constructor and analysis components are introduced.
(The built-in type definition can be initialized in the C style, or the variable definition time can be delayed. for uniformity,
It is best to delay the time defined by the variable .)
For example:
Std: string encryptPassword (const std: string & password) {using namespace std; string encrypted; if (password. length () <MinimumPasswordLength) throw logic_error ("Password is too short ");... return encrypted;} // If a logic_error error is thrown, encrypted is not used at all. // Therefore, it should be changed to std: string encryptPassword (const std: string & password) {if (password. length () <MinimumPasswordLength) throw logic_error ("Password is too short"); using namespace std; string encrypted (password );... return encrypted ;}
#2. methods a and B have different definitions. If you know that the cost of assignment is lower than that of "Construction + analysis,
And focus on efficiency, you should use method. Otherwise, to ensure the readability and maintainability of the program,
You should use method B.
// Method A: defines the out-of-loop Widget w; for (int I = 0; I <n; I ++) {w = depends on A value of I ;...} // Method B: defined outside the loop for (int I = 0; I <n; I ++) {Widget w (depending on a value of I );} // method A: 1 constructor + 1 destructor + n assignment operations // Method B: n constructor + n destructor
____________________________________________________________________________________________________________________________________
Article 27: Minimize transformation actions
#1. const_cast, dynamic_cast, reinterpret_cast, and static_cast transformation operators.
Const_cast: constant division, the only C ++-style transformation operator with this capability.
Dynamic_cast: Mainly used to perform "Security downward transformation", that is, to determine whether an object belongs
In the inheritance system. It is the only action that cannot be executed by legacy statements and is also the only possibility.
Transformation actions that consume significant operation costs.
Reinterpret_cast: it is intended to perform a low-level transformation. The actual action (and result) may depend on the compiler,
This also indicates that it cannot be transplanted, for example, transforming a pointer to int into an int. This category
Transformation is rare in low-level code.
Static_cast: Forced implicit conversion, for example
Transforming non-contst into const,
Convert int to double,
Convert the void * pointer to the typed pointer,
Convert pointer-to-base to pointer-to-derived,
However, const cannot be converted to non-const, because only const_cast can perform this operation.
#2. If possible, avoid transformation as much as possible, especially avoid dynamic_cast in efficiency-oriented code. If
There is a design that requires transformation, and we try to develop alternative designs without transformation.
Reason 1: One of the design goals of the C ++ rule is to ensure that "type errors" do not occur.
It is not safe and absurd. Please try to avoid transformation.
Reason 2: any type conversion usually requires the compiler to compile the code executed during the runtime, for example:
Class Base {...}; class Derived: public Base {...}; derived d; Base * pb = & d; // to obtain the Base * pointer value, an offset is added to the Derived * pointer during running, // The compiler will compile the code executed during the running. (Note: The offset here is not clear, // because the object Layout mode and address calculation mode vary with the compiler .)
Reason 3: dynamic_cast consumes significant runtime costs. In Deep inheritance
The strcmp comparison of the "class Name string" takes a certain amount of efficiency.
[Two alternative strategies to avoid dynamic_cast Transformation ]:
(1) In a single inheritance, the derived class pointer is directly used to replace the derived processed by the base class interface.
Class Object, for example:
// Assume that only SpecialWindows In the first Window/SpecialWindow inheritance system supports flickering effect. // try not to do this: class Window {...}; class SpecialWindow: public Window {public: void blink ();...}; typedef std: vector <std: tr1: shared_ptr <Window> VPW; VPW winPtrs ;... for (VPW: iterator iter = winPtrs. begin (); iter! = WinPtrs. end (); ++ iter) {if (Specialwindow * psw = dynamic_cast <SpecialWindow *> (iter-> get () psw-> blink ();} // It should be changed to: typedef std: vector <std: tr1: shared_ptr <SpecialWindow> VPW; VPW winPtrs ;... for (VPW: iterator iter = winPtrs. begin (); iter! = WinPtrs. end (); ++ iter) {(* iter)-> blink ();}
(2). In multi-inheritance, use the virtual function instead of what dynamic_cast does.
// When using dynamic_cast, It is shown as follows: class Window {...};... typedef std: vector <std: tr1: shared_ptr <Window> VPW; VPW winPtrs ;... for (VPW: iterator iter = winPtrs. begin (); iter! = WinPtrs. end (); ++ iter) {if (SpecialWindow * psw1 = dynamic_cast <SpecialWindow *> (iter-> get () psw1> blink (); else if (SpecialWindow * psw2 = dynamic_cast <SpecialWindow *> (iter-> get () psw2-> blink (); else if (SpecialWindow * psw3 = dynamic_cast <SpecialWindow *> (iter-> get () psw3-> blink ();} // there are a series of dynamic_cast, this will make the generated code long and slow, // and the foundation is not stable, because the inheritance system has changed, all this type of code should be checked to see whether it needs to be modified, for example, to add a new derived class, you must add a new condition Branch to a series of judgments, And is not easy to maintain. // So it should be changed to this: class Window {public: virtual void blink () ;{}// default implementation code and nothing else ...}; class SpecialWindow: public Window {public: virtual void blink (){...}; // blink does something ...};... typedef std: vector <std: tr1: shared_ptr <Window> VPW; VPW winPtrs ;... // container, containing the Window pointer, pointing to all possible derived class objects for (VPW: iterator iter = winPtrs. begin (); iter! = WinPtrs. end (); ++ iter) {(* iter)-> blink ();}
#3. If the transformation is unavoidable, hide it behind the function, so that you do not need
Transformation actions are put into their code to ensure code encapsulation and maintainability.
#4. Try to replace the old-style transformation with the C ++-style transformation (except the constructor), because
(1). They are easily identified in the code, which simplifies the process of "identifying type system damage points.
(2). Transformation actions are divided into different categories, so it is more conducive to the use of compilers to identify errors.
____________________________________________________________________________________________________________________________________
Clause 28: If handles is returned and points to the internal component of the object, ensure that the const constraint of the returned value is greater than or equal to the member function.
#1. References, pointers, and iterators are handles used to obtain an object.
#2. If handles points to an internal object and the member function is const, ensure that the returned value is also const.
Class Point {public: Point (int x, int y );... void setX (int newVal); void setY (int newVal );...}; struct RectData {Point ulhc; Point lrhc ;}; class Rectangle {... private: std: tr1: shared_ptr <RectData> pData; Point & upperLeft () const {return pData-> ulhc;} Point & lowerRight () const {return pData-> lrhc ;}...}; point coord1 (0,0); Point coord2 (100,100); const Rectangle rec (coord1, coord2); rec. upperLeft (). setX (50 ); // Here the returned handles can be changed, but it is opposite to the intention of the upperLeft const () member function. // So it should be changed to: class Rectangle {... private :... const Point & upperLeft () const {return pData-> ulhc;} const Point & lowerRight () const {return pData-> lrhc ;}...}; // this way, the write permission is prohibited while the read/write permission is guaranteed.
[This is my opinion :]
#3. For a non-const member function whose return value is of the custom (or system-defined) type, internal members are returned.
Handles (non-const) of the variable indicates that the internal variable is expected to be public, while
The getXXX () function of pass-by-valued and setXXX (X) are used for the member variables of the type respectively.
Differentiate read/write behaviors.
For example:
// When upperLeft () and lowerRight () are non-const: class Rectangle {... private :... point & upperLeft () {return pData-> ulhc;} Point & lowerRight () {return pData-> lrhc ;}...}; point coord1 (100,100); Point coord2 (); Rectangle rec (coord1, coord2); // we can set the value and obtain the value through the unified function interface as follows: rec. upperLeft (). setX (50); int val = rec. lowerRight (). getY ();
____________________________________________________________________________________________________________________________________
Article 29: Efforts for "exceptional security" are worthwhile
#1. Abnormal security functions do not leak resources or allow any data corruption even if an exception occurs.
There are three possible guarantees for such a function: basic type, strong type, and no abnormality.
(1). Basic Type: After an exception is thrown, all the constraints of the class are met, and the objects in the program are in a valid state.
(2). Strongly guaranteed type: After an exception is thrown, the program State does not change, and the function returns to the status before calling.
(3). Do not throw the exception type: ensure that no exception is thrown, all operations on the built-in type (int, pointer, etc.)
Both provide the nothrow guarantee.
(Any use of dynamic memory will throw the bad_alloc exception if it cannot meet the memory requirements)
#2. Unless the traditional non-security exception security code is called, the exception security should be ensured because the traditional non-exception Security Code
No exception security, so any code that calls it does not have exception security, so write it for it
The exception security code is meaningless.
#3. "strongly guaranteed" is often implemented using copy-and-swap.
// An example using copy-and-swap: struct PMImpl {// PMImpl = PrettyMenu Impl std: tr1: shared_ptr <Image> bgImage; int imageChanges ;}; class PrettyMenu {... private: Mutex mutex; std: tr1: shared_ptr <PMImpl> pIml;}; void PrettyMenu: changeBack (std: istream & imgSrc) {using std: swap; lock ml (& mutex); // obtain the mutex copy data std: tr1: shared_ptr <PMImpl> pNew (new PMImpl (* pImpl); pNew-> bgImage. reset (new Image (imgSrc); // modify the copy + + pNew-> ImageChanges; swap (Impl, pNew); // Replace (swap) data, release mutex} //, but it is "strongly guaranteed" that not all functions can be implemented or have implementation significance:
(1) because the strong guarantee such as copy-and-swap often consumes more time and space
To ensure better efficiency, we should replace it with "basic assurance" to make it more practical.
(2) When a function has an influence on "non-local data", it is very difficult to provide a strong guarantee.
Void someFunc {... // make a copy of the local status f1 (); f2 ();... // Replace the modified state} // assume that someFunc strongly guarantees the "local data", but f1 () and f2 () provide a strong guarantee for someFunc () it is // non-local ", so f1 (), f2 () will affect the strong guarantee expectations of someFunc (), // assume f1 (), f2 () provide a basic guarantee. Obviously, someFunc will only provide the basic guarantee. // However, if f1 (), f2 () also provide a strong guarantee, but if f1 (), f2 () if no exception is thrown, then // throws an exception, someFunc () cannot provide a strong guarantee. After all, the program state changes. // On the other hand, it is very difficult for someFunc to restore non-local data such as some databases once it is changed. In this case, it is really difficult for someFunc to provide strong assurance.
#4. The "exception Security Guarantee" provided by the function is equal to the "exception Security Guarantee" of each function called by the function at most"
From the above example ).
____________________________________________________________________________________________________________________________________