To sum up the precautions for the class: 1. do not perform too many logic-related initialization in the constructor; 2. the default constructor provided by the compiler does not initialize the variables. If other constructor is defined, the compiler does not provide the default constructor. You must provide the default constructor by yourself. 3. to avoid implicit conversion, You need to declare a single parameter constructor as explicit ;......
Class
Class is a basic unit of code in C ++ and is naturally widely used. This section lists what to do and what not to do when writing a class.
1. Constructor's Responsibilities
The constructor only performs those that have no practical significance (trivial). Note: simple initialization has no practical logic significance for program execution, because most of the "meaningful" values of the member variables are not determined in the constructor) initialization. If possible, use the Init () method to initialize the non-trivial data in a centralized manner.
Definition: Perform initialization in the constructor.
Advantage: convenient layout, no need to worry about class initialization.
Constructor:
1) The constructor does not report errors and cannot use exceptions.
2) An operation failure may result in an object initialization failure and an uncertain state.
3) When a constructor calls a virtual function, the call is not distributed to the subclass implementation. Even if there is no subclass implementation, it will remain a hidden danger in the future.
4) if someone creates a global variable of this type (although it violates the rules mentioned above), the constructor will be called before main, it may damage the assumptions implied in the constructor. For example, gflags has not been initialized.
Conclusion: if the object requires meaningful (non-trivial) initialization, consider using another Init () method and (or) adding a member tag to indicate whether the object has been initialized successfully.
2. Default Constructors)
If a class defines several member variables and no other constructor, You need to define a default constructor. Otherwise, the compiler will automatically generate the default constructor.
Definition: when a new object without parameters is created, the default constructor is called. When new [] (array) is called, the default constructor is always called.
Advantage: by default, the struct is initialized to an "impossible" value, making debugging easier.
Disadvantage: it is unnecessary for code writers.
Conclusion:
If a member variable is defined in the class and no other constructor is provided, You need to define a default constructor (No parameter ). The default constructor is more suitable for initializing objects, so that the internal state (internal state) of objects is consistent and valid.
The default constructor is provided because: if you do not provide other constructor and do not define the default constructor, the compiler will automatically generate one for you, the constructor generated by the compiler does not initialize the object.
If the class you define inherits the existing class without adding new member variables, you do not need to define the default constructor for the new class.
3. Explicit Constructors)
Use the C ++ keyword to explain a single parameter constructor.
Definition: Generally, only one constructor can be used for conversion (conversion). Note: It mainly refers to implicit conversion, which can be seen below). For example, Foo :: foo (string name): When you pass in a string to a function that needs to pass in a Foo object, the constructor Foo: Foo (string name) is called and converted to a Foo temporary object to be passed to the call function. It looks very convenient, but if you don't want to generate a new object through conversion, the trouble will also come. To avoid implicit conversions caused by constructors, you can declare them as explicit.
Advantage: Avoid out-of-date changes.
Disadvantage: none.
Conclusion:
All single-parameter constructors must be clear. In the class definition, add the keyword explicit before the single-parameter constructor: explicit Foo (string name );
Exception: in rare cases, the copy constructor can not be declared as explicit; It is specially used as a class of transparent wrapper for other classes. Similar exceptions should be explicitly stated in the notes.
4. Copy Constructors)
Use the copy constructor only when you need to copy a class object in the Code. Use DISALLOW_COPY_AND_ASSIGN if you do not need to copy the class object.
Definition: When copying a new object, you can use the copy constructor (especially when the object is passed in ).
Advantage: The copy constructor makes it easier to copy objects. The STL container requires that all content can be copied and assigned values.
Disadvantages: Implicit copying of objects in C ++ is the root cause of many performance problems and bugs. The copy constructor reduces the readability of the Code. Compared with passing by reference, it is more difficult to track objects passing by value, and the modification of objects becomes unpredictable.
Conclusion:
A large number of classes do not need to be copyable, nor need a copy constructor or value assignment operation (assignment operator ). Unfortunately, if you do not manually declare them, the compiler will automatically generate them for you and make them public.
You can consider adding an empty (dummy) copy constructor and assign values to the private class. Only declarations are required and are not defined. Since these empty programs are declared as private, the compiler reports an error when other code tries to use them. For convenience, you can use the macro DISALLOW_COPY_AND_ASSIGN:
// Prohibit the use of macros for copy constructors and assignment operations
// Should be used in private: of the class
# Define DISALLOW_COPY_AND_ASSIGN (TypeName)
TypeName (const TypeName &);
Void operator = (const TypeName &)
Class Foo {
Public:
Foo (int f );
~ Foo ();
Private:
DISALLOW_COPY_AND_ASSIGN (Foo );
};
As mentioned above, DISALLOW_COPY_AND_ASSIGN should be used in most cases. If the class does need to be copyable, the reason should be stated in the Class header file, and the copy constructor and value assignment operations should be properly defined, note that self-assignment is detected in operator =.
When you think of classes as STL containers, you may have the urge to make the classes copyable. In a similar case, the actual thing to do is to use pointers to objects in the STL container. You can consider using std: tr1: shared_ptr.
5. struct and class (Structs vs. Classes)
Use struct only when there is only data, and use class for others.
In C ++, the keywords struct and class have almost the same meaning. We add semantics to them so that we can reasonably choose which keyword to use for the defined data type.
Struct is used on passive objects, which only contains data. It may include associated constants, but does not provide function functions other than data members, the access function is implemented through direct access without calling a method. The method mentioned here is only used to process data members, such as constructors, destructor, Initialize (), and Reset (), Validate ().
If more function functions are required, the class is more suitable. If you are not sure, use the class directly.
If combined with STL, you can use struct instead of class for functions and features.
Note: The member variables of classes and struct use different naming rules.
6. Inheritance)
The use of composition (the Translator's note, which is also repeatedly emphasized by GoF in Design Patterns) is generally more appropriate than the use of inheritance. If inheritance is used, only public inheritance is used.
Definition: When a subclass inherits the base class, it contains the definition of all data and operations of the parent base class. In C ++ practice, inheritance is mainly used in two scenarios: implementation inheritance, subclass inherits the implementation code of the parent class, and interface inheritance ), subclass only inherits the method name of the parent class.
Advantage: inheritance reduces the amount of code by reusing the base class code. Because inheritance is a compile-time declaration statement, both the coders and the compiler can understand the corresponding operations and find errors. Interface inheritance can be used to enhance the function of a specific API of the class in the program. When the class does not define the necessary implementation of the API, the compiler can also detect errors.
Disadvantage: for implementation inheritance, it is more difficult to understand its implementation because the code of the implementation subclass extends between the parent class and the Child class. Sub-classes cannot override non-virtual functions of the parent class, and of course they cannot be modified. The base class may also define some data members and distinguish the physical outlines of the base class (physical layout ).
Conclusion:
All inheritance must be public. If you want to perform private inheritance, you should replace it with a base-class instance as a member.
Do not use implementation inheritance too much. combinations are usually more appropriate. Try to use inheritance only when "is a" ("is-a", the Translator's note, and other "has-a" cases, use a combination: if Bar is indeed "a Type" Foo, Bar is a subclass of Foo.
If necessary, make the Destructor virtual. If the class has virtual functions, the Destructor should be virtual functions.
Note: In special circumstances where the child class does not have any additional data members or even the parent class does not have any data members, is it necessary to call the Destructor as a semantic argument, from the perspective of programming design specifications, it is absolutely necessary to define virtual destructor in the parent class containing virtual functions.
The member function accessed only in the subclass is protected. Note that the data member should always be private.
When you redefine a derived virtual function, it is explicitly declared as virtual in the derived class. Root Cause: If the virtual function is omitted, the reader needs to retrieve all the ancestors of the class to determine whether the function is a virtual function (the Translator's note, although it does not affect its nature ).
7. Multiple Inheritance)
There are very few scenarios where multi-implementation inheritance (multiple implementation inheritance) is really needed. Only when at most one base class contains implementations, multiple inheritance is used only when other basic classes are pure Interface classes with the extension of interfaces.
Definition: Multi-inheritance allows sub-classes to have multiple base classes, which must be different from the base classes with implementations as pure interfaces.
Advantage: compared to single inheritance, multi-implementation inheritance allows you to reuse more code.
Disadvantages: there are very few times when multi-implementation inheritance is really needed. Multi-implementation inheritance seems to be a good solution. Generally, you can find a more clear and different solution.
Conclusion: multiple inheritance can be used only when all superclasses except the first one are pure interfaces. To ensure they are pure interfaces, these classes must be suffixed with interfaces.
Note: For this rule, there is an exception in Windows (note, as described in the rule exception in the last article of this translation ).
8. Interface)
An Interface is a class that meets certain conditions. These classes are suffixed with interfaces (not required ).
Definition: When a class meets the following requirements, it is called a pure interface:
1) only pure virtual functions ("= 0") and static functions (except the Destructor mentioned below );
2) No non-static data member;
3) No constructor is defined. If yes, it does not include the parameter and is protected;
4) if it is a subclass, it can only inherit the class that meets the above conditions and uses the Interface as the suffix.
The interface class cannot be directly instantiated because it declares pure virtual functions. To ensure that all implementations of the interface class can be correctly destroyed, virtual destructor must be declared for it (except for the 1st rules, the Destructor cannot be pure virtual functions ). For details, see section 12.4 of The C ++ Programming Language, 3rd edition in Stroustrup.
Advantage: using an Interface as the suffix can make others know that they cannot add implementation functions or non-static data members to the Interface class. This is especially important for multi-inheritance. In addition, for Ja