directory

  • polymorphism
    • Are constructors and destructors polymorphic?
    • Virtual function table
    • Virtual destructor
    • Pure virtual functions and abstract classes
    • The difference between runtime and compile-time polymorphism
  • inheritance
    • Design example
    • The difference between pointer objects and ordinary objects
    • Correctly initialize the derived class method
    • Compatibility rules for inheritance and assignment
    • Protected and private inheritance
    • Pointer cast to base and derived classes
  • How to use C to implement three C++ features
    • C language to achieve encapsulation
    • C language implementation inheritance
    • C language to achieve polymorphism

polymorphism

In the class definition, member functions preceded by the virtual keyword are called virtual functions; The virtual keyword is used only in function declarations in class definitions, not in function bodies. Pointers to derived classes can be assigned to Pointers to base classes; If the pointer points to an object of the base class, then the virtual function of the base class is called. If the pointer points to an object of a derived class, the derived class’s virtual function is called.

Which virtual function is called depends on what type of object the pointer object points to. When a “virtual function” of the same name in the base class and derived class is called through a base class reference: If the reference refers to an object of the base class, the virtual function of the base class is called. If the reference refers to an object of a derived class, then the virtual function of the derived class is called. Which virtual function is called depends on what type of object is referenced. The use of “polymorphism” in object-oriented programming can enhance the extensibility of the program, that is, when the program needs to change or add functionality, there is less code to change or add. The purpose of this pointer is to point to the object on which a member function functions, so we can use this directly in non-static member functions to represent a pointer to the object on which the function functions. The pBase pointer object refers to the derived object. There is no fun1 member function in the derived class, so the fun1 member function of the Base class is called. When this->fun2() is called in the Base::fun1() member function body, it actually refers to the fun2 member function of the derived object.

Are constructors and destructors polymorphic?

Calls to “virtual functions” in constructors and destructors are not polymorphic. Calling virtual functions in constructors or destructors is generally not recommended, because calling virtual functions in constructors and destructors does not present polymorphism.

The reason? When a base class calls the base class constructor, it is not possible to dynamically bind a derived object with a virtual function.

There is no dynamic binding for foo that is not a virtual function, so the base class part of the call; For the third bar call, it is a virtual function that implements dynamic binding, so the derived part is called.

Similarly, when a destructor is called for a class at one level of the inheritance hierarchy, it usually means that its derived class has been partially destructed and therefore does not appear polymorphic.

You can determine at compile time whether the function you are calling is defined in your own class or base class. You do not wait until run time to decide whether to call your own or a derived function.

Virtual function table

Every class that has virtual functions (or a derived class from a class that has virtual functions) has a virtual table, and Pointers to the virtual table are placed in any object of that class. The virtual function table lists the virtual function addresses of this class. You can see that the class with the virtual function has an extra 8 bytes. The size of the pointer type on the 64-bit machine is exactly 8 bytes. The extra 8 bytes are the addresses of the virtual function table. Polymorphic function call statements are compiled into a series of instructions to look up the address of the virtual function in the virtual function table based on the address of the object pointed to (or referenced by) the base class reference. The pointer to the virtual function table points to the virtual function table, and the virtual function table stores the address of the virtual function in the class, so in the call process, you can achieve the characteristics of polymorphism.

Virtual destructor

Destructors are functions that are automatically called when an object is deleted or a program exits to do some resource freeing.

Then in the polymorphic scenario, when the derived object is deleted through the pointer of the base class, only the destructor of the base class is usually called, which leads to the situation that the destructor of the derived object is not called and there is resource leakage.

The destructor of a derived class can be virtual without declaring it. When a derived object is deleted through a pointer to the base class, the destructor of the derived class is called first, then the destructor of the base class, again following the “construct first, fabricate later” rule.

So develop good habits:

A class that defines a virtual function should define its destructor as a virtual function. Alternatively, if a class is intended to be used as a base class, the destructor should also be defined as virtual. Note: the constructor cannot be defined as a fictitious function structure formed in the base class call the base class constructor, part of the derived class has not been constructed, how would it be possible to use virtual function to achieve dynamic binding born derived class object, so the construction base class B part, called the base class function bar;

Pure virtual functions and abstract classes

Pure virtual function: a virtual function without a function body. Classes that contain pure virtual functions are called abstract classes

An abstract class can only be used as a base class to derive new classes. Objects of an abstract class cannot be created. Pointers and references to an abstract class can point to objects of a class derived from an abstract class

The difference between runtime and compile-time polymorphism

Compile-time polymorphism, which is the difference in argument lists to distinguish between different functions, automatically becomes two different functions after compilation.

Runtime polymorphism: using the technique of late binding, it is not known before the program runs which method will be called, and at runtime, the operator dynamically calculates the address to be called. Runtime polymorphism, also known as dynamic binding, refers to determining the actual type of the referenced object during execution (rather than compilation), determining and invoking properties and methods based on the actual type

inheritance

Derived classes are derived by modifying and extending the base class. In derived classes, new member variables and member functions can be extended. A derived class has all the member functions and variables of the base class, whether private, protected, or public. Note that the private members of the base class cannot be accessed in the member functions of the derived class. A derived object contains a base object, and the base object is stored before the new member variable of the derived object, equivalent to the base object being the header. The size of a derived object = the size of the member variable of the base object + the size of the derived object’s own member variable

Design example

Suppose you want to write a community dog management system:

You need to write a “master” class. You need a dog.

Given that dogs have only one owner, but an owner can have up to 10 dogs, how should the “owner” and “dog” classes be designed and used? Let’s start with a couple of examples.

Set an owner object pointer for the dog class; Set an array of Pointers to dog objects for the master.

class CDog;
class CMaster// Master class {
    CDog  * pDogs[10]; // Dog object pointer array
};

class CDogA dog / / {
    CMaster * pm;   // An object pointer to the host class
};
Copy the code



This is because the dog and its owner are independent. Through the pointer, the dog can point to one owner, and the owner can also point to 10 dogs belonging to himself at the same time, which will be more flexible.

The difference between pointer objects and ordinary objects

If the pointer object is not used, the A object is also constructed along with the B object. This is not the case with Pointers, which are good for efficiency and memory.

class Car
{
    Engine engine; // Member objects
    Wing * wing;   // Member pointer object
};
Copy the code

Define a car. All cars have an engine, but not all cars have a wing. In this way, for cars without a wing, the wing only takes a pointer, which is also convenient to judge. Spatially, using Pointers saves space from constructing B objects. Instead, it opens up only one pointer in the object, rather than the size of an object B. In terms of efficiency, using Pointers is suitable for reuse. Object B is accessible not only to object A, but also to other objects that need to use it. Pointer objects can use the property of polymorphism. A pointer to a base class can point to any derived class in the derived chain. Pointer objects need to be instantiated only when they are needed, but when they are not in use, they need to be reclaimed manually.

Correctly initialize the derived class method

class Bug {
private :
    int nLegs; int nColor;
public:
    int nType;
    Bug (int legs, int color);
    void PrintBug (a){}; }; Bug::Bug( int legs, int color)
{
    nLegs = legs;
    nColor = color;
}
class FlyBug : public Bug  // FlyBug is a Bug derived class
{
    int nWings;
public:
    FlyBug( int legs,int color, int wings);
};
Copy the code

The correct FlyBug constructor: Initializes the base class by calling the base class constructor, which is always executed before executing the constructor of a derived class. So when a derived class is destructed, the derived class destructor is executed first, followed by the base class destructor.

// The correct FlyBug constructor:
FlyBug::FlyBug ( int legs, int color, int wings):Bug( legs, color)
{
    nWings = wings;
}
Copy the code

Compatibility rules for inheritance and assignment

An object of a derived class can be assigned to a base object. A derived object can be initialized. The address of a reference to a derived object can be assigned to a base pointer.

Protected and private inheritance

In protected inheritance, the public and protected members of the base class become protected members of the derived class. In private inheritance, public members of the base class become private members of the derived class, and protected members of the base class become inaccessible members of the derived class. Private or protected derived objects cannot be assigned, referenced, or pointer to base objects as public derived objects can.

Pointer cast to base and derived classes

Public; the pointer to a derived object can be assigned directly to the base pointer:

Base *ptrBase = & objDerived;
Copy the code

PtrBase refers to an object of a Derived class. *ptrBase can be thought of as an object of the Base class, whose public members are accessed directly through ptrBase, but not through ptrBase. Members of an objDerived object that belong to a Derived class but not to a base class. You can convert ptrBase to a pointer to the Derived class by forcing a pointer type conversion.

Base * ptrBase = &objDerived;
Derived *ptrDerived = ( Derived * ) ptrBase;
Copy the code

How to use C to implement three C++ features

C language to achieve encapsulation

AchievePackage has member variable _A and two function Pointers, which are assigned the addresses of the two functions in InitStruct function (function name is its address or &fun1, which can get the same value), so InitStruct function is equivalent to the constructor of this structure here, It is possible to initialize the value of its member variable _A and assign a value to its function pointer at the same time as the object is defined.

C language implementation inheritance

If there is an inheritance relationship between two classes, the subclass must have the related attributes (variables) and methods (functions) of the parent class.

Use composition to implement inheritance in C:

Structure nesting:

C language to achieve polymorphism

Polymorphism is a dynamic binding mechanism that calls a function of type Virtual in the parent class through a pointer or reference to the parent class. If you want to call a function of a subclass using a pointer/reference to its parent class, you need to declare it virtual in the parent class, and it must have the same argument list and return the same value as the function in the subclass. C++ polymorphism is achieved by overwriting, that is, the functions of the parent class are overwritten by the subclass! When you call this function, you must look to see which class object you are binding to, and then call the function in that class!

//C simulates C++ inheritance and polymorphism

typedef void (*FUN)(a);      // Define a function pointer to implement the inheritance of member functions

struct _A/ / {the parent class
    FUN _fun;   // Since C constructs cannot contain functions, they can only be implemented with function Pointers outside

    int _a;
};

struct _B{/ / subclass
    _A _a_;     // We can inherit from the parent class by defining a base class object in a subclass
    int _b;
};

void _fA()       // A function of the same name as the parent
{
    printf("_A:_fun()\n");
}
void _fB()       // Subclass function of the same name
{
    printf("_B:_fun()\n");
}
Copy the code

Here is the test code:

//C language simulation inheritance and polymorphic testing
_A _a;    // Define a parent object _a
_B _b;    // Define a subclass of _b
_a._fun = _fA;        // An object of the parent class calls a function of the same name
_b._a_._fun = _fB;    // The object of the subclass calls the function of the same name as the subclass

_A* p2 = &_a;   // Define a superclass pointer to an object of the superclass
p2->_fun();     // Call a function of the same name as the parent
p2 = (_A*)&_b;  // make the parent pointer point to the object of the child class, strong because the type does not match
p2->_fun();     // Call the subclass's function of the same name
Copy the code

Effect: