C++ advanced polymorphism

insert image description here

The concept of polymorphism

The concept of polymorphism : Generally speaking, to complete a certain behavior, when different objects complete it, different states will be produced .

In C++Java, polymorphism ( Polymorphism) is an important concept of object-oriented programming, which allows you to use a unified interface to deal with different data types, thereby increasing the flexibility and scalability of the code. Polymorphism is divided into two types: compile-time polymorphism (static polymorphism) and run-time polymorphism (dynamic polymorphism).

  1. Compile-time polymorphism (static polymorphism) : Compile-time polymorphism is achieved through function overloading ( Function Overloading) and operator overloading ( ). Operator OverloadingThis kind of polymorphism can determine the function or operator to be called at the compilation stage, and choose to execute different codes according to the parameter type of the function or operator.

    For example, function overloading allows you to define multiple functions with the same name, but with different parameter lists, and the compiler chooses to call the appropriate function according to the type of parameters passed.

    void print(int num) {
          
          
        cout << "Printing an integer: " << num << endl;
    }
    
    void print(double num) {
          
          
        cout << "Printing a double: " << num << endl;
    }
    

    Operator overloading allows you to define operator behavior for custom types.

  2. Runtime Polymorphism (Dynamic Polymorphism) : Runtime Polymorphism is Virtual Functionachieved through inheritance and virtual functions (). This kind of polymorphism allows you to decide which function to call according to the actual type of the object at runtime, thus realizing dynamic function distribution.

    In runtime polymorphism, base classes can define virtual functions, and derived classes can override these virtual functions. By using a base class pointer or reference to a derived class object, you can call a derived class virtual function at runtime.

    class Shape {
          
          
    public:
        virtual void draw() {
          
          
            cout << "Drawing a shape." << endl;
        }
    };
    
    class Circle : public Shape {
          
          
    public:
        void draw() override {
          
          
            cout << "Drawing a circle." << endl;
        }
    };
    
    class Square : public Shape {
          
          
    public:
        void draw() override {
          
          
            cout << "Drawing a square." << endl;
        }
    };
    

    when using it:

    Shape* shapePtr;
    
    Circle circle;
    Square square;
    
    shapePtr = &circle;
    shapePtr->draw(); // 调用 Circle 的 draw()
    
    shapePtr = &square;
    shapePtr->draw(); // 调用 Square 的 draw()
    

    In this way, according to the actual object type pointed by the pointer, it is realized to call different functions according to the object type at runtime.

All in all, C++ polymorphism allows you to deal with different types of objects through a unified interface, both at compile time and run time. This greatly improves the maintainability and scalability of the code.

Definition and Implementation of Polymorphism

1. The conditions for polymorphism

Then there are two conditions to constitute polymorphism in inheritance :

  1. The virtual function must be called through the pointer or reference of the base class
  2. The called function must be a virtual function, and the derived class must override the virtual function of the base class

insert image description here

2. Virtual function

Virtual function: the virtualmodified class member function is called a virtual function

class Person {
    
    
public:
	virtual void BuyTicket() {
    
     cout << "买票-全价" << endl;}
};

3. Rewriting of virtual functions

Rewriting (covering) of virtual functions : There is a virtual function in the derived class that is exactly the same as the base class ( that is, the return value type, function name, and parameter list of the derived class virtual function are exactly the same as those of the base class virtual function ), called a subclass The virtual function of the overrides the virtual function of the base class

class Person {
    
    
public:
	virtual void BuyTicket() {
    
     cout << "买票-全价" << endl; }
};

class Student : public Person {
    
    
public:
	virtual void BuyTicket() {
    
     cout << "买票-半价" << endl; }
};

void Func(Person& p)
{
    
     p.BuyTicket(); }
int main()
{
    
    
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}

Note : When rewriting the virtual function of the base class, the virtual function of the derived class can also be rewritten without adding virtualkeywords (because the virtual function of the base class is inherited after inheritance, and the derived class still maintains the virtual function attribute ), but this kind of writing is not very standardized, and it is not recommended to use it like this

insert image description here

The essential principle of polymorphism meets the two conditions of polymorphism, then when calling, it will find the corresponding virtual function address in the virtual table pointing to the object, and call it

insert image description here

When the polymorphic call runs, find the function address in the virtual table pointing to the object and call it

insert image description here

Ordinary calls have already confirmed the function address when compiling and linking, and call directly at runtime

4. Two exceptions for virtual function rewriting

  1. Covariant (base class and derived class virtual function return value types are different)

When the derived class overrides the virtual function of the base class, the return value type of the virtual function of the base class is different. That is, when the base class virtual function returns a pointer or reference to the base class object, and the derived class virtual function returns a pointer or reference to the derived class object, it is called covariance

class A{
    
    };
class B : public A {
    
    };

class Person {
    
    
public:
	virtual A* f() {
    
    return new A;}
};

class Student : public Person {
    
    
public:
	virtual B* f() {
    
    return new B;}
};
  1. Rewriting of destructors (base class and derived class destructors have different names)

If the destructor of the base class is a virtual function, as long as the destructor of the derived class is defined, no matter whether virtualkeywords are added, it will be rewritten with the destructor of the base class, although the names of the destructors of the base class and the derived class are different . Although the function names are different, it seems to violate the rewriting rules, but in fact it is not. It can be understood that the compiler has made special treatment for the name of the destructor, and the name of the destructor is uniformly processed after compilation destructor.

class Person {
    
    
public:
	virtual ~Person() {
    
    cout << "~Person()" << endl;}
};

class Student : public Person {
    
    
public:
	virtual ~Student() {
    
     cout << "~Student()" << endl; }
};

int main()
{
    
    
	Person* p1 = new Person;
	Person* p2 = new Student;
	delete p1;
	delete p2;
	return 0;
}

5. C++11 override and final

It can be seen from the above that C++the requirements for function rewriting are relatively strict, but in some cases, due to negligence, the alphabetical order of the function name may not constitute overloading, and this error will not be reported during compilation. It is not worth the loss only if the expected result is not obtained when the program is running debug, therefore: and two keywords C++11are provided to help users detect whether to rewrite.overridefinal

  1. final: Modifies the virtual function, indicating that the virtual function can no longer be rewritten
class Car
{
    
    
public:
	virtual void Drive() final {
    
    }
};
class Benz :public Car
{
    
    
public:
	virtual void Drive() {
    
    cout << "Benz-舒适" << endl;}
};
错误(活动)	E1850	无法重写“final”函数 "Car::Drive" (已声明 所在行数:4)	
  1. override: Check whether the virtual function of the derived class overrides a virtual function of the base class, and if not, compile and report an error
class Car {
    
    
public:
	virtual void Drive() {
    
    }
};
class Benz :public Car {
    
    
public:
	virtual void test() override {
    
     cout << "Benz-舒适" << endl; }
};
错误	C3668	“Benz::test”: 包含重写说明符“override”的方法没有重写任何基类方法
错误(活动)	E1455	使用“override”声明的成员函数不能重写基类成员

6. Comparison of overloading, overriding (rewriting), hiding (redefinition)

insert image description here

It should be noted that even if the subclass virtual function does not add virtual, it still constitutes rewriting, and the covariant return value of rewriting can be different, but it must be a pointer or reference of parent-child relationship

abstract class

1. Concept

Write it after the virtual function =0, then this function is a pure virtual function. Classes containing pure virtual functions are called abstract classes (also called interface classes), and abstract classes cannot instantiate objects . Objects cannot be instantiated after the derived class inherits. Only by rewriting the pure virtual function can the derived class instantiate the object. The pure virtual function specifies that the derived class must be rewritten, and the pure virtual function also reflects the interface inheritance .

class Car
{
    
    
public:
	virtual void Drive() = 0;
};
class Benz :public Car
{
    
    
public:
	virtual void Drive()
	{
    
    
		cout << "Benz-舒适" << endl;
	}
};
class BMW :public Car
{
    
    
public:
	virtual void Drive()
	{
    
    
		cout << "BMW-操控" << endl;
	}
};
void Test()
{
    
    
	Car* pBenz = new Benz;
	pBenz->Drive();
	Car* pBMW = new BMW;
	pBMW->Drive();
}

2. Interface inheritance and implementation inheritance

The inheritance of ordinary functions is a kind of implementation inheritance. The derived class inherits the functions of the base class, can use the function, and inherits the implementation of the function. The inheritance of virtual functions is a kind of interface inheritance. The derived class inherits the interface of the virtual function of the base class. The purpose is to rewrite and achieve polymorphism, and what is inherited is the interface. So if you don't implement polymorphism, don't define the function as a virtual function

In Object-Oriented Programming, Interface Inheritance and Implementation Inheritance are two different ways of inheritance used to share functionality and behavior between classes . Let's understand these two inheritance styles in more detail:

  1. Interface Inheritance : Interface Inheritance is a style of inheritance in which one class (called subclass or derived class) inherits method declarations from another class (called parent or base class) without inheriting the actual implementation. This type of inheritance is used to define a standard interface for a set of methods without concern for the specific implementation. In C++, interface inheritance is usually implemented by defining pure virtual functions (Pure Virtual Function).

Example:

class Shape {
    
    
public:
    virtual void draw() = 0; // 纯虚函数,定义接口
};

class Circle : public Shape {
    
    
public:
    void draw() override {
    
    
        // 实现具体的绘制圆的代码
    }
};

class Square : public Shape {
    
    
public:
    void draw() override {
    
    
        // 实现具体的绘制正方形的代码
    }
};

In the above example, Shapethe class defines a pure virtual function , which is implemented drawas an interface inherited by the inherited classes Circleand .Square

  1. Implementation Inheritance : Implementation Inheritance is a style of inheritance in which a class inherits both the declaration and the actual implementation of a method from another class. This type of inheritance is used to share existing code and implementation. In C++, inheritance is implemented through the normal inheritance mechanism.

Example:

class Vehicle {
    
    
public:
    void startEngine() {
    
    
        // 启动引擎的代码
    }

    void stopEngine() {
    
    
        // 关闭引擎的代码
    }
};

class Car : public Vehicle {
    
    
public:
    void drive() {
    
    
        // 具体的驾驶代码
    }
};

In the example above, the class implements inheritance Carfrom the class, which inherits the implementations of and .VehiclestartEnginestopEngine

It should be noted that single inheritance in C++ restricts a class to inherit from only one parent class, which helps to avoid the complexity and ambiguity that multiple inheritance may bring.

Summarize:

  • Interface inheritance is used to define a standard interface for methods without concern for the actual implementation. Interface inheritance is implemented through pure virtual functions.
  • Implementation inheritance is used to share existing code and implementation, and subclasses inherit the methods and implementations of the parent class.
  • In actual programming, you should choose the appropriate inheritance method according to your needs to avoid the inheritance relationship being too complicated, so as to maintain the readability and maintainability of the code.

The principle of polymorphism

1. Virtual function table

What is sizeof(Base)?

class Base
{
    
    
public:
	virtual void Func1()
	{
    
    
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};  

Through observation and testing, we found that bthe object is 8bytes, in addition to _bthe members, one more __vfptris placed in front of the object (note that some platforms may put it at the end of the object, this is related to the platform), and this pointer in the object is called the virtual function table pointer ( v stands for virtual, f stands for function ). A class containing virtual functions has at least one virtual function table pointer, because the address of the virtual function must be placed in the virtual function table, and the virtual function table is also referred to as the virtual table, so what is placed in this table in the derived class ? Let's go on to analyze

insert image description here

For the above code, we make the following modifications

1. We add a derived class Derive to inherit from Base
2. Rewrite Func1 in Derive
3. Add a virtual function Func2 and a normal function Func3 to Base

class Base
{
    
    
public:
	virtual void Func1()
	{
    
    
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
    
    
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
    
    
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};
class Derive : public Base
{
    
    
public:
	virtual void Func1()
	{
    
    
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};
int main()
{
    
    
	Base b;
	Derive d;
	return 0;
}

Through observation and testing, we found the following problems:

  1. There is also a virtual table pointer in the derived class object d. The d object is composed of two parts**, one part is a member inherited from the parent class, and the virtual table pointer is the existing part, and the other part is its own member**.
  2. The virtual table of the base class b object and the derived class d object are different. Here we find that Func1 has been rewritten, so the rewritten Derive::Func1 is stored in the virtual table of d, so the rewriting of the virtual function is also called For coverage, coverage refers to the coverage of virtual functions in the virtual table . Rewriting is called syntax, and coverage is called principle layer.
  3. In addition, Func2 is inherited as a virtual function, so it is put into the virtual table, and Func3 is also inherited, but it is not a virtual function, so it will not be put into the virtual table .
  4. The essence of the virtual function table is an array of pointers that store virtual function pointers . Generally, a nullptr is placed at the end of this array.
  5. Summarize the virtual table generation of the derived class : a. First copy the content of the virtual table in the base class to the virtual table of the derived class b. If the derived class rewrites a virtual function in the base class, use the derived class’s own The virtual function overrides the virtual function of the base class in the virtual table c. The newly added virtual function of the derived class is added to the end of the derived class virtual table according to the order of declaration in the derived class.
  6. There is another confusing question here: where does the virtual function exist? Where does the virtual table exist? Note that the virtual table stores virtual function pointers, not virtual functions. Virtual functions are the same as ordinary functions, and they all exist in the code segment, but their pointers are stored in the virtual table . In addition, what is stored in the object is not a virtual table, but a virtual table pointer . So where does the virtual table exist? In fact, if we verify it, we will find that there is a code segment under vs

insert image description here

2. The principle of polymorphism

So much has been analyzed above, so what is the principle of polymorphism? Remember that Functhe function is passed Personand called here Person::BuyTicket, and the passed Studentand called isStudent::BuyTicket

insert image description here

class Person {
    
    
public:
	virtual void BuyTicket() {
    
     cout << "买票-全价" << endl; }
};
class Student : public Person {
    
    
public:
	virtual void BuyTicket() {
    
     cout << "买票-半价" << endl; }
};
void Func(Person& p)
{
    
    
	p.BuyTicket();
}
int main()
{
    
    
	Person Mike;
	Func(Mike);
	Student Johnson;
	Func(Johnson);
	return 0;
}
  1. Observing the red arrow in the figure below, we can see pthat when it points to mikethe object, the virtual function is found in the virtual p->BuyTickettable .mikePerson::BuyTicket
  2. Observing the blue arrow in the figure below, we can see pthat when it points to johnsonthe object, the virtual function is found in the virtual p->BuyTickettable .johsonStudent::BuyTicket
  3. In this way, when different objects perform the same behavior, they show different forms.
  4. Conversely, if we want to achieve polymorphism, there are two conditions, one is virtual function coverage, and the other is the pointer or reference of the object to call the virtual function.
  5. Through the analysis of the following assembly code, it can be seen that the function call after satisfying the polymorphism is not determined at compile time, but is found in the object after it is run. Confirmed at compile time when calling a function that does not satisfy polymorphism

insert image description here

void Func(Person& p)
{
    
    
    // 将参数 p 存储到栈上
    // rcx 寄存器包含了传递的参数 p 的引用
    mov qword ptr [rsp+8],rcx  

    push rbp        // 保存调用函数之前的 rbp 寄存器状态
    push rdi        // 保存调用函数之前的 rdi 寄存器状态
    sub rsp,0E8h    // 在栈上为局部变量分配空间,0xE8 字节的空间

    lea rbp,[rsp+20h]    // 计算 rbp 的值,指向当前栈帧的基址
    lea rcx,[__B666B148_test@cpp (07FF629704067h)]  
    call __CheckForDebuggerJustMyCode (07FF6296F1410h)  
    // 调用某个函数(可能是与调试器相关的),用于检查是否只调试自己的代码

    p.BuyTicket();    // 调用对象 p 的 BuyTicket() 方法
    mov rax,qword ptr [p]    // 将对象 p 的地址存储到 rax 寄存器
    mov rax,qword ptr [rax]  // 将 p 对象的 vtable(虚函数表)的地址加载到 rax
    mov rcx,qword ptr [p]    // 将对象 p 的地址存储到 rcx 寄存器
    call qword ptr [rax]     // 通过虚函数表调用 p.BuyTicket() 方法

    // 清理栈上的局部变量分配
    add rsp, 0E8h

    pop rdi         // 恢复调用函数之前的 rdi 寄存器状态
    pop rbp         // 恢复调用函数之前的 rbp 寄存器状态
}

3. Dynamic binding and static binding

Dynamic binding ( Dynamic Binding ) and static binding ( Static Binding ) are two important concepts related to polymorphism, which are used to describe how to decide which function or method to call at compile time and runtime.

  1. Static Binding (Static Binding) : Static binding is the process of determining which function or method to call at compile time (compile phase). In static binding, the compiler determines which function to call based on information in the call expression, which usually happens when the compiler generates object code. Static binding applies to non-virtual functions, ordinary function overloading, and operator overloading.

Example:

class Base {
    
    
public:
    void print() {
    
    
        cout << "Base class" << endl;
    }
};

class Derived : public Base {
    
    
public:
    void print() {
    
    
        cout << "Derived class" << endl;
    }
};

int main() {
    
    
    Derived d;
    Base& b = d;

    b.print(); // 静态绑定,编译时确定调用 Base::print()
}

In the above example, although brefers to Derivedthe object of the class, because printthe function is not a virtual function, the call has been determined at compile time Base::print().

  1. Dynamic Binding (Dynamic Binding) : Dynamic binding is the process of deciding which function or method to call at runtime (running phase) based on the actual type of the object. This usually involves the use of virtual functions, where functions declared in base classes are marked as virtualand overridden in derived classes. The runtime calls the appropriate function based on the actual type of the object (not just the type of a reference or pointer).

Example:

class Shape {
    
    
public:
    virtual void draw() {
    
    
        cout << "Drawing a shape." << endl;
    }
};

class Circle : public Shape {
    
    
public:
    void draw() override {
    
    
        cout << "Drawing a circle." << endl;
    }
};

int main() {
    
    
    Circle c;
    Shape& s = c;

    s.draw(); // 动态绑定,运行时根据实际对象类型调用 Circle::draw()
}

In the above example, since drawthe function is declared as a virtual function, the call will be called s.draw()according to the actual object type .CircleCircle::draw()

Summary :

  • Static binding is the way to determine function calls at compile time, mainly applicable to non-virtual functions and function overloading.
  • Dynamic binding is a way to determine the function call according to the actual type of the object at runtime, and is mainly applicable to the realization of virtual functions and polymorphism.
  • Dynamic binding makes polymorphism possible, making code more flexible and extensible.

Virtual function tables for single and multiple inheritance relationships

1. Virtual function table in single inheritance

class Base {
    
    
public:
	virtual void func1() {
    
     cout << "Base::func1" << endl; }
	virtual void func2() {
    
     cout << "Base::func2" << endl; }
private:
	int a;
};
class Derive :public Base {
    
    
public:
	virtual void func1() {
    
     cout << "Derive::func1" << endl; }
	virtual void func3() {
    
     cout << "Derive::func3" << endl; }
	virtual void func4() {
    
     cout << "Derive::func4" << endl; }
private:
	int b;
};

Looking at the monitoring window in the figure below, we find that we cannot see func3and func4. Here is that the compiler's monitoring window deliberately hides these two functions, and it can also be considered a small one of him bug. So how do we view dthe virtual table? Below we use the code to print out the functions in the virtual table

insert image description here

typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
    
    
	// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
    
    
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
		VFPTR f = vTable[i];
		f();
	}
	cout << endl;
}
int main()
{
    
    
	Base b;
	Derive d;
	VFPTR* vTableb = (VFPTR*)(*(int*)&b);
	PrintVTable(vTableb);
	VFPTR* vTabled = (VFPTR*)(*(int*)&d);
	PrintVTable(vTabled);
	return 0;
}

Ideab : Take out dthe head of the object 4bytes, which is the pointer of the virtual table. We said earlier that the virtual function table is essentially an array of pointers storing virtual function pointers. At the end of this array is anullptr

1. Take bthe address first, and force it into a int*pointer
2. Then dereference the value, and get the value of bthe object header 4bytes, which is the pointer to the virtual table
3. Then force it into VFPTR*, because the virtual table is a storage VFPTRtype (virtual function pointer type) array.
4. The virtual table pointer is passed to PrintVTableprint the virtual table.
5. It should be noted that the code for printing the virtual table often crashes, because the compiler sometimes does not clean the virtual table, and the virtual table is not placed at the end, resulting in an out of bounds nullptr. It's a compiler problem. We just need to click - Generate - Clean Solution in the directory bar, and then compile it.

insert image description here

2. Virtual function table in multiple inheritance

class Base1 {
    
    
public:
	virtual void func1() {
    
     cout << "Base1::func1" << endl; }
	virtual void func2() {
    
     cout << "Base1::func2" << endl; }
private:
	int b1;
};
class Base2 {
    
    
public:
	virtual void func1() {
    
     cout << "Base2::func1" << endl; }
	virtual void func2() {
    
     cout << "Base2::func2" << endl; }
private:
	int b2;
};
class Derive : public Base1, public Base2 {
    
    
public:
	virtual void func1() {
    
     cout << "Derive::func1" << endl; }
	virtual void func3() {
    
     cout << "Derive::func3" << endl; }
private:
	int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
    
    
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
    
    
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
		VFPTR f = vTable[i];
		f();
	}
	cout << endl;
}
int main()
{
    
    
	Derive d;
	VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
	PrintVTable(vTableb1);
	VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
	PrintVTable(vTableb2);
	return 0;
}

Non-overridden virtual functions of multiple inheritance derived classes are placed in the virtual function table of the first inherited base class part

insert image description here

Inheritance and polymorphism common interview questions

1. Concept review

Which of the following object-oriented approaches will make you rich A

A: Inheritance B: Encapsulation C: Polymorphism D: Abstraction

B is a mechanism in object-oriented programming languages. This mechanism realizes that the definition of the method has nothing to do with the specific object, but the call to the method can be associated with the specific object.
A: Inheritance B: Template C: Object's own reference D: Dynamic binding

Dynamic binding (Dynamic Binding) is a mechanism in object-oriented programming, also known as runtime polymorphism. It allows method calls to be determined at runtime based on the actual type of the object, rather than at compile time. This allows you to use different types of objects under a unified interface, and decide which method to call based on the actual type of the object. Dynamic binding realizes the association of method calls with specific objects, making polymorphism possible.

Inheritance and composition in object-oriented design, which of the following statements is wrong? C

A: Inheritance allows us to override and rewrite the implementation details of the parent class. The implementation of the parent class is visible to the subclasses, which is a kind of static reuse, also known as white box reuse B: The combined
objects do not need to care about their respective implementations The details and the relationship between them are determined at runtime, which is a kind of dynamic reuse, also known as black box reuse
C: Prioritize the use of inheritance instead of composition, which is the second principle of object-oriented design
D: Inheritance can Enable subclasses to automatically inherit the interface of the parent class, but in the design mode, this is considered to be a performance that breaks the encapsulation of the parent class

This statement is wrong. In object-oriented design, composition is generally recommended over inheritance. This is called the principle of "composition over inheritance", which refers to the fact that in design, it is more inclined to build new classes through composition (association between objects), rather than to obtain the functions of existing classes through inheritance. This is because inheritance can introduce unnecessarily tight coupling, break encapsulation, and lead to brittle inheritance chains. The correct approach is to choose inheritance or composition according to the specific situation. Inheritance is appropriate in certain situations, such as when the subclass is a special case of the parent class and the subclass does not require modifications to the behavior of the parent class. And in other cases, composition can better achieve code flexibility, maintainability, and loose coupling.

Which of the following statements about pure virtual functions is correct ?

A: A class that declares a pure virtual function cannot instantiate an object

B: The class that declares the pure virtual function is a virtual base class

C: The subclass must implement the pure virtual function of the base class

D: A pure virtual function must be an empty function

The correct description of the virtual function is B

A: The virtual function of the derived class has a different number and type of parameters than the virtual function of the base class

B: Inline functions cannot be virtual functions

C: The derived class must redefine the virtual function of the base class

D: The virtual function can be a static function

The correct statement about virtual tables is D

A: A class can only have one virtual table
B: There are virtual functions in the base class. If the virtual function of the base class is not rewritten in the subclass, the subclass and the base class share the same virtual table at this time. C: The virtual table is
in D dynamically generated during runtime
: Different objects of a class share the virtual table of the class

A virtual table is a table that stores virtual function pointers, and each class (including base classes and derived classes) may have a corresponding virtual table. Here's a discussion of the other options: A: A class can have only one vtable. This statement is incorrect. Each class (both base and derived) may have a virtual table to support polymorphism. B: There is a virtual function in the base class. If the virtual function of the base class is not rewritten in the subclass, the subclass and the base class share the same virtual table. This statement is incorrect. Each class has its own virtual table, even if the subclass does not override the virtual function, it will have its own virtual table. C: The virtual table is generated during compilation, not dynamically at runtime. The virtual table of each class is created at compile time, which stores pointers to the virtual functions of the class.

Suppose there is a virtual function in class A, B inherits from A, B rewrites the virtual function in A, and does not define any virtual function, then D

A: The first 4 bytes of the class A object store the address of the virtual table, and the first 4 bytes of the class B object are not virtual table addresses B: The first 4 bytes of the class A object
and the class B object are stored in the virtual base table Address
C: The addresses of the virtual tables stored in the first 4 bytes of class A objects and class B objects are the same
D: The number of virtual functions in class A and class B virtual tables is the same, but class A and class B do not use the same virtual table

What is the output of the following program? A

#include<iostream>
using namespace std;
class A {
    
    
public:
	A(char* s) {
    
     cout << s << endl; }
	~A() {
    
    }
};
class B :virtual public A
{
    
    
public:
	B(char* s1, char* s2) :A(s1) {
    
     cout << s2 << endl; }
};
class C :virtual public A
{
    
    
public:
	C(char* s1, char* s2) :A(s1) {
    
     cout << s2 << endl; }
};
class D :public B, public C
{
    
    
public:
	D(char* s1, char* s2, char* s3, char* s4) :B(s1, s2), C(s1, s3), A(s1)
	{
    
    
		cout << s4 << endl;
	}
};
int main() {
    
    
	D* p = new D("class A", "class B", "class C", "class D");
	delete p;
	return 0;
}

A:class A class B class C class D B:class D class B class C class A
C:class D class C class B class A D:class A class C class B class D

Pointer offset problem in multiple inheritance? The correct statement below is C

class Base1 {
    
     public: int _b1; };
class Base2 {
    
     public: int _b2; };
class Derive : public Base1, public Base2 {
    
     public: int _d; };
int main() {
    
    
	Derive d;
	Base1* p1 = &d;
	Base2* p2 = &d;
	Derive* p3 = &d;
	return 0;
}

A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3

In multiple inheritance, each base class has its own member variables and virtual table pointers (if there are virtual functions). An object of a derived class contains the member variables of each base class, in the order in which they appear in the derived class's declaration. The virtual table pointer is at the beginning of the derived class object. In the given code, Derivethe class inherits from two base classes Base1and Base2, in order of inheritance, its memory layout is _b1, _b2, _d. The pointers p1and p3both point to dthe beginning of the object, so their values ​​are equal. The pointer points to the location of the member in p2the object , so its value is different from and . So, the correct description is C: p1 == p3 != p2.d_b2p1p3

What is the output of the following program B

class A
{
    
    
public:
	virtual void func(int val = 1) {
    
     std::cout << "A->" << val << std::endl; }
	virtual void test() {
    
     func(); }
};
class B : public A
{
    
    
public:
	void func(int val = 0) {
    
     std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
    
    
	B* p = new B;
	p->test();
	return 0;
}

A: A->0 B: B->1 C: A->1 D: B->0 E: Compilation error F: None of the above is correct

First of all, the object of the derived class is opened up here, which is mainly related to the function. When newwe call testthe function, because this function has not been rewritten, the function of the base class we call normally passes in testthe pointer of the In fact, it is a function rewritten in , virtual function rewriting is interface inheritance, so the value passed in here is actually the default value of the base class function , so this question is selectedAthisBfuncvalfunc1B

2. Quiz

What is polymorphism ? Answer: Refer to the polymorphic concepts and definitions section above

What is overloading, rewriting (covering), redefinition (hiding) ? Answer: Refer to the comparison section of overloading, overriding (overwriting), and hiding (redefinition) above

The realization principle of polymorphism ? Answer: Refer to the section on the realization principle of polymorphism above.

Can an inline function be a virtual function ? Answer: Yes, but the compiler ignores inlinethe attribute, and this function is no longer inline, because the virtual function must be placed in the virtual table.

Can a static member be a virtual function ? Answer: No, because static member functions have no thispointers, and the method of calling type ::member functions cannot access the virtual function table, so static member functions cannot be placed in the virtual function table.

Can a constructor be virtual ? Answer: No, because the virtual function table pointer in the object is initialized at the constructor initialization list stage.

Can copy construction and operator== be virtual functions ? Answer: Copy construction is not possible, copy construction is also a constructor. operator==Possible but of no practical value.

Can a destructor be virtual? Under what circumstances is the destructor a virtual function ? Answer: Yes, and it is best to define the destructor of the base class as a virtual function. Refer to the two exceptions subsection of virtual function overriding

Is object access to ordinary functions faster or virtual functions faster ? Answer: First of all, if it is an ordinary object, it is as fast. If it is a pointer object or a reference object, the ordinary function called is fast, because it constitutes polymorphism, and calling a virtual function at runtime needs to look up in the virtual function table.

At what stage is the virtual function table generated and where does it exist ? Answer: The virtual function table is generated during the compilation stage, and generally exists in the code segment (constant area).

Problems with C++ diamond inheritance? The principle of virtual inheritance ? A: Refer to the Inheritance Blog. Be careful not to confuse the virtual function table with the virtual base table here

What is an abstract class? The role of abstract classes ? Answer: Refer to the abstract class section. The abstract class forcibly rewrites the virtual function, and the abstract class reflects the interface inheritance relationship.

Guess you like

Origin blog.csdn.net/kingxzq/article/details/132623546
Recommended