Effective C + + construction/destructor/assignment function: Clause 5-clause 9

Source: Internet
Author: User

In each class, there are constructors, destructors, assignment operators. These functions are the fundamental function of a class that controls the creation and initialization of objects, the cleanup of objects when they die, and the assignment of new values from old values. If there is a problem with this function, then the effect is extremely serious.

Article 5: Understand what functions are written and called by C + + by defaultjoin to write an empty class, then after compiling, C + + by default what functions are written.
Class empty{};
The compiler will have a default constructor, a copy constructor, an assignment operator, and a destructor after processing. These functions are both public and inline.
Class Empty{public:empty () {}empty (const empty& RHS) {}empty& operator= (const empty& RHS) {}~empty () {}};

When these functions are needed, these functions are created by the compiler, which are the most common and most used. So what did the compiler create these functions for?First, if there is no constructor, the compiler creates a default constructor that calls the constructor of the base class and the Non-static member variable. whether the destructor is a virtual function, inherits the base class, and if there is no base class, the default is non-virtual. Destructors call the base class and the destructor of the Non-static member variable. The compiler creates a copy constructor and an assignment constructor that is a shallow copy. Shallow copy can be referenced here.
In the copy constructor and assignment operator created by the compiler, the assignment constructor and assignment operator of the member variable are called when the member variable is initialized or assigned.
For assignment operators, in some cases the compiler is not composited. The assignment operator is synthesized only if the generated code is legitimate and has the appropriate opportunity to prove that it makes sense. The original English text is: The resulting code is both legal and have a reasonable chance of make sense.For example, a class contains immutable (re-assigned member variables)
Template<typename t>class namedobject{public:namedobject (const char* name, const t& value); Namedobject (const std::string& name, const t& value);p rivate:std::string& namevalue;const T objectValue;};
Two member variables, one is a reference: cannot be changed after initialization, one is constant: it is also initialized and cannot be changed.after the compiler synthesizes the assignment operator, can you change the values of the two variables when the assignment operator is called? Obviously not. The compiler rejects the action of compiling the re-assigned value. If you write the assignment operator yourself and are legitimate, the compilation will not go wrong. if the assignment operator of its base class is private, the compiler rejects the composition assignment operator for the derived class. For Some of these three functions, you can also refer to this section.
Article 6: If you do not want to use the compiler automatically generated functions, you should explicitly denySuppose there is an intermediary selling a house, and the house is a class
Class homeforsale{...};
There are no two identical houses in the world, so copy constructors and assignment operators should not be used.
Homeforsale H1; Homeforsale H2; Homeforsale H3 (H1);//should be wrong h1=h2;//should be wrong
But the compiler has helped us synthesize these two functions by default. And the functions that the compiler synthesized are all accessible to public.if we want to block the use of these two functions, we can declare them private.
Class Homeforsale{public:......private:homeforsale (const homeforsale&); homeforsale& operator= (const homeforsale&);};
Only declared, not implemented. If the normal call will be in the compile phase error (unable to access private), but the friend and member function can access, so the error will occur in the link stage, because we just declared, not implemented.
It is best to advance the error to the compile stage, after all, the sooner the error is better. Can be implemented by inheritance, in which a class that cannot be replicated is designed
Class Uncopyable{{protected:uncopyable () {}~uncopyable () {};p rivate:uncopyable (const uncopyable&); uncopyable& operator= (const uncopyable&);};
Let other classes inherit this class.
Class Homeforsale:public uncopyable{...};
In this way, if the corresponding assignment or copy constructor is produced in the Homeforsale class, the corresponding function of the base class is called, and the corresponding function of the base class is private, and a compilation error occurs.
Article 7: Declaring a virtual destructor for a polymorphic base classWhen creating hierarchical classes, it is often necessary to declare the destructor of the base class as a virtual function. this is because when using these classes, it is often used by the base class pointer or by reference (the instance of the class is on the heap), and if the destructor is by a delete + pointer, if the destructor is not a virtual function, the destructor of the current pointer to the object will not be called. This is the principle of polymorphism. in the same vein, to implement a polymorphic function, the base class is also declared as a virtual function. When a class is not used as a base class, it is a bad idea to declare its destructor as a virtual function. Because the imaginary function is called through the virtual function table, one more step pointer operation when the virtual function is called, and in addition, the memory space occupied by the object will be one more dummy pointer.
A class does not contain virtual functions and is generally not suitable for base classes. For example, the string class does not contain any virtual functions, but sometimes the programmer uses
Class Specialstring:public std::string{...};
If used with a base class pointer, a memory leak may occur when the delete occurs. Keep in mind that there are no virtual destructors for the containers in the STL.
sometimes a class contains pure virtual functions. Such a class is called an abstract class and cannot be instantiated.
Class Awov{public:virtual ~awov () = 0;};
If you think of it as a base class, there is a problem because its destructor is only declared. When a destructor is called from a derived class to a base class, if it is not defined, a link error occurs, which is to define an empty destructor
Awov::~awov () {}


Article 8: Don't let exceptions escape destructorsin C + +, destructors can throw exceptions, but this is not recommended.

Class Widget{public:......~widget () {...}}; void DoSomething () {std::vector<widget> v;......//v to destructor, the Widget's destructor is called}

Destructors are called when the container is destroyed, and if the destructor throws an exception, the remaining elements in the container should be destroyed or there may be a memory leak. At this point, if you continue to destroy other elements, there will be an exception, there are two exceptions. The presence of two exceptions can lead to ambiguous behavior. You will also encounter similar problems with other containers of the standard library or any container or array of TR1.However, there are times when you must perform some action in the destructor, which may throw an exception. For example, a class is responsible for database connections
Class Dbconnection{public:......void Close ();//Close database};
In order to ensure that the user closes the connection after use, the connection is closed in the destructor.
Class Dbconnection{public:......~dbconn ()//destructors close Connection {db.close ();} Private:dbconnection DB;};

If the call to close succeeds, then everything is fine. However, if an exception occurs, Dbconn throws an exception, which is to allow the exception to leave the destructor, which propagates the exception.There are two ways to avoid this problem: 1 . If close throws an exception, terminate the program. This is usually done by calling abort. It is generally a reasonable option to terminate a program when it is not possible to continue execution after "errors occurred during the destruction". After all, this ensures that exceptions are propagated from the destructor.
~dbconn ()//destructors close Connection {try{db.close ();} catch (...) {//Record the failure of the close call to Std::abort ();//Exit}}
2.Swallow this abnormality.
~dbconn ()//destructors close Connection {try{db.close ();} catch (...) {//Record the failure of the close call}}
Swallowing this exception is not a good idea, it suppresses important information about some of the failure actions.a good strategy is to redesign the Dbcoon interface so that customers can react to anomalies that may occur. For example, Dbconn can provide a close function on its own, giving the client a chance to handle "exceptions that occur as a result of this operation." Dbconn can also track whether the DbConnection it manages is closed, and if the answer is no, it is closed by its destructor, which prevents the database connection from being lost. But if DbConnection's destructor call Close fails, the problem goes back to the starting point.
The responsibility for calling close is transferred from the Dbconn destructor to the Dbconn client, which gives an extra insurance. The customer calls the close function themselves without burdening them, and gives them an opportunity to handle the error or they will not be able to respond.
Article 9: Never call the virtual function in the construction and destruction processcalling the virtual function in a construct or destructor does not render polymorphic. Consider an example of a class inheritance system that records orders for buying and selling. Each transaction must be recorded, and the base class writes
Class Transaction{public:transaction (); virtual void logtransaction () const//virtual Function{//log the TRANSACTIONSTD ::cout<< "This is Transaction logtransaction" <<std::endl;}}; Transaction::transaction () {logtransaction ();//called in Ctor}
In the buy and sell classes, you can override the Logtransaction () function.
Class buytransaction:public transaction{public:virtual void Logtransaction () const{std::cout<< "This is Buytransaction logtransaction "<<std::endl;}}; Class selltransaction:public transaction{public:virtual void Logtransaction () const{std::cout<< "This is Selltransaction logtransaction "<<std::endl;}};

If you create a new buy order, use
Buytransaction b;

The output was: this is Transaction logtransactionnot wanted this is buytransaction logtransaction. when constructing Buytransaction object B, the constructor of the Buytransaction class is called first, and in this constructor, the constructor of the base class Transaction is called, where theThe logtransaction () function. This outputs the displayed content. During the construction of the base class, it does not fall to the derived class to invoke the virtual function of the derived class. the constructor of the base class precedes the constructor of the derived class. The derived class object was not built during the base class constructor, and if derived class's virtual uses the local variable, then if the virtual function of derived class is actually called, Will use variables that are initialized, there will be ambiguous behavior. So C + + won't let you go this way. Another reason is that during the base class construction, the object type is base class, not the derived class. The virtual function is parsed by the compiler (resolve to) base class. If the run-time type information is used (runtimes type information), the compiler also treats it as the base class type.
the same principle applies to destructors. The destruction process and the construction process are reversed. First, the part of the derived class is reconstructed, and then the base class part is reconstructed. When you refactor to a base class, the variables in the derived class are initialized, and the object type is the base class type.
In the above code, it is easy to see that the virtual function is called in the constructor if it is rewritten in the following form:
Class transaction{public:transaction (); void Init () {logtransaction ();} virtual void logtransaction () const//virtual function{//log The transactionstd::cout<< "This is Transaction Logtransaction "<<std::endl;}}; Transaction::transaction () {Init ();}
It's not that easy to see the problem. The constructor calls the Init () function, and the virtual function is called in the init () function.
So how can we achieve the above requirements? Records the corresponding record when the object is constructed. One method is to pass information to the Logtransaction () function when the constructor is constructed, passing from the derived class constructor to the base class constructor.
 #include <iostream>class transaction{public:explicit Transaction (const std::string& parameter); void Logtransaction (const std::string& parameter) const//virtual Function{//log the transactionstd::cout<< "This is" << parameter<< "Logtransaction" <<std::endl;}}; Transaction::transaction (const std::string& parameter) {logtransaction (parameter);//called in Ctor}class Buytransaction:public transaction{public:buytransaction (): Transaction (Creatpamameter ()) {}private:static std:: String Creatpamameter () {return "Buytransaction";}}; Class Selltransaction:public Transaction{public:selltransaction (): Transaction (Creatpamameter ()) {}private:static std::string Creatpamameter () {return "Selltransaction";}}; int main () {buytransaction B; Selltransaction S;return 0;} 
This solves the problem that has been raised. The private static function is used in the derived class to create parameters. This enhances the readability of the code, and the static function does not point to a variable in the uninitialized derived class.

Effective C + + construction/destructor/assignment function: Clause 5-clause 9

Related Article

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.