How can access to a constructor or destructor be set to private in C++

  • 2020-05-09 18:57:32
  • OfStack

Today, I was asked about the technical means commonly used in this singleton pattern in the interview. The analysis is as follows:
              many times require only one object in the current program. For example, a program has only 1 connection to the database, and only 1 mouse object. Normally we put the constructor declaration in the public section. What happens if we put it in the private section? What does that mean?
              when we declare an object in the program, the compiler to invoke a constructor (if any), and this call will usually is external, that is to say, it does not belong to the call of class object itself, if the constructor is private, because on the outside of the class not allowed to access the private members, so it will lead to a compiler error.
              for class itself, however, its static public members can be utilized, as they are independent of class objects and can be used without having to generate objects.
              since the constructor is privatized by class, to create an object, we must have access to class's private domain; Only members of class can do this. But how can we make use of its members before we construct its object? The static public member, which exists independently of the class object, can be accessed by "we". If you create an class object in an static function and return it as a reference or pointer (it is not returned as an object here, mainly because the constructor is private and no temporary object can be created externally), you gain the right to use the object.
Here's an example:


class OnlyHeapClass 
{ 
public: 
  static OnlyHeapClass* GetInstance() 
  { 
    //  create 1 a OnlyHeapClass Object and returns its pointer  
    return (new OnlyHeapClass); 
  } 
  void Destroy(); 
private: 
  OnlyHeapClass() { } 
  ~OnlyHeapClass() {} 
}; 
 
int main() 
{ 
  OnlyHeapClass *p = OnlyHeapClass::GetInstance(); 
  ... //  use *p 
  delete p; 
  return 0; 
} 

 
              this example USES the private constructor, GetInstance () as the OnlyHeapClass static member function to create the object in the memory: due to pass across function and cannot use value delivery, so we choose to create the object on the heap, so even if getInstance exit (), the object will not be released, then you can manually release.
              constructor private class design ensures the other class cannot be derived or create instances of the class from this class, there is such a purpose: for example, implementing such a class: 1 it is in memory, or a specified number of objects (can be in the private domain of the class add 1 counter static type, its initial value is 0, then the GetInstance () make some restrictions: Every time I call it the first check whether the value of the counter has reached the upper limit of the number of objects, if it is an error, otherwise only new out of new objects, and add the value of the counter 1. Finally, in order to avoid the value are produced in a copy of the new object replication, except to put the constructor is private, copy constructor also want special statement and set to private.
              can do the same thing if the constructor is designed as Protected, but it can be inherited.
              also how to ensure that only one new class object new1 is on the heap? You just define the destructor as a private member.
              because C++ is a statically bound language. During compilation, all non-virtual function calls must be parsed. Even virtual functions need to be checked for accessibility. Because of this, when an object is generated on a stack, the object is automatically destructed, which means that the destructor must be accessible. Generating objects on the heap requires a destructor because the destructor timing is controlled by the programmer. After ensuring that an object cannot be generated on the stack, you need to prove that it can be generated on the heap. The only difference between OnlyHeapClass and a 1-like object is that its destructor is private. The delete operation calls the destructor. So it doesn't compile.
              so how do you release it? The simple answer is to provide a member function to complete the delete operation. In member functions, destructors are accessible. Of course, the detele operation can also be compiled.


void OnlyHeapClass::Destroy() { 
  delete this; 
} 

The                 constructor is privatized class design to ensure that only new commands can be used to generate objects in the heap, and only to create objects dynamically, thus freely controlling the object's life cycle. However, such a class needs to provide a common interface to create and undo.
              delete new new placement new placement new

Supplement:
1. Why call it yourself? Isn't the destructor automatically called when the object ends its lifetime? When do you need to call the destructor yourself?    
                for example, in a situation where you want to do something before you destruct, but the person in your class doesn't know it, you can rewrite a function and call the destructor after everything is done. So they can only call your function to destruct the object, which guarantees that 1 will do what you want before destructing.

2. When do you need to generate piles of objects?
              heap objects are new objects, as opposed to stack objects. When to use new and when to pre-allocate it on the stack is nothing more than a question of when to use dynamic and when to use static generation. This should be analyzed on a case-by-case basis. If you know that there are at most 10 objects in a function, you can define an array of that object. Ten elements, each of which is a stack object. If you are not sure of the number, you can define a pointer to the object, create new and manage it with list or vector.

The "private" permission in             class means that private members can only be accessed within the class domain and not outside the class domain.

By defining destructors as private,               prevents users from using destructors outside the class domain. This is reflected in the following two aspects:

              1. The user is not allowed to define variables of this type, that is, objects of this type are not allowed to be created in the stack memory space. To create an object, you can only do it on the heap using new.

            2. Users are not allowed to delete objects of this type using delete in the program. Object deletion can only be realized in the class, that is to say, only the implementer of the class can achieve delete of the object, and the user cannot delete the object arbitrarily. If a user wants to delete an object, he or she can only do so by following the methods provided by the implementer of the class.

                                          1 generally don't do this; This is usually done for a specific purpose, such as the implementation of singleton.

PS: why can't constructors be virtual
In addition, the difference between constructors and virtual functions:
1. From the perspective of storage space, virtual functions correspond to a pointer to the vtable table of virtual functions, which is known to all, but the pointer to vtable is actually stored in the object's memory space. The problem is, if the constructor is virtual, it needs to be called through vtable, but the object is not instantiated yet, that is, the memory space is not there, how to find vtable? So the constructor can't be a virtual function.
              2. From a usage point of view, virtual functions are mainly used to make overloaded functions get corresponding calls in the case of incomplete information. The constructor itself initializes the instance, so there's no point in using virtual functions. So the constructor doesn't have to be a virtual function. A virtual function is a member of a subclass that can be called by a pointer or reference to its parent class. Constructors are automatically called when objects are created, and cannot be called by a pointer or a reference to the parent class, so it is stipulated that constructors cannot be virtual functions.
              3. The constructor does not need to be virtual functions, does not allow is a virtual function, because we always create an object to explicitly specify the type of the object, even though we may be through laboratory base class pointer or reference to access it but the destructor is not 1, we tend to destroy the object through a base class pointer. In this case, if the destructor is not a virtual function, it cannot correctly identify the object type and therefore cannot call the destructor correctly.
From the implementation point of view,               4. vbtl is established after the constructor is called, so the constructor cannot become a virtual function. Also, the role of the constructor is to provide initialization, which is performed only once during the lifetime of the object, is not the dynamic behavior of the object, and does not necessarily become a virtual function.
              5. When a constructor is called, one of the first things it does is initialize its VPTR. Therefore, it knows only that it is of the "current" class and completely ignores whether there is a successor behind the object. When the compiler generates code for this constructor, it generates code for the constructor of the class -- neither for the base class nor for its derived class (because the class does not know who inherits it). So the VPTR it USES must be the VTABLE for this class. Moreover, as long as it is the last constructor call, VPTR will remain initialized to point to VTABLE for the lifetime of the object, but if a later derived constructor is subsequently called, the constructor will set VPTR to point to its VTABLE, etc., until the end of the last constructor. The state of VPTR is determined by the constructor that was last called. This is another reason why constructor calls are ordered from a base class to a more derived class. However, when this series 1 constructor call is taking place, each constructor has already set VPTR to point to its own VTABLE. If the function call USES the virtual mechanism, it will only produce calls through its own VTABLE, not the last VTABLE (the last VTABLE is only available after all constructors have been called).


Related articles: