A brief analysis of C++ constructor virtualization

  • 2020-10-31 21:52:02
  • OfStack

Virtual constructor

When you have a pointer or reference but do not know what the actual type of the object is pointing to, you can call virtual functions to perform the behavior of an object of a particular type (ES3en-ES4en). You only call a constructor if you don't already have an object but you know exactly what type of object you want. So where do virtual constructors come in?

For example, suppose you write a program to do the work of a news report, a news report consists of words or pictures. You can manage them this way:


class NLComponent { // Used for  newsletter components  Abstract base class 
public:
  ... // Contain at least 1 A pure virtual function 
}; 

class TextBlock: public NLComponent {
public:
  ... //  It doesn't include pure virtual functions 
}; 

class Graphic: public NLComponent {
public:
  ... //  It doesn't include pure virtual functions 
};

class NewsLetter { // 1 a  newsletter  Object by NLComponent  Object's linked list composition 
public: 
  NewsLetter(istream& str);
  ...

private:
  list<NLComponent*> components;
};

The list class used in NewsLetter is a standard template class (STL). The object NewLetter is stored on disk when it is not running. In order to be able to create Newsletter objects from disk-mounted alternatives, it is a convenient way to have the NewLetter constructor take the istream argument. When the constructor needs some core data structure, it reads information from the stream. The pseudocode for this constructor looks like this:


NewsLetter::NewsLetter(istream& str)
{
  while (str) {
 from str Read the 1 a component object ;
 Add objects to newsletter the  components
 Object into the linked list ;
  }
}

Or, apply this technique to a separate function called readComponent, as shown below:


class NewsLetter {
public:
  ...

private:
  //  In order to establish the 1 a NLComponent Objects from str Read the data ,
  //  To establish component  And return 1 A pointer. 
  static NLComponent * readComponent(istream& str);
  ...
};

NewsLetter::NewsLetter(istream& str)
{
  while (str) {
    //  the readComponent The returned pointer is added to components At the end of the list, 
    // "push_back" 1 A member function of a linked list, used to insert operations at the end of the list. 
    components.push_back(readComponent(str));
  }
}

Consider the work done by readComponent in figure 1. It creates a new object, either TextBlock or Graphic, based on the data it reads. Because it can create new objects, it behaves like a constructor, and because it can create different types of objects, we call it a virtual constructor. Virtual constructors are the ability to create different types of objects based on the data input to them.

Virtual copy constructor

There is also a special kind of virtual constructor, the virtual copy constructor, which also has a wide range of USES. The virtual copy constructor returns a pointer to a new copy of the object that called the function. Because of this behavior, the name of the virtual copy constructor 1 is usually copySelf, cloneSelf, or clone as shown below. Few functions implement it 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 constructor
  {
    return new TextBlock(*this);
  }
  ...
};

class Graphic: public NLComponent {
public:
  virtual Graphic * clone() const // virtual copy constructor
  {
    return new Graphic(*this);
  }
  ...
};

Virtual copy constructors of classes simply call their real copy constructors. So "copy" means the same as a real copy constructor. If the real copy constructor makes a simple copy, the virtual copy constructor makes a simple copy as well. If the real copy constructor makes a full copy, the virtual copy constructor makes a full copy as well.

Note that the implementation of the above code takes advantage of the more lenient virtual function return value type rules that have been adopted recently. A virtual function redefined by a derived class does not have to have the same return type as a virtual function of the base class. A function of a derived class can return a pointer (or a reference) to a derived class of a base class if the function's return type is a pointer (or a reference) to a derived class of the base class. This is not a bug in the type checking of C++, which makes it possible to declare functions such as virtual constructors. This is why the clone function of TextBlock can return TextBlock* and clone of Graphic can return Graphic*, even though the clone return value of ES46en-ES47en is of type NLComponent*.

The virtual copy constructor in NLComponent makes it easy to implement a (normal) copy constructor for NewLetter:


class NewsLetter {
public:
  NewsLetter(const NewsLetter& rhs);
  ...

private:
  list<NLComponent*> components;
};

NewsLetter::NewsLetter(const NewsLetter& rhs)
{
  //  Traverse the entire rhs Linked list, using a virtual copy constructor for each element 
  //  Copy the element into this object component A linked list. 
  //  For details on how the following code works, see the terms 35 . 
  for (list<NLComponent*>::const_iterator it = rhs.components.begin(); it != rhs.components.end(); ++it)
  {
    // "it"  Point to the rhs.components Is the current element of the call element clone The function, 
    //  You get this element 1 Make a copy and put the copy in 
    // Of this object component The end of a list. 
    components.push_back((*it)->clone());
  }
}

Traversing the entire component linked list in the copied NewsLetter object, invoking the virtual constructor for each element object in the list. We need a virtual constructor here because the list contains Pointers to the NLComponent object, but we know that each pointer points to either the TextBlock object or the Graphic object. No matter who it points to, we want to make the right copy, and virtual constructors can do that for us.

The above content is basically from More Effective C++.

Above is a brief analysis of C++ constructor virtualization in detail, more information about C++ constructor virtualization please pay attention to this site other related articles!


Related articles: