Chapter 4 Design and Declaration)

Source: Internet
Author: User
6.1 Clause 18: Make interfaces easy to use correctly and hard to use incorrectly)

Clause 13 indicates that the customer places the newly applied resources in the smart pointer to avoid resource leakage. A constructor provided by STD: tr1: shared_ptr receives two real parameters. One is the managed pointer, and the other is the called "deleteer" when the number of references is 0. Think like this:
STD: tr1: shared_ptr <investment> createinvestment ()
{
// Create a null shared_ptr and use getridofinvestment as the deleteer
STD: tr1: shared_ptr <investment> retval (static_cast (Investment *) (0 ),
Getridofinvestment );
Retval = ...; // Point retval to the correct object
Return retval;
}
Example 6-1-1 A constructor of shared_ptr
Tr1: shared_ptr has a special property: it will automatically call its "exclusive delimiters for each Pointer", thus eliminating another potential customer error: the so-called "cross-DLL problem".
Good interfaces are easy to use correctly and cannot be misused. Methods to promote correct use include interface consistency and compatibility with built-in behaviors.

6.2 clause 19: design class is like design type (treat class design as type design)

Skipped. For more information, see Objective C ++, Chinese Third Edition p84.

6.3 Clause 20: replace pass-by-reference-to-const with pass-by-value (prefer pass-by-reference-to-const to pass-by-value)

This rule does not apply to built-in types, as well as STL iterators and function objects. For them, pass-by-value is often more appropriate.
By default, C ++ is passed by value.
Class person {
Public:
Person ();
Virtual ~ Person ();
PRIVATE:
STD: string name;
STD: String address;
};
Class student: Public Person
{
Public:
Student ();
~ Student ();
PRIVATE:
STD: String schoolname;
STD: String schooladdress;
};
Bool validatestudent (student s );
Student Plato;
Bool platoisok = validatestudent (Plato); // call the function by value
Example 6-3-1 passing parameters by value
Based on Plato, the transfer cost of this function is "one student copy constructor call, plus one student destructor call". Student and person have four string objects. In this case, there are "Six constructor and six constructor".
The more efficient transmission method is pass by reference-to-Const.
Bool validatestudent (const student & S );
No constructor or destructor is called. Const is necessary, because otherwise, the caller will worry whether validatestudent will change the student they passed in.
Using the by reference method to pass parameters can avoid the slicing (Object cutting) problem. When a derived class object is passed by value as a base class object, only the copy constructor of the base class will be called, cutting the features of the derived class object.
Class window {
Public:
STD: string name () const;
Virtual void display () const;
};
Class required wwithscrollbars: public window
{
Public:
Virtual void display () const;
};
Void printnameanddispaly (window W)
{
STD: cout <W. Name ();
W. Display ();
}
Descriwwithscrollbars wwsb;
Printnameanddispaly (wwsb); // features are cut by value
// The correct method
Void printnameanddispaly (const window & W)
{
STD: cout <W. Name ();
W. Display ();
}
Example 6-3-1 passing parameters by value

6.4 Clause 21: when an object must be returned, do not think about returning its reference (don't try to return a reference when you must return an object)

Consider a class used to represent rational numbers. It contains a function to calculate the scores of two rational numbers.
Class rational {
Public:
Rational (INT Numerator = 0, int Denominator = 1) (); // It is not declared as explicit why?
PRIVATE:
Int N, D;
Friend const rational operator * (const rational & LHS, const rational & RHs );
};
Rational A (1/2); // A =
Rational B (3/5); // B =
Rational c = a * B; // C = 3/10
Example 6-4-1 rational class
We expect "a rational object with a value of 3/10 already exists".
Method 1: Create a stack.
Const rational operator * (const rational & LHS, const rational & RHs)
{
Rational result (LHS. N * RHS. N, LHS. D * RHS. D );
Return result;
}
Example 6-4-2 create a rational class in the stack space
This function returns the result indicated by a reference, but the result is a local object, and the local object is destroyed before the function exits.
Method 2: Create in heap space.
Const rational operator * (const rational & LHS, const rational & RHs)
{
Rational * result = New Rational (LHS. N * RHS. N, LHS. D * RHS. D );
Return * result;
}
Rational w, x, y, z;
W = x * y * z; // how to release a new pair twice
Example 6-4-3 create a rational class in the heap Space
The same statement calls operator * twice. Therefore, if you call New twice, you need to delete it twice. But how do I delete two times?
Const rational operator * (const rational & LHS, const rational & RHs)
{
Static rational result;
Result = (LHS. N * RHS. N, LHS. D * RHS. D );
Return result;
}
Example 6-4-4 the rational class creates a static object in the stack space
The preceding functions are not multi-threaded security functions. If the following code is displayed:
Bool operator = (const rational & LHS, const rational & RHs)
Rational a, B, c, d;
...
If (A * B) = (C * D ))
{
} Else {
}
The expression (A * B) = (C * D) is always true, regardless of the value of A, B, C, and D. Because the caller always sees the current value of the static rational object.
Correct syntax:
Rational operator * (const rational & LHS, const rational & RHs)
{
Return rational (LHS. N * RHS. N, LHS. D * RHS. D );
}
Example 6-4-5 operator * of the rational class returns an object
When you have to choose between returning a reference and returning an object, your job is to pick out the one with the correct behavior.

6.5 Clause 22: declare member variables as private (declare data members private)

Skipped. For more information, see Objective C ++.

6.6 clause 23: Native replaces member functions with non-Member and non-friend functions (prefer non-member non-friend functions to member functions)

This class is used to clear the element cache next week, clear the history of the accessed URL, and remove all cookies from the system.
Class webbrowser {
Public:
Void upload AchE ();
Void clearhistory ();
Void removecookies ();
Void cleareverything (); // member function call the first three functions
};
Void cleareverything (webbrowser & WB) // non-menber Function
{
WB. Sort AchE ();
WB. clearhistory ();
WB. removecookies ();
}
Example 6-6-1 rational class
The reason we advocate encapsulation: it enables us to change things and only influence limited customers. Only member functions of class and friend functions can access private member variables. If you choose between a member function and a non-member and non-friend function and provide the same functions, the non-Member and non-friend functions are encapsulated in a large manner, because it does not increase the number of functions that can access the private component in the class.
Notes
First, this article applies only to the non-Member and non-friend functions. The access permission of the friend function to the class private member is the same as that of the member function.
Second, let the function "Become a non-member of Class" because it cares about encapsulation. It does not mean that it "cannot be a member of another class".
In C ++, it is natural to make clearbrowser a non-member function and be located in the same namespace of webbrowser:
Namespace webbrowserstuff {
Class webbrowser {...};
Void clearbrowser (webbrowser & WB );
...
}

6.7 Clause 24: If all parameters require type conversion, use the non-member function (declare non-member functions when type conversions shocould apply to all parameters)

Suppose you design a class to express rational numbers. It seems reasonable to allow the integer "implicit conversion" to rational numbers.
Class rational {// allow int-to-rational implicit conversion
Public:
Rational (INT Numerator = 0, int Denominator = 1) (); // It is not declared as explicit why?
Int numerator () const;
Int denominator () const;
Const rational operator * (const rational & RHs) const; // terms and 21
};

Rational oneeight (1/8); // A =
Rational onehalf (1/2); // B =
Rational result = oneeight * onehalf; // very good
Result = Result * oneeight; // very good
Example 6-7-1 rational class
However, when you try mixed arithmetic, only half of it works:
Result = onehalf * 2; // very good
Result = 2 * onehalf; // Error
Multiplication should satisfy the exchange law. Why? See:
Result = onehalf. Operator * (2); // very good
Result = 2. Operator * (onehalf); // Error
Yes, onehalf is a class object containing the operator * function, so the compiler calls this function. However, there is no corresponding class or operator * member function for integers 2. The compiler will also try to find non-member operator * (within the namespace or within the global scope) that can be called as follows ):
Result = Operator * (2, onehalf); // Error
In fact, there is no such function that accepts int and rational as the non-member operator * parameter, so the search fails.
Also, because non-explicit constructor is involved, the compiler will make result = onehalf * 2 legal and convert 2 to a rational object. If the rational constructor is explicit, none of the following statements are compiled.
Result = onehalf * 2; // error (no problem with the non-explicit constructor)
Result = 2 * onehalf; // error (even when the non-explicit constructor is used)
The conclusion is that this parameter is a qualified participant in implicit type conversion only when it is included in the parameter column.

To support the multiplication exchange law, let operator * be a non-member function, and the compiler is allowed to perform implicit type conversion on each real parameter.
Class rational {// allow int-to-rational implicit conversion
Public:
Rational (INT Numerator = 0, int Denominator = 1) (); // It is not declared as explicit why?
Int numerator () const;
Int denominator () const;
};
Const rational operator * (const rational & LHS,
Const rational & RHs); // now becomes a non-member
{
Return rational (LHS. numerator () * RHS. numerator (),
LHS. denominator () * RHS. denominator ());
}
Rational Onefourth (1/4); // A =
Rational result;
Result = Onefourth * 2; // very good
Result = 2 * Onefourth; // very good, very powerful
Example 6-7-2 non-member of the rational class
The opposite side of the member function is the non-member function, not the friend function.

6.8 Clause 25: consider writing a swap function without throwing an exception (consider support for non-throwing swap)

Swap assigns the object values to each other. STD: swap typical implementation:
Namespace STD {
Template <typename T>
Void swap (T & A, T & B ){
T temp ();
A = B;
B = temp;
}
}
Example 6-8-1 STD: swap implementation
As long as the T type supports copying behavior, the swap function can be completed. However, for the Type "pointing to an object with a pointer containing real data", the default swap does not need to do so. A common manifestation of this design is the so-called "pimpl" technique (pimpl is short for "pointer to Implementation ).
Class widgetimpl {// class designed for widget data
Public:
...
PRIVATE: // there may be a lot of data, which means the replication takes a long time.
Int A, B, C;
STD: vector (double) V;
...
};
Class widget {// class designed for widget data
Public:
Widget (const widget & RHs );
Widget & operator = (const widget & RHs) // copy the widgetimpl object
{
...
* Pimpl = * (RHS. pimpl );
...
}
...
PRIVATE:
Widgetimpl * pimpl; // pointer to the data contained in the object
};
Example 6-8-2 Design of widgetimpl
Once you want to replace the values of two widgets, the only thing you need to do is replace the pimpl pointer, but swap does not know. So it not only copies three widgets, but also copies three widgetimpls objects. Very inefficient.
To solve this problem, we need to make STD: swap special for widgets.
Class widget {// Add the swap Function
Public:
Void swap (widget & other)
{
STD: swap (pimpl, other. pimpl );
}
...
};
Namespace STD {
Template <> // revised STD: swap special version
Void swap <widget> (T & A, T & B ){
A. Swap (B );
}
}
Example 6-8-3 STD: special version of swap
The "template <>" at the beginning of the function indicates that it is a fully-specialized version of STD: swap, the "<widget>" after the function indicates that this specific version is designed for "T is a widget. We can declare the special version as friend to access the member variables of the widget, but we can also declare a public member function swap in the widget class.

Assume that widgets and widgetimpl are both Class templates rather than classes. For example:
Template <typename T>
Class widgetimpl {...};
Template <typename T>
Class widget {...};
Putting a swap member function in the widget is as simple as before, but it encounters a stream in STD: swap.
Namespace STD {
Template <typename T> // The revised STD: swap version
Void swap <widget <t> (widget <t> & A, // invalid
Widget <t> & B)
{
A. Swap (B );
}
}
Example 6-8-4 STD: Illegal special version of swap
We are attempting to convert (partially sepcialize) a function template (STD: swap), but C ++ only allows the class template to be special, and does not work in function templates. This part should not be compiled. The practice is to simply add an overloaded version to it.
Namespace STD {
Template <typename T> // STD: Heavy Load version of swap
Void swap (widget <t> & A, widget <t> & B)
{
A. Swap (B );
}
}
Example 6-8-5 STD: swap version
Generally, the function templates is reloaded. However, STD is a special namespace and new templates cannot be added to STD. The correct method is to declare a non-member swap so that it can call the member swap.
Namespace widgetstuff {
Template <typename T>
Class widgetimpl {...};
Template <typename T>
Class widget {...};
Template <typename T> // non-member version of the non-STD Space
Void swap (widget <t> & A, widget <t> & B)
{
A. Swap (B );
}
}
Example 6-8-6 STD: swap version
This method works for both classes and class templates.
Once the compiler sees the call to swap, they will find and call the appropriate swap. C ++ naming rules: make sure that any T exclusive swap within the global scope or the namespace where T is located is found. If T is a widget and is located in the namespace widgetstuff, the compiler will call the swap of widgetstuff Based on the Search rules determined by the real parameters. If no exclusive swap exists, the compiler will call the STD swap.
Notes
When STD: swap is inefficient at your type, provide a swap member function and make sure this function does not throw an exception.
If you provide a member swap, you should also provide a non-member to call the former. For classes (non-templates), please also specialize in STD: swap.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.