More Effective C ++ Terms 25

zhaozj2021-02-08  259

skill

Most of the content involved in this book are programming guidelines. Although these guidelines are important, programmers cannot live alone. There is a very early cartoon called "Felix the cat), Philip Cats will take its trick bag whenever they encounter difficulties. If a cartoon role has a trick package, then the C programmer should be more. Turn this chapter to your trick package.

When designing C software, there will always be some problems in three places. How do you make constructor and non-member functions have a characteristic of virtual functions? How do you limit the number of instances of a class? How do you prevent it from building an object in the stack? How can you ensure that the object is built in a heap? The member functions of some other classes do you have to create an object and let it automate some operations automatically? How do you make different objects share the data structure, let each user have their own copies? How do you distinguish Operator [] read and write? How do you create a virtual function, its behavior characteristics depend on the dynamic type of different objects?

All of these issues (more) have been answering in this chapter. In this chapter, I will describe the problems that C programmers are generally encountered, and the solution has also been confirmed. I called these solutions, but when they were archived in stylized fashion, they were also Idiom and Pattern. No matter what you call it, when you work in software development in a day, the following information will benefit you. It will make you believe that no matter what you do, you can always use C to complete it.

Terms 25: Virtualization of constructor and non-member functions

From the literal, it is meaningless to talk about the "virtual constructor". When you have a pointer or reference, but don't know what it is in the real type of object, you can call the virtual function to complete the behavior of a Type-Specific object. You will call the constructor when you haven't had an object yet. So what does the virtual constructor talk?

Very simple. Although the virtual constructor seems to seem to have meaningless, in fact, they have a very large use (if you think that there is no meaning, how do you explain the achievements of modern physics?) (Because the main achievements of modern physics are Narrow, generalized relativity, quantum mechanics, these theories seem to be very ridiculous, not well understood. Translator's note). For example, suppose you write a program to work for news reports, a news report consists of text or image. You can manage them like this:

Class nlcomponent {// Used for Newsletter Components

PUBLIC: // Abstract base class

... // Contains only one pure virtual function

}

Class textBlock: public nlcomponent {

PUBLIC:

... // Does not contain pure virtual functions

}

Class graphic: public nlcomponent {

PUBLIC:

... // Does not contain pure virtual functions

}

Class newsletter {// a newsletter object

Public: // consists of NLcomponent objects ... //

Private:

List Components;

}

The relationship between the class is shown below:

The List class used in Newsletter is a standard template class (STL), STL is part of the standard C class library (see Effective C Terms 49 and Terms 35). The behavior of the List type object Some icons are two-way linked lists, although it does not implement in this way.

Object Newletter is not running on disk. In order to be able to establish a newsletter object by an alternative to a disk, let the Newletter constructor are a very convenient way to use the iStream parameter. When the constructor requires some core data structure, it reads information from the stream:

Class newsletter {

PUBLIC:

Newsletter (Istream & Str);

...

}

The pseudo code for this constructor is like this:

Newsletter :: Newsletter (Istream & Str)

{

While (str) {

Read the next Component object from the STR;

Add objects to Newsletter Components

Object's linked list;

}

}

Or, use this tip for another independent function called Readcomponent, as shown below:

Class newsletter {

PUBLIC:

...

Private:

/ / Read data from STR to establish the next nlcomponent object,

/ / Establish Component and return a pointer.

Static NLComponent * Readcomponent (Istream & Str);

...

}

Newsletter :: Newsletter (Istream & Str)

{

While (str) {

// Add the pointer returned by Readcomponent to the final,

// "Push_Back" member function of a linked list is used to insert the linked list.

Components.push_back (readcomponent (STR));

}

}

Consider the work made by Readcomponent. It establishes a new object based on the data read, or TextBlock or graphic. Because it can establish new objects, its behavior is similar to constructor, and because it can establish different types of objects, we call it virtual constructor. The virtual constructor refers to the establishment of different types of objects that can be established depending on the data input to it. The virtual constructor is used in many applications, from the disk (or over the network, or from the tape drive) is only one of the applications.

There is also a special type of virtual constructor - virtual copy constructor - also has a wide range of purposes. The virtual copy constructor returns a pointer to point to the new copy of the object that calls the function. Because this behavioral characteristic, the name of the virtual copy constructor is generally Copyself, Cloneseelf, or like this is called Clone. Little will have functions to implement it in such a direct manner:

Class nlcomponent {

PUBLIC:

// Declaration of Virtual Copy Constructor

Virtual nlcomponent * clone () const = 0;

...

}

Class textBlock: public nlcomponent {

PUBLIC: Virtual TextBlock * Clone () const // virtual Copy

{RETURN New TextBlock (* this);} // Constructionor

...

}

Class graphic: public nlcomponent {

PUBLIC:

Virtual graphic * Clone () const // virtual copy

{RETURN New Graphic (* this);} // Constructor

...

}

As we see, the type of virtual copy constructor is just calling their true copy constructor. Therefore, the meaning of "copy" is the same as the true copy constructor. If the real copy constructor is only a simple copy, the virtual copy constructor is also a simple copy. If the real copy constructor makes a comprehensive copy, the virtual copy constructor is also a comprehensive copy. If the real copy constructor do some strange things, the reference count or Copy-ON-WRITE (see Terms 29), then the virtual constructor does this. It is exactly the same, great.

Note that the implementation of the above code utilizes a relatively loose virtual function return value type rule that has been adopted. The virtual function defined by the derived class does not have to have the same return type as the virtual function of the base class. If the return type of the function is a pointer (or a reference) pointing to the base class, the functionality of the party can return a pointer (or reference) that pointing to the classic class. This is not a vulnerability on the type C type, which makes it possible to declare a function like a virtual constructor. That is why TextBlock's Clone function returns TextBlock * and Graphic Clone to return Graphic *, even if NLComponent's Clone return value is nlcomponent *.

The virtual copy constructor in nlcomponent enables the implementation of the newletter (normal) copy constructor is easy:

Class newsletter {

PUBLIC:

Newsletter (Const Newsletter & RHS);

...

Private:

List Components;

}

Newsletter :: Newsletter (Const Newsletter & RHS)

{

// Traverse the entire RHS linked list, use the virtual copy constructor of each element

// Copy the element into the Component Link list of this object.

// For details on how to run the following code, see Terms 35.

For (List :: const_iterator it =

RHS.Components.begin ();

It! = rhs.components.end ();

IT) {

// "IT" points to the current element of RHS.Components, calling the Element's clone function,

// Get a copy of this element and put this copy to

// The end of this object's Component Link list.

Components.push_back ((* it) -> clone ());

}

}

If you are not familiar with the standard template library (STL), this code may be somewhat disintegration, but the principle is simple: traverse the entire Component Link list in the copied Newsletter object, call the virtual constructor of each element object in the linked list. We need a virtual constructor here because the linked list contains pointers that point to NLComponent objects, but we know that each pointer does not point to the TextBlock object to point to Graphic objects. No matter who it points, we want to make the correct copy operation, the virtual constructor can do this for us. Virtualization non-member function

As constructor can't really be a virtual function, non-member functions cannot be a real virtual function (Participate in Effective C Terms 19). However, since a function can construct a new object of different types of different types, there is also a non-member function, which can also be different depending on the different dynamic types of the parameters. For example, suppose you want to implement an output operator for TextBlock and Graphic objects. Obvious approach is virtualized this output operator. However, the output operator is the operator <<, and the function is made as its left parameter (ie, put it on the left translator of the function parameter list), which is impossible to make the function become TextBlock or GRAPHIC member function.

(You can do this, but look at what will happen:

Class nlcomponent {

PUBLIC:

/ / An unusual statement on the output operator

Virtual Ostream & Operator << (Ostream & Str) const = 0;

...

}

Class textBlock: public nlcomponent {

PUBLIC:

// Virtual output operator (equally unusual)

Virtual Ostream & Operator << (Ostream & Str) Const;

}

Class graphic: public nlcomponent {

PUBLIC:

// Virtual output operator (unusual)

Virtual Ostream & Operator << (Ostream & Str) Const;

}

TextBlock T;

Graphic g;

...

t << cout; // via Virtual Operator <<

// Print T into COUT.

// unusual syntax

g << cout; // via Virtual Operator <<

// Print G to COUT.

// unusual syntax

The user has to put the Stream object in the right side of the symbol, which is the opposite of the output operator. In order to go back to normal syntax, we must remove the Operator << from the TextBlock and Graphic class, but if we do this, you can't declare it as a virtual. )

Another method is to declare a virtual function (for example, Print) to define it in the TextBlock and Graphic classes. But if this, the syntax of printing the TextBlock and Graphic object is inconsistent with other types of objects using Operator << as an output operator,

These solutions are not satisfactory. What we want is a non-member function called Operator <<, which has the behavior characteristics of the Print virtual function. The description we want is actually very close to how it gets its description. We define the Operator << and print function, let the former call the latter! Class nlcomponent {

PUBLIC:

Virtual Ostream & Print (Ostream & S) Const = 0;

...

}

Class textBlock: public nlcomponent {

PUBLIC:

Virtual Ostream & Print; Ostream & S) Const;

...

}

Class graphic: public nlcomponent {

PUBLIC:

Virtual Ostream & Print; Ostream & S) Const;

...

}

inline

Ostream & Operator << (Ostream & S, Const NLComponent & C)

{

Return C.Print (s);

}

Non-member functions with virtual behavior are simple. You write a virtual function to complete the work, then write a non-virtual function, it doesn't do just call this virtual function. In order to avoid this sentenceful trick caused a function call overhead, you can of course inline this non-virtual function (see Effective C Terms 33).

Now you know how to make non-member functions virtualized according to their parameters, you may want to know if they may make it virtually according to more than one parameter? Yes, but it is not easy. How difficult is it? See Terms 31; it will specifically discuss this problem.

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

New Post(0)