"Reprint" to complete what C + + cannot do-visitor mode

Source: Internet
Author: User

Original:

Do what C + + can't do-visitor mode

With the freshly ground hot coffee, I sat in front of the monitor. "The good Day has begun again," I thought.

A very difficult task was done last night and sent to the American colleague review, so today we just need to modify the code and submit it according to their comments, and a week's mission is complete. For the remaining two or three days, I can have some spare time to look at other materials to continue to enrich myself.

With review Board Open, you can see that my code has been marked as ready to commit, but the following annotations leave my attention:

"Great job! With this solution, we can start our integration work and perform testing earlier. One thing is and we have used several ' instance of ' in the overrided function. That's double dispatch, an obvious signature for using Visitor pattern. We can switchto the.

Visitor mode I know, but what does double dispatch mean? I opened the search engine and found a few introductory articles about double dispatch and began to read them.

Double Dispatch

Of course, the most clear and accurate description of double dispatch is on Wikipedia:

In software engineering, double dispatch are a special form of multiple dispatch, and a mechanism that dispatches a functio n call to different concrete functions depending on the runtime types of both objects involved in the call. In most object-oriented systems, the concrete function, which is called from a function call in the code depends on the Dyna Mic type of a single object and therefore they is known as single dispatch calls, or simply virtual function calls.

At the end of the paragraph, I saw a familiar noun, "virtual function." As soon as I see the word, I start recalling the steps to invoke the virtual function: When the virtual function is called, the C + + runtime will first look up the virtual function table corresponding to the object, and then invoke the corresponding virtual function implementation based on the address recorded in the virtual function table. Because a virtual function table is associated with a type, the logic that is invoked on the virtual function is related to the type of the object itself.

Double dispatch, however, needs to be related to the two objects that participate in the function call. So I think: that by adding a function overload for the type, you can not implement double dispatch it? I opened Visual Studio and wrote the following code in it:

Regular car, discount for 0.03class vehicle{public:virtual double getbasediscountrate () {return 0.03;}};/ /Because it is a Mercedes-Benz special dealer, so you can get a bigger discount class Benz:public vehicle{public:virtual double Getbasediscountrate () {return 0.06;}};/ /ordinary sales staff, only according to the company's specified discount sales class sales{public:virtual double getdiscountrate (vehicle& Vehicle) {return ve Hicle.    Getbasediscountrate (); } virtual Double getdiscountrate (benz& Benz) {return Benz.    Getbasediscountrate ();    }};//Sales Manager, can offer additional benefits for Mercedes class Salesmanager:public sales{public:virtual double getdiscountrate (vehicle& Vehicle) {return vehicle.    Getbasediscountrate (); } virtual Double getdiscountrate (benz& Benz) {return Benz.    Getbasediscountrate () * 1.1;    }};int _tmain (int argc, _tchar* argv[]) {//There are two cars that need to be sold, one is a regular sedan and the other is Mercedes-Benz vehicle& Vehicle = Vehicle ();    vehicle& Benz = Benz ();    Ask for a discount on these two cars for general sales sales* psales = new sales (); Double rate = psales->getdiscountrate (vehicle);   cout << "sales:the rate for common vehicle are:" << rate << Endl;    Rate = Psales->getdiscountrate (Benz);    cout << "sales:the rate for Benz are:" << rate << Endl;    Ask the sales manager for the discount on these two cars salesmanager* Psalesmanager = new SalesManager ();    Rate = psalesmanager->getdiscountrate (vehicle);    cout << "Sales manager:the rate for common vehicle are:" << rate << Endl;    Rate = Psalesmanager->getdiscountrate (Benz);    cout << "Sales manager:the rate for Benz are:" << rate << Endl; return 0;}

  

Click to run, but the answer is not what I thought:

Ah, the sales manager does not offer an extra discount. It's a big problem. Starting the debugging capabilities of Visual Studio, I see that the statement "Psalesmanager->getdiscountrate (Benz)" Calls the overload defined for a normal car defined in the SalesManager class:

Class Salesmanager:public Sales{public:    virtual Double getdiscountrate (vehicle& Vehicle) <---- The run-time type of the parameter passed in is Benz, but the overload    {return vehicle defined for vehicle is called        . Getbasediscountrate ();    }    ......};

  

Is my understanding of function overloading incorrect? Typing "C + + overload resolution" in the search engine, I opened the C + + standard on the function overload resolution explained. The beginning of the passage gave me the answer:

In order to compile a function call, the compiler must first perform name lookup, which, for functions, may involve argume Nt-dependent lookup, and for function templates is followed by template argument deduction. If these steps produce more than one candidate function, then overload resolution are performed to select the function that Would actually be called.

Oh, yes! The function overload resolution is done at compile time. And just because we're passing in a reference to the vehicle type, the compiler has no way of knowing whether the arguments passed into Getdiscountrate () at run time are vehicle instances or Benz instances. Therefore, the compiler may choose to invoke overloads that accept vehicle type references only. If the type of the passed-in parameter Benz is no longer a vehicle reference, but rather a more specific Benz reference, then the compiler will correctly determine what function it needs to invoke:

But this is no longer based on the parameters of the type of dynamic decision need to invoke the logic, it is no longer a double dispatch. How do you achieve this effect? I was struggling to think.

"What are you thinking?" "My colleagues handed me the fruits of today's company and asked me while I was eating." I told him the program I had just written and the question I was thinking about right now.

"Now that you want to dynamically decide what logic you need to invoke, put that logic in a dynamic place, like putting it in your car class and exposing a virtual function to determine the discount rate that the car needs to use, depending on the type of car you're passing in." ”

"Oh yes," I suddenly understood. C + + at runtime Dynamic resolution of the basic method is virtual function, that is, a single Dispatch, if the object and the incoming parameters sequentially called two virtual function, then it is not a double Dispatch it? In the example of selling a car, I want to determine the logic that needs to be executed, along with the sales person's title and the type of car being sold. Then we first need to call a virtual function through a pointer to the sales type, so that we can determine the actual logic that it needs to execute at the time of sale based on the actual type of salesperson. In the process of executing these logic, we can also continue to invoke the virtual function defined on the passed parameter instance, and we can determine the logic that needs to be executed according to the type of the passed parameter.

Do what you say. I added a new virtual function getmanagerdiscountrate () in the vehicle class to allow the function implementation of the SalesManager class to be called to get the discount that the sales manager can get, and to override it in the Benz class to return a unique discount rate for Mercedes. In the sales and implementation of the SalesManager class, we need to call Getbasediscountrate () and the new Getmanagerdiscountrate () function separately to return the discount rate that normal sales and sales managers can get. In this way, we can jointly determine the discount rate used, based on the position of the salesperson and the model being sold. The changed code looks like this:

Regular cars with a discount of 0.03class vehicle{public:virtual double getbasediscountrate () {return 0.03;} Virtual Double getmanagerdiscountrate () {return 0.03;}};/    /Because it is a Mercedes-Benz special dealer, so you can get a bigger discount class Benz:public vehicle{public:virtual double Getbasediscountrate () {return 0.06;} Virtual Double getmanagerdiscountrate () {return 0.066;}};/ /ordinary sales staff, only according to the company's specified discount sales class sales{public:virtual double getdiscountrate (vehicle& Vehicle) {return ve Hicle.    Getbasediscountrate (); }};//Sales Manager, can offer additional benefits for certain models class Salesmanager:public sales{public:virtual double getdiscountrate (vehicle& Vehicle ) {return vehicle.    Getmanagerdiscountrate ();    }};int _tmain (int argc, _tchar* argv[]) {//Two vehicles required for sale vehicle& Vehicle = Vehicle ();    benz& Benz = Benz ();    Ask for a discount on these two cars for general sales sales* psales = new sales ();    Double rate = psales->getdiscountrate (vehicle);    cout << "sales:the rate for common vehicle are:" << rate << Endl; Rate = Psales->getdiscountrate (Benz);    cout << "sales:the rate for Benz are:" << rate << Endl;    Ask the sales manager for the discount on these two cars salesmanager* Psalesmanager = new SalesManager ();    Rate = psalesmanager->getdiscountrate (vehicle);    cout << "Sales manager:the rate for common vehicle are:" << rate << Endl;    Rate = Psalesmanager->getdiscountrate (Benz);    cout << "Sales manager:the rate for Benz are:" << rate << Endl; return 0;}

  

Running the program again, I found that the right results are now available:

In other words, my self-created double dispatch implementation has been able to run correctly.

Hello, Visitor

"Why do you think C + + These high-level languages do not directly support double Dispatch?" "I asked my colleagues who were working with fruit.

"No need to chant." He did not lift his head, casually answering a sentence, and picked up another fruit.

In other words, he can really eat.

"Really don't need it?" In my mind, I entered "why C + + double dispatch" in the search engine.

In the years of work, I have developed a fixed habit of learning. For example, for a point of knowledge, I often first understand how it works, then why, that is, why it works in this way, and then the when, that is, when we know why we work in this way, we can use it in the right circumstances.

Fortunately, it has been discussed in many forums why these languages do not directly support double dispatch. Simply put, a language often does not support all functions, otherwise the language will become very complex, and the compiler and the runtime will become very difficult to write. So what is supported is actually determined by the target domain of a language. When a language can solve a particular problem in a straightforward way, the language no longer has to provide a built-in solution for that particular problem. These solutions will gradually be fixed and given a unique name. For example, a common pattern in C + + is observer. This pattern is easy to implement and easy to understand. In other languages, it is possible to provide native support for observer, such as Delegate in C #. The visitor mode is essentially a standard simulation of the double dispatch function of C + +.

Next, I searched for several standard implementations of the visitor pattern and began to compare the differences between the implementation of the double dispatch and the visitor mode standard implementation. This is another habit of mine: practice can often be used to test whether there is any deviation in the understanding of a particular point of knowledge. Just like I've just committed. The process of forming your own solution often makes you understand why a particular technology has a deeper understanding of why. By comparing our solutions and standard solutions, I can find some very ingenious solutions that others have done and standardize their implementations.

I carefully examined the difference between the example of selling a car and the standard visitor mode implementation that I have just written. It is clear that the standard implementation of the visitor pattern is smarter: In the member functions of sales and SalesManager, the compiler knows the type of the instance to which this is pointed, so the function overload resolution functionality provided by C + + can be properly exploited by passing *this as a parameter into the function. This is better than my way of calling different functions in the implementation. I don't know how much:

Class Salesmanager:public Sales{public:    virtual Double getdiscountrate (vehicle& Vehicle)    {        return Vehicle. Getdiscountrate (*this); <----compiler knows that *this is an instance of SalesManager type, so you can choose to accept overloads of the SalesManager type parameter correctly    }};

  

So in the vehicle class and the Benz class, we only need to create function overloads that receive different types of parameters:

Class Benz:public Vehicle {public:     virtual double getdiscountrate (sales& Sales) {return 0.06;}     Virtual Double getdiscountrate (salesmanager& salesmanager) {return 0.066;}};

  

Class Sales;class salesmanager;//Auto interface class ivehicle{public:virtual double Visit (sales& Sales) = 0; Virtual Double Visit (salesmanager& sales) = 0;};/    /regular car with a discount of 0.03class vehicle:public ivehicle{public:virtual double Visit (sales& Sales) {return 0.03;} Virtual Double Visit (salesmanager& SalesManager) {return 0.03;}};/    /Because it is a Mercedes-Benz special dealer, so you can get a greater discount class Benz:public ivehicle{public:virtual double Visit (sales& Sales) {return 0.06;} Virtual Double Visit (salesmanager& salesmanager) {return 0.066;}}; Class Isales{public:virtual Double Accept (ivehicle& vehicle) = 0;};/ /ordinary sales staff, only according to the company's specified discount sales class Sales:public isales{public:virtual double Accept (ivehicle& vehicle) {R Eturn vehicle.    Visit (*this);        }};//Sales Manager, can offer additional benefits for certain models class Salesmanager:public isales{public:virtual double Accept (ivehicle& vehicle) { Return vehicle.    Visit (*this); }};int _tmain (int argc, _tchar* argv[]) {//two cars to be sold VEHICLe& vehicle = vehicle ();    benz& Benz = Benz ();    Ask for a discount on these two cars for general sales sales* psales = new sales ();    Double rate = psales->accept (vehicle);    cout << "sales:the rate for common vehicle are:" << rate << Endl;    Rate = psales->accept (Benz);    cout << "sales:the rate for Benz are:" << rate << Endl;    Ask the sales manager for the discount on these two cars salesmanager* Psalesmanager = new SalesManager ();    Rate = psalesmanager->accept (vehicle);    cout << "Sales manager:the rate for common vehicle are:" << rate << Endl;    Rate = psalesmanager->accept (Benz);    cout << "Sales manager:the rate for Benz are:" << rate << Endl; return 0;}

  

"How is the visitor mode extended?" "I asked myself. After all, in enterprise applications, the extensibility of each component can determine the maintainability and expansibility of the system to a great extent.

I noticed that the visitor pattern implementations above are divided into two main types: Ivehicle and Isales. It is easy to add a new car type in the visitor implementation. Derive from Ivehicle and implement the appropriate logic:

Class Fiat:public Ivehicle {public:     virtual double Visit (sales& Sales) {return 0.05;}     Virtual Double Visit (salesmanager& SalesManager) {return 0.06;}};

  

But adding a type that implements the Isales interface is very difficult: you need to change all known car types and add overloads specific to that interface implementation type.

What if you have to change the two-part composition? After looking, I also found a pattern that allows two types to be added at the same time: Acyclic Visitor. In addition, there are a series of related patterns, such as hierachical Visitor pattern. There seems to be a lot of knowledge about the visitor model.

I opened the search engine again and continued my self-learning journey. And the colleagues around also continue to struggle with the fruit.

Related reading

Singleton:http://www.cnblogs.com/loveis715/archive/2012/07/18/2598409.html in the Interview

Reference

Explanation of the C + + overload resolution: Http://en.cppreference.com/w/cpp/language/overload_resolution

Acyclic Visitor Mode: http://www.objectmentor.com/resources/articles/acv.pdf

Hierachical Visitor pattern Mode: Http://en.wikipedia.org/wiki/Hierarchical_visitor_pattern

"Reprint" to complete what C + + cannot do-visitor mode

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.