Static and dynamic linking for C++

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

Recently, I have been looking at the contents of the destructor function, and I have seen some good articles about it. Here, I also have some of my own experience. Here, I write 1 and record 1.

Linking refers to the process in which a computer program itself is related to each other. In this linking process, the mapping relationship between the operation call (function call) in the program and the code segment that performs the operation (function) needs to be determined.

This means that there are multiple implementations of this function, and linking is the operation of mapping the call to the corresponding implementation.

According to the different stages of linkage, it can be divided into static linkage and dynamic linkage.

Static binding

Static linking occurs during the program compilation join phase, also known as early linking, because it is done before the program begins to run. This linkage, which occurs during the program compilation phase, resolves the relationship between the program's operation invocation and the code that performs the operation at compile time.

Dynamic binding

The compiler does not exactly guide the function to be called at the compilation stage, but only at the execution of the program can it determine the function to be called. For this purpose, it needs to guide the function to be called exactly and requires the linking work to be done at the program run time, which is called dynamic linking. In C++, dynamic linking is implemented with the support of virtual functions.

Both static and dynamic linking are polymorphic, and they make different choices for different implementations at different stages.

Dynamic linking requires the support of virtual functions, which is determined by the working principle of virtual functions. It is precisely because virtual functions are used to realize dynamic linking that the efficiency of dynamic linking is slightly lower than that of static linking. In general, the compiler handles virtual functions by adding a hidden member to each object, which holds a pointer to an array of function addresses, known as the table of virtual functions (virtual function table, vtbl). Virtual function table stores the address of the virtual function for class objects, called virtual functions, the program will see vtbl address stored in the object, and then turned to the corresponding function address table, if you use a class declaration that are defined in the first virtual functions, the program will use the first function is used in the address, and perform with the address of the function, if use the third virtual functions in the class declaration, application will use the address of a function of the third element in an array.

The concept of virtual functions is the essence of C++. Note when dealing with virtual functions:

Defining a function as a virtual function does not mean that the function is not implemented (it can have its own implementation).

The other bit virtual function is defined to allow the subclass to call this function with a pointer to the base class (provides a way for the base class to call subclass functions)

Define a function as a pure virtual function, which means that the function is not implemented (the derived class must implement this virtual function if the declaration is followed by =0 virtual func() =0)

A class with a pure virtual function is an abstract class, which cannot be used to generate an object (that is, it cannot be instantiated), but can only be derived, and its derived class is still an abstract function if it does not implement a pure virtual function.

Virtual destructor

Virtual destructors are defined as virtual functions. A memory leak occurs if we allocate memory space in a derivation, but the destructor of the base class is not a virtual destructor. Let's take an example


#include <iostream>

using namespace std;

class Base
{
  public:
    Base(){ data = new char[10];}

    ~Base(){ cout << "destroying Base data[]\n";delete []data;}
  private:
    char *data;
};

class Derive: public Base
{
  public:
    Derive(){ D_data = new char[10];}

    ~Derive(){ cout << "destroying Derive data[]\n";delete []D_data;}
  private:
    char *D_data;
 };

int main()
{
Base *basePtr = new Derive();

delete basePtr;
return 0;
}

Output results:


$ ./a.out
destroying Base data[]

In this example, the destructor of the derived class is not called, which is a disaster in a large project. The reason is that we defined an Base pointer in the main function. When we delete1 dynamically allocated Base pointer, the Base pointer now points to an object of type Derive. However, the compiler still calls the destructor according to type Base and does not execute the virtual destructor of type Derive. Changing the destructor of class Base to virtual destructor ensures that the correct version of the destructor is executed.

Finally, I'd like to summarize some common questions about virtual functions:

Virtual functions are dynamically bound, that is, using Pointers and references to virtual functions can correctly find the corresponding function of the actual class, rather than executing the function that defines the class, which is the basic function of virtual functions. Constructors cannot be virtual functions. Moreover, when the virtual function is called in the constructor, the corresponding function of the parent class is actually executed, because the polymorphic function is disable. Destructors can be virtual functions, and, in a complex class structure, this is often required. To define a function in a base class as a pure virtual function is to define the class as a bit abstract class and not to instantiate an object. Pure virtual functions usually have no defined body, but they can be owned. If the destructor of Base is a pure virtual function, define Base::~Base(){... } to define its definition body) Destructors can be pure virtual, but pure virtual destructors must have a body of definition because the destructor's call is implicit in subclasses. An impure virtual function must have a body of definitions, otherwise it is an error. The override virtual function definition of a derived class must be exactly 1 of its parent class, except for one exception. If the parent class returns a pointer or reference, the child class override can return a derivation of this pointer (or reference). For example, virtual Base clone() is defined in Base; virtual Derive clone() can be defined in Derive.

Related articles: