Introduction: What is a virtual function?
In fact, from the perspective of virtual functions, we have to trace back to the definition of virtual functions:
In short, the definition of a virtual function can be expressed as follows:
Definition: A member function that is declared as virtual in a base class and redefined in one or more Derived classes
Syntax: virtual function return type function name (parameter table) {function body}
Purpose: Implement polymorphism. by pointing to the base class pointer of a derived class, access the member functions with the same name in the derived class.
A virtual function must be a non-static member function of the base class. Its access permission can be protected or public. The general form of defining a virtual function in the class definition of the base class is as follows:
Form:
1 class base class name {2 ...... 3 virtual return value type function name to be reloaded in the derived class (parameter list); 4 };
As for the use of virtual functions, this can be defined according to your own wishes. After all, the use scenario rules are determined by someone.
I. virtual functions are never called during construction and analysis.
1. Constructor
1 class transaction {// base class 2 public for all transactions: 3 transaction (); 4 virtual void logtransaction () const = 0; // make a log record of different types (log entry) 5... 6}; 7 transaction: transaction () // Implementation of the base class constructor 8 {9... 10 logtransaction (); // The final action is to log this transaction 11} 12 class buytransaction: Public transaction {// derived class13 public: 14 virtual void logtransaction () const; // log this transaction 15... 16}; 17 class selltransaction: Public transaction {// derived class18 public: 19 virtual void logtransaction () const; // log (log) this type of transaction 20... 21 };
If we perform the following operations:
BuyTransaction b;
Derivation of its execution process:
A buytransaction constructor is called, but the transaction constructor is called first (because the base class components in the object of the derived class are constructed before the components of the derived class are constructed ). The last line of the transaction constructor calls the virtual function logtransaction. The called logtransaction is the version in the transaction (base class), not the version in the buytransaction. During the construction of the base class, the virtual function will never fall to the class of the derived class.
In other words, during the execution of the base class constructor, the virtual function is not a virtual function.
Reason ① --- direct cause
Because the base class constructor is executed earlier than the derived class constructor, when the base class constructor is executed, the member variables of the derived class are not initialized. If the virtual function called during this period drops to the class of the derived class, the function of the derived class will almost inevitably use local member variables, and those member variables have not been initialized. This obviously violates the "requirement to use components not initialized inside the object "!
Reason ② --- Root Cause
During the construction of the base class of the derived class object, the object type is base class rather than the derived class. Not only does the virtual function be parsed to the base class by the compilerRuntime type information (※appendix 1)The object will also be treated as the base class type. In this example, when the transaction constructor is executing and intends to initialize "base class components in the buytransaction object", the object type is transaction. The object does not become a derived class object before the constructor of the derived class starts execution.
The same applies to destructor. Once the destructor of the derived class starts to be executed, the member variables of the derived class in the object are displayed with undefined values. Therefore, any part of C ++ includes virtual functions, dynamic_casts and so on.
2. Avoid "calling virtual functions during the constructor or destructor operation"
After reading the above example, I may not find the idea of this problem if the compilation fails. But in fact, a big pitfall is waiting for you to jump!
1 class transaction {2 public: 3 transaction () // call non-virtual... 4 {Init ();} 5 virtual void logtransaction () const = 0; 6... 7 private: 8 void Init () 9 {10... 11 logtransaction (); // call virtual here! 12} 13 };
From the execution perspective alone, there is no problem with the link compilation of this code, but because logtransaction is a pure virtual function in transaction, when the pure virtual function is called, most of the Execution Systems terminate the program and then send a message (warning or something) about the result ).
Amendment 1 (temporary ):
Obtain the implementation code (example) for the logtransaction function in transaction)
However, the Tangle is that the pure virtual function has already installed the code, and it is no longer a pure virtual function.
Amendment 2 (permanent ):
One way is to change the logtransaction function to non-virtual in class transaction, and then require the derived class constructor to pass necessary information to the transaction constructor, then the constructor can safely call non-virtual logtransaction.
1 class transaction {2 public: 3 explicit transaction (const STD: string & loginfo); 4 void logtransaction (const STD: string & loginfo) const; // now it is a non-virtual function 5... 6}; 7 transaction: transaction (const STD: string & loginfo) 8 {9... 10 logtransaction (loginfo); // currently a non-virtual call 11} 12 class buytransaction: Public transaction {13 public: 14 buytransaction (parameters): transaction (createlogstring (parameters )) // pass the log information to the base class constructor 15 {...} 16... 17 private: 18 static STD: String createlogstring (parameters); 19 };
Because Virtual functions cannot be called downward from the base class, during the construction, you can replace them with "make the derived classes pass necessary constructor information up to the base class constructor. In other words, the constructor cannot determine the type, so it will call its own function without the virtual function.Example ()
Iii. Differences between constructors in Java and C ++ in calling virtual functions
If Java calls a virtual function in the constructor, it can be compiled without running errors, but the running result may not be what you want.
In Java, the virtual function executed by the constructor will be a function in the classes Class (if the classes override the virtual function ), but here we have doubts. The constructor is built from the parent class, that is, the struct class has not yet been constructed. Therefore, if the functions of the struct class are executed at this time, it is undoubtedly insecure. Therefore, there is no problem in Java.
Java writes the type information in the class file, which can distinguish different classes. In fact, different classes can be distinguished. c ++ does not, it's just a piece of memory structure (the C ++ virtual function requires a pointer offset, the base class or the derived class is different, so you need to determine the class)
※Appendix 1 rtti
1. Definition
Runtime type information (runtime type information) can also be called rtti (run-time type identification). You can obtain data type or class information at runtime.
2. Generation of rtti:
Let's take a look at a piece of code.
1 Figure *p; 2 p = new Circle(); 3 Figure &q = *p;
During execution, P points to an object, but it is difficult to know the class information of this object. You cannot obtain the information of the Q referenced object.
In the C ++ environment, header files contain Class and Class definitions. However, these materials are only used by the compiler and are not left after compilation. Therefore, during the execution period, you cannot find the category information of the object, including the category name, data member name and type, function name and type, etc, rtti was born to solve this problem.
Different applications require different rtti ranges. For specific application scenarios, refer to http://wenku.baidu.com/view/d252ffc52cc58bd63186bd39.html.
3. Introduction
Generally, in Object-Oriented Programming, most people advocate using virtual member functions instead of the rtti mechanism. However, in many cases, virtual functions cannot overcome their limitations. When dealing with heterogeneous containers and underlying class layers (such as MFC), it is inevitable to dynamically judge the object type, that is, to detect the dynamic type. How can we determine the dynamic type of an object? The answer is to use the operator in rtti:TypeidAndDynamic_cast.
(1) The typeid operator returns the pointer and the actual type referred to by the reference;
(2) The dynamic_cast operator securely converts a pointer or reference of the base class type to a pointer or reference of the derived type.
This involves four types of forced Data Conversion in C ++: dynamic_cast, const_cast, static_cast, and reinterpret_cast.
① Dynamic_cast is used to point to a base class of the derived class.Pointer or referenceConvert toPointer or reference(It can only be used for classes containing virtual functions )!
dynamic_cast < ObjectType-ID* > ( ObjectType*)
If you want to successfully convert objecttype * To objecttype-ID *, this possibility must exist. That is to say, the object that objecttype * points to must "contain" objecttype-ID * points, in this way, you can succeed. example:
1 A * pA = new B; 2 C * Pc = new C; 3 B * pb = dynamic_cast <B *> Pa; // OK .4 C * Pc = dynamic_cast <C *> Pa; // fail. compilation is successful, but PC is null.
② When performing type conversion, the conversion operator first checks whether the conversion can be successful. If the conversion is successful, the conversion is successful. If the conversion fails, the conversion returns a 0 value, if the conversion is a reference, a bad_cast exception is thrown. Bad_cast exception can refer to http://technet.microsoft.com/zh-cn/library/82f1eehz
③ Rtti has four common application scenarios: Exceptions handling-Java, dynamic casting, module integration, and object I/O.