More effective C + +----Tips & (25) virtualization of constructors and non-member functions

Source: Internet
Author: User

Skills
Most of the content in this book is a programming guideline. Although these guidelines are important, programmers cannot live by the rules alone. There is a long-time cartoon called "Philip Cat" (Felix the Cat), Philip Cat whenever encountered difficulties, it will take its trick bag. If a cartoon character has a trick package, then C + + programmers should have more. Think of this chapter as the starter of your trick bag.
When designing C + + software, there are always some problems to be bothered about. How do you make constructors and non-member functions have the characteristics of virtual functions? How do you limit the number of instances of a class? How do you prevent objects from being built in the heap? How can you be sure to build the object in the heap? When a member function of some other class is called, how can you set up an object and let it do some things automatically? How can you allow different objects to share data structures and let each user assume that each one has its own copy? How do you distinguish between read and write operations for operator[]? How do you create a virtual function whose behavioral characteristics depend on the dynamic type of the different objects?
All of these questions (and more) are answered in this chapter, and in this chapter I describe the common problems that C + + programmers have encountered, and the solutions have been proven. I call these solutions tricks, but when they are archived in stylized style (stylized fashion), they are also used as idiom and pattern. Whatever you call it, the following information will benefit you as you work on your software development day after day. It will convince you that no matter what you do, you can always do it in C + +.
Item M25: Virtualization of constructors and non-member functions
literally, talking about "virtual constructors" doesn't make sense. when you have a pointer or reference, but do not know what the true type of the object is pointing to, you can call the virtual function to complete the behavior of the particular type (type-specific) object. You call the constructor only if you don't have an object yet and you know exactly what type of object you want. So what about the virtual constructor?
Very simple. Although virtual constructors may seem meaningless, they are of great use ( If you think meaningless ideas are useless, how do you explain the achievements of modern physics? (because the main achievement of modern physics is the narrow, general theory of relativity, quantum mechanics, these theories all seem absurd, not well understood.) Translator note). For example, suppose you write a program that is used for news reporting, and every news story is made up of words or pictures. You can manage them like this:
Class Nlcomponent {               //For Newsletter Components public://                           Abstract base class                             ... Contains at least 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:                           //By Nlcomponent object                             ... The linked list of the private:  list<nlcomponent*> components;};

Diagram between classes

The list class used in newsletter is a standard template class (STL), and STL is part of the standard C + + class library (see effective C + + clause 49 and clause M35). The behavior characteristics of a list type object are somewhat like a doubly linked list, although it is not implemented in this way. the object newletter is stored on disk when it is not running. in order to be able to build a newsletter object by a substitute on the disk, it is convenient to have the Newletter constructor with the IStream parameter. When a constructor requires some core data structures, it reads information from the stream:

Class NewsLetter {public:  NewsLetter (istream& str);  ...};

The pseudo-code for this constructor is this:

Newsletter::newsletter (istream& str) {while  (str) {    reads the next component object from STR;     Add the object to the list of newsletter's Components;  }}

Alternatively, use this technique for another independent function called Readcomponent, as follows:

Class NewsLetter {public:  ... private:  //Read data from STR for establishing the next Nlcomponent object,  //Establish component and return a pointer.  Static Nlcomponent * readcomponent (istream& str);   ...};  Newsletter::newsletter (istream& str)  {    while (str) {        //Adds the pointer returned by Readcomponent to the last of the list of components,        //"push_back" a list of member functions that are used to insert at the end of the list.        components.push_back (readcomponent (str));    } }

Consider the work that Readcomponent has done. it establishes a new object based on the data being read, either TextBlock or graphic. Because it can create a new object, it behaves like a constructor, and because it can build different types of objects, we call it Virtual Constructorsa virtual constructor is the ability to create different types of objects based on the data entered into it. Virtual constructors are useful in many situations, and reading object information from disk (or through a network connection, or from a tape drive) is just one of those applications. (Wq Raise: readcomponent () Implementation can be described in "Tom Swan C + + Programming Tips")
There is also a special kind of virtual constructor-- Virtual Copy Constructors-It also has a wide range of uses.The virtual copy constructor can return a pointer to a new copy of the object that called the function. because of this behavioral trait, the name of the virtual copy constructor is generally copyself,cloneself or as follows, which is called clone. There are very few functions that can be implemented in such a straightforward way:

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);  }          //constructor  ...}; class Graphic:public nlcomponent {public:  virtual Graphic * Clone () const            // Virtual copy  {return new Graphic (*this);}             Constructor ...  };
   

As we have seen, the virtual copy constructors of classes simply call their true copy constructors. so the meaning of "copy" is the same as the real copy constructor. If the true copy constructor only makes a simple copy, the virtual copy constructor also makes a simple copy. If the true copy constructor has a full copy, the virtual copy constructor also makes a full copy. If the real copy constructor does something peculiar, like a reference count or copy-on-write (see clause M29), then the virtual constructor does the same. Exactly the same, that's great.
Note that the implementation of the above code takes advantage of the relatively loose virtual function return value type rule that was recently adopted. A virtual function that is redefined by a derived class does not have to have the same return type as the base class's virtual function. if the return type of a function is a pointer to a base class (or a reference), the function of the derived class can return a pointer (or reference) to a derived class of the base class. This is not a vulnerability in C + + type checking, which makes it possible to declare functions such as virtual constructors. This is why the clone function of TextBlock can return the reason that textblock* and graphic clone can return graphic*, even if Nlcomponent's clone return value type is nlcomponent*.
C + + Primer in version fifth: The return type of a virtual function in a derived class must also match the base class function, but there is an exception to this rule, which is invalid when the class's virtual function return type is a pointer or reference to the class itself. As shown in the example above. A virtual function in a base class is implicitly a virtual function in a derived class, and when a derived class overrides a virtual function, the parameters of the function in the base class must match the parameters in the derived class exactly!
The virtual copy constructor in Nlcomponent makes it easy to implement the Newletter (normal) copy constructor:

Class NewsLetter {public:  NewsLetter (const newsletter& RHS);  ... private: list<nlcomponent*> components  ;}; Newsletter::newsletter (const newsletter& RHS) {  //traverse the entire RHS linked list, using the virtual copy constructor of each element  //Copy the element into the component list of this object.  //For details on how the following code works, see clause M35.  for (List<nlcomponent*>::const_iterator it =          rhs.components.begin ();       It! = Rhs.components.end ();       ++it) {   //"It" points to the current element of rhs.components, invokes the element's clone function,  //obtains a copy of the element, and places the copy on//At the end of the  component linked list of the object.    Components.push_back ((*it)->clone ());}  }

If you are unfamiliar with the Standard Template Library (STL), this code may be somewhat confusing, but the rationale is simple: traverse the entire component linked list in the copied newsletter object, invokes the virtual (copy) constructor of each element object in the linked list . We need a dummy constructor here, because the list contains pointers to nlcomponent objects, but we know that each pointer is not pointing to the TextBlock object or to the graphic object. no matter who it is pointing to, we want to do the right copy operation, and the virtual constructor can do that for us.
virtualization of non-member functions
just as a constructor cannot really be a virtual function, a non-member function cannot be a true virtual function.(see effective C + + clause 19). However, since it is understandable that a function can construct different types of new objects, it is also There are non-member functions that can behave differently depending on the dynamic type of the parameter。 For example, suppose you want to implement an output operator for TextBlock and graphic objects. The obvious approach is to virtualize this output operator. But the output operator is operator<&lt, and the function ostream& as its left argument (left-hand argument) (that is, put it on the left side of the function argument list), which makes it impossible for the function to be TextBlock or The graphic member function.
(This can be done, but look at what happens:
Class Nlcomponent {public:  //Unusual declaration of the output operator  virtual ostream& operator<< (ostream& str) const = 0;< c2/> ...}; Class Textblock:public Nlcomponent {public:  //Virtual output operator (also unusual)  ostream& operator<< (ostream & str) const;}; Class Graphic:public Nlcomponent {public:  ///Virtual output operator (makes it unusual)  ostream& operator<< (ostream & str) const;}; TextBlock T; Graphic G; ... t << cout;                                            print T to cout via virtual operator<<//.                                            //Unusual syntax G << cout;                                            print G to cout via virtual operator<<//.                                            //Unusual grammar

The user of the class has to put the stream object to the right of the << symbol, as opposed to the general use of the output operator. In order to get back to normal grammar, we have to move operator<< out of the TextBlock and graphic classes, but if we do, we can no longer declare it as virtual. )
Another way is to declare a virtual function (such as print) for the print operation to define it in the TextBlock and graphic classes. But if so, the syntax for printing TextBlock and graphic objects is inconsistent with other types of objects that use operator<< as the output operator, and these workarounds are unsatisfactory. What we want is a non-member function called operator<<, which has behavior characteristics like the print virtual function. The description of what we want is actually very close to how to get a description of it. We define the operator<< and print functions and 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;  ... }; inlineostream& operator<< (ostream& s, const nlcomponent& c) {  return c.print (s);}

A non-member function with virtual behavior is simple. You write a virtual function to do the work, and then write a non-virtual function, and it does nothing but call this virtual function. To avoid this syntactic trick, you can certainly inline this non-virtual function (see effective C + + clause 33).
Now that you know how to virtualize non-member functions based on one of their parameters, you might wonder if they could be virtualized based on more than one parameter. Yes, but it's not easy. How difficult is it? See article M31; it will be devoted to this issue.



More effective C + +----Tips & (25) virtualization of constructors and non-member functions

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.