Secrets under ATL Pradesh (3)

zhaozj2021-02-16  71

Author: Zeeshan Amjad

Original link:

http://www.codeproject.com/atl/atl_underthehood_3.asp

Introduction

If you are a template master, you can enjoy the study of ATL as an enjoyment. In this section, I will try to explain some template technology used by ATL. I can't guarantee that you can become a template master after reading this section, you can only let you understand the source of ATL after reading this article.

Program 35.

#include using namespace std; template t maximum (const t & a, const t & b) {RETURN A> B? A: B;} int main () {cout << maximum (5, 10) << endl; cout << maximum ('a', 'b') << Endl; Return 0;} The output of the program is: 10B here, due to the relationship between the template function, we don't need to overload int and char respectively The function version of the data type. It is important to say that the two parameter types of the function must be consistent. But if we pass different data types, we need to inform the compiler that should take this parameter which data type.

Program 36.

#include using namespace std; template t maximum (const t & a, const t & b) {return a> b? a: b;} int main () {cout << maximum (5 , 'B') << Endl; cout << maximum (5, 'b') << Endl; return 0;} The output of the program: 66b We can also write class templates, below is a simple version Stack class template.

Program 37.

#include using namespace std; template class stack {private: t * m_pdata; int m_itop; public: stack (int p_isize = 0): m_itop (0) {m_pdata = new t [p_isize];} Void Push (T p_idata) {m_pdata [m_itop ] = p_idata;} T Pop () {return m_pdata [- m_itop];} T TOP () {return m_pdata [m_itop];} ~ stack () {if (m_pdata) {Delete [] m_pdata;}} private: Stack (const stack &); stack & operator = (const stack );}; int main () {stack a (10 ); A.push (10); a.push (20); a.push (30); cout << a.pop () << endl; cout << a.pop () << Endl; cout << A.POP () << Endl; Return 0;} There is no error check in this program, but this program is only to demonstrate the usage of the template, not really to write a professional stack class. The output of the program is: 302010 We can also pass the data type as a template parameter and set a default value for it. Let us change the program 37 (the original note: the original text is "program 36", should be 37), and transmit the size of the stack as a template parameter instead of as a parameter of the constructor.

Program 38.

#include using namespace std; time isize = 10> class stack {private: t m_pdata [isize]; int m_itop; public: stack (): m_itop (0) {} void Push (T P_IData ) {M_pdata [m_itop ] = p_idata;} T Pop () {return m_pdata [- m_itop];} T top () {return m_pdata [m_itop];} private: stack (const stack &); stack < T> & operator = (const stack &);}; int main () {stack a; a.push (10); A.PUSH (20); a.push (30); Cout << a.pop () << endl; cout << a.pop () << endl; cout << a.pop () << endl; return 0;} The output of the program is the same. The most important thing for this program is: Template There is a problem now: Which one is better? Typically, the way to transfer template parameters is better than passing parameters for constructor. why? Because when you pass the stack size as a template parameter, the array of this given data type will be created automatically; to deliver parameters to constructor means that the constructor uses new or Malloc a series of functions when running. RAM. If we have determined that it will no longer change its size (just like the copy constructor in the private segment in the above program), then the template parameters are more suitable. (Translation: Author Amjad does not implement copy constructor and assignment operator in both programs above, this is probably because both of these are not critical for this article. Here I want to point out that as the author said "Don't really write a professional stack class", "There is no error inspection", and this class's organizational structure makes accurate implementation of copy constructor and assignment operator have a certain difficulty, especially the program 37 - We Unable to get its maximum capacity from a defined stack.)

You can also pass user-defined classes as a type of parameters, but confirm that this class has all operators overloaded in that template function or template class.

For example, please see the function of the program 35 to make the maximum value. This program uses an Operator>, so if we pass your own class, then this class must be overloaded> operator. The following example demonstrates this.

Program 39.

#include using namespace std; template t maximum (const t & a, const t & b) {RETURN A> B? A: B;} Class Point {private: int m_x, m_y; public: point INT P_X = 0, INT P_Y = 0): m_x (p_x), m_y (p_y) {} BOOL Friend Operator> (Const Point & LHS, Const Point & Rhs) {Return LHS.M_X> RHS.M_X && LHS.M_Y> RHS .m_y;} Friend Ostream & Operator << (Ostream & OS, Const Point & P) {RETURN OS << "(" << p.m_x << "," << p.m_y << ")";}}; int Main () {Point A (5, 10), B (15, 20); COUT << maximum (a, b) << Endl; Return 0;} The output of the program is: (15, 20), we also Ability to pass a template class as a template parameter. Let us now write such a Point class and passed it as a template parameter to the Stack template class. The program 40.

#include using namespace std; template class point {private: t m_x, m_y; public: point (t p_x = 0, t p_y = 0): m_x (p_x), m_y (p_y) {} Bool Friend Operator> (Const Point & LHS, Const Point & RHS) {RTURN LHS.M_X> RHS.M_X && lhs.m_y> rhs.m_y;} Friend Ostream & Operator << (Ostream & OS, Const Point & P) {Return OS << "(" << p.m_x << "," << p.m_y << ")";}}; template Class Stack {private: t m_pdata [isize]; int m_itop; public: stack (): m_itop (0) {} void push (t p_idata) {m_pdata [m_itop ] = p_idata;} T POP () {Return M_PData [ m_itop];} T TOP () {return m_pdata [m_itop];} private: stack (const stack &); stack & operator = (const stack );}; int main () {St.PUSH (Point (5, 10)); st.push (Point (15, 20)); cout << st.pop () << Endl; cout << st.pop () << endl; return 0;} The output of the program: (15, 20) (5, 10) The most important part of this program is: Stack > ST Here, you must place a space between two larger than the number, otherwise the compiler will look at it. Work >> (right shift operator) and generate an error. We can do this for this program, which is to transfer the default type value for the template parameters, which is

Template Change to Template Now we don't have to pass data type when you create a Stack class object, but you still need to write this pair Aggregular arc to inform the compiler using the default data type. You can create this object:

Stack <> st; when you are defined by the class (the original note: Original here is "declare", I thought it should be "define" more accurate.) When the member functions of the template class, you still need to write Template class of template parameters.

Program 41.

#include using namespace std; template class point {private: t m_x, m_y; public: point (t p_x = 0, t p_y = 0); void setxy (t p_x, t p_y); t Getx () const; t gety () const; friend ostream & operator << (Ostream & OS, Const Point & P) {Return OS << ("<< p.m_x <<", "<< p. M_Y << "";}}; template POINT :: Point (t p_x, t p_y): m_x (p_x), m_y (p_y) {} template void Point :: setxy (t p_x, t p_y) {m_x = p_x; m_y = p_y;} template t point :: getX () const {return m_x;} template t point < T> :: get () const {return m_y;} int main () {point p; p.setxy (20, 30); cout << p << Endl; return 0;} The output of the program is: (20, 30) Let's change the program 35 slightly, pass the string value (instead of int or float) as a parameter, and look at the results. Program 42.

#include using namespace std; template t maximum (t a, t b) {RETURN A> B? A: B;} int main () {cout << maximum ("Pakistan", "Karachi ") << Endl; return 0;} The output of the program is Karachi. (Translation: In my Visual Studio.Net 2003, the output is Pakistan, which is different reason that the compiler organizes string address differently decisive, but the result of the maximum function should return the address of the memory high., This is consistent with the truth of the author.) Why? Because this char * is passed as a template parameter, Karaachi stores more in memory, and> The operator only compares the two address values ​​instead of the string itself.

So, if we want to compare the length of the string, what should you do?

The solution is the specialization of the template for the char * data type. Here is an example of a template.

Program 43.

#include using namespace std; template t maximum (t a, t b) {RETURN A> B? A: B;} template <> char * maximum (char * a, char * b) { RETURN STRLEN (A)> STRLEN (B)? A: B;} int main () {cout << maximum ("pakistan", "karachi") << endl; return 0;} As for the template class, you can also use the same The method is specialized. Program 44.

#include using namespace std; template class testclass {public: void f (t pt) {cout << "t version" << '/ t'; cout << pt << endl;}} TeMplate <> class testclass {public: Void f (int pt) {cout << "int version" << '/ t'; cout << pt << endl;}}; int main () {testclass obj1; testclass obj2; obj1.f ('a'); OBJ2.f (10); return 0;} The output of the program is: T version is similar to this, there are several classes like this. The special version, such as the CCOMQIPTR defined in Atlbase.h.

Templates can also be used in different design patterns, such as the Strategy design mode can be implemented using template.

Program 45.

#include using namespace std; class runk1 {public: void play () {cout << "runk1 :: play" << Endl;}}; class runk2 {public: void play () {cout << "Round2 :: play "<< Endl;}}; template class statulegy {private: t objt; public: void play () {objt.play ();}}; int main () {strategy Obj1 Strategy obj2; obj1.play (); obj2.play (); return 0;}

Here, Round1 and Round2 are different levels in a game, and the Strategy class relies on the transfer template parameters to determine what to do.

The output of the program is:

Round1 :: Playround2 :: Play ATL is using Strategy design mode to implement threads.

The agent design mode can also be implemented using a template, and the smart pointer is an example. Below is a simple version of the smart pointer without using the template.

Program 46.

#include using namespace std; class inner {public: void fun () {cout << "Inner :: fun" << Endl;}}; class outr; public: inner * m_pinner; public: inner * p_pinner): m_pinner (p_pinner) {} inner * operator -> () {return m_pinner;}}; int main () {Inner Objinner; Objinner; Objouter-> Fun (); return 0;} program The output is: Inner :: fun () Simply, we only overloaded the-> operator, but in the actual smart pointer, all must operate (for example =, ==,!, &, *) Need to be overloaded. The above intelligent pointer has a big problem: it can only contain pointers to the Inner object. We can write Outer class templates to cancel this limit, now let us change the program slightly. Program 47.

#include using namespace std; class inner {public: void fun () {cout << "inner :: fun" << Endl;}}; template class outr; public: t * m_pinner; public : OUTER (T * p_pinner): m_pinner (p_pinner) {} t * operator -> () {return m_pinner;}}; int main () {INNER OBJINNER; OTER Objouter (& objinner); objouter-> fun ); RETURN 0;} The output of the program is the same as the previous one, but now the Outer class can contain any type, just pass the type as a template parameter to pass.

There are two intelligent pointers, ccomptr, and ccomqiptr in ATL.

You can do some interesting things with templates, for example, your class can become subclasses of different base classes in different situations.

Program 48.

#include using namespace std; class base1 {public: base1 () {cout << "base1 :: base1" << endl;}}; class base2 {public: base2 () {cout << "Base2 :: Base2 "<< Endl;}}; template Class Drive: public t {public: drive () {cout <<" drive :: drive "<< endl;}}; int main () {drive Obj1; drive Obj2;} The output is: Base1 :: Base1Drive :: Drivebase2 :: Base2Drive :: Drive Here, the Drive class is inherited from base1 or base2 is passed when the object is created Decide to the parameters of the template.

ATL also uses this technology. When you use ATL to create a COM component, CCOMOBJECT will inherit your class. Here ATL uses templates because it does not know the name of the class you created as COM components. The CCOMOBJECT class is defined in the ATLCOM.H file. With the help of the template, we can also simulate the virtual function. Now let's re-recall the virtual function, the following is a simple example.

Program 49.

#include using namespace std; class base {public: virtual void fun () {cout << "base :: fun" << Endl;} void Dosomething ()} ();}}; class drive: public Base {public: void fun () {cout << "drive :: fun" << endl;}}; int main () {drive obj; obj.dosomething (); Return 0;} The output of the program: Drive: : Fun With the help of the template, we can achieve the same behavior.

The program 50.

#include using namespace std; template class base {public: void fun () {cout << "base :: fun" << Endl;} void dosomething () {t * pt = static_cast (this); Pt-> Fun ();}}; class drive: public base {public: void fun () {cout << "drive :: fun" << endl;}}; int main () {Drive Obj; Obj.dosomething (); return 0;} The output of the program is the same, so we can use the template to simulate the behavior of the virtual function. One interesting place in the program is Class Drive: public base {This indicates that we can pass the DRIVE class as a template parameter. Another interesting place in the program is the DOSMETHING function in the base class.

T * pt = static_cast (this); Pt-> Fun (); pointer to the base class here is converted to a pointer to the derived class because the derived class is passed as a template parameter as the Base class. This function can be performed by a pointer, since the pointer points to the object of the derived class, the object of the derived class is called.

But this has a problem: Why do we do this? The answer is: This saves additional overhead with virtual functions, which is the virtual function table pointer, virtual function table, and the additional time saved by invoking virtual functions. This is the main thinking of the components as small as possible in ATL.

Now, another problem may appear in your mind. If you rely on this overhead less technology to simulate the virtual function, why should we call the virtual function? Should we use this technology to replace all virtual functions? For this issue, I can answer you shortly: No, we can't use this technology to replace the virtual function.

In fact, this technology has some problems. First, you can't make a deeper inheritance from the DRIVE class. If you try to do this, then it will no longer be a virtual function. And for virtual functions, this will not happen. Once you declare the function as a virtual function, all functions in the derived class will become virtual functions, regardless of how deep in the inheritance chain. Now let's see what will happen when you inherit a class from Drive. Program 51.

#include using namespace std; template class base {public: void fun () {cout << "base :: fun" << Endl;} void dosomething () {t * pt = static_cast (this); Pt-> Fun ();}}; class drive: public base {public: void fun () {cout << "drive :: fun" << Endl;}}; Class MostDrive : Public Drive {public: void fun () {cout << "MostDrive :: fun" << endl;}}; int main () {MostDrive obj; obj.dosomething (); return 0;} The output and before the program One is the same. But for the case of virtual functions, the output should be: MOSTDRIVE :: Fun This technology has another problem, that is, when we use the Base class pointer to store the address of the school.

Program 52.

#include using namespace std; template class base {public: void fun () {cout << "base :: fun" << Endl;} void dosomething () {t * pt = static_cast (this); Pt-> Fun ();}}; class drive: public base {public: void fun () {cout << "drive :: fun" << endl;}}; int main () {Base * PBase = NULL; PBASE = New drive; return 0;} This program gives an error because we do not pass template parameters to the base class. Now we have changed slightly and transfer template parameters.

Program 53.

#include using namespace std; template class base {public: void fun () {cout << "base :: fun" << Endl;} void dosomething () {t * pt = static_cast (this); Pt-> Fun ();}}; class drive: public base {public: void fun () {cout << "drive :: fun" << endl;}}; int main () {Base * PBase = NULL; PBASE = New Drive; PBase-> DOSMETHING (); return 0;} Now the program works normally, and gives the output we expect, that is, Drive :: fun But when there is a number of inherits in the Base class, there will be problems. In order to better understand this, please see the procedure below. Program 54.

#include using namespace std; template class base {public: void fun () {cout << "base :: fun" << Endl;} void dosomething () {t * pt = static_cast (this); Pt-> Fun ();}}; class drive1: public base {public: void fun () {cout << "drive1 :: fun" << Endl;}}; Class Drive2 : Public Base {public: void fun () {cout << "drive2 :: fun" << endl;}}; int main () {base * PBase = null; PBASE = New Drive1; PBASE -> DOSMETHING (); delete PBase; PBase = new drive2; pBase-> DOSMETHING (); return 0;} The program will give an error at the code below: PBase = New Drive2; because PBase is a point The pointer instead of Base . Simply, you can't point the Base class's pointer to a different Drive class. In other words, you cannot use the array of Base pointers to store different derived classes, and it is feasible in the virtual function.

I hope to explore some other secrets of ATL in the next article.

转载请注明原文地址:https://www.9cbs.com/read-12001.html

New Post(0)