[C++ learning diary] 8.3 8.4 8.7

8.3 Virtual functions

If you need to point to the object of the derived class through the pointer of the base class, and access a member with the same name as the base class, you need to declare the function with the same name as a virtual function in the base class. Thus achieving polymorphism.

1. General virtual function members

Grammatical form:

virtual function type function name (formal parameter list);

1. Virtual function declarations can only appear in function prototype declarations in class definitions

2. Virtual functions in derived classes hide all overloaded forms of functions with the same name in base classes

EG 8-4

#include <iostream>
using namespace std;
class base1
{
    public:
    virtual void show(){cout<<"base1 is constructing"<<endl;}
};
class base2:public base1
{
    public:
    virtual void show(){cout<<"base2 is constructing"<<endl;} //You can also use virtual in derived classes to remind yourself that this is a virtual function
};
void fun(base1* ptr) //It must be a pointer or a reference to play the role of a virtual function
{
    ptr->show(); //If it is changed to ptr->base2::show(), no matter what the class pointed to by ptr is, the show function of base2 will be called
}

int main()
{
    base1 b1;
    base2 b2;
    fun(&b1);
    fun(&b2);
    return 0;
}

operation result

base1 is constructing
base2 is constructing

2. Virtual destructor

If it is possible to call the object's destructor (via delete) through the base class pointer, you need to make the base class's destructor virtual.

EG 8-5

#include <iostream>
using namespace std;
class base
{
    public:
    virtual ~base(){cout<<"desructing base"<<endl;}
};
class derived:public base
{
    public:
    derived(){p=new int(0);}//Dynamically allocate memory, you must use delete to delete the memory after the end of the call
    virtual ~derived(){cout<<"destructing derived"<<endl;}
    private:
    int *p;
};
void fun(base *ptr)
{
    delete ptr;
}
int main()
{
    base *b=new derived();//Call the default constructor to generate an object of derived class, and assign its address to b
    fun(b);
    return 0;
}

operation result

destructing derived//According to 7.4, the derived class destructor is called first, and then the base class destructor is called
desructing base

8.4 Pure virtual functions and abstract classes

1. Pure virtual function

Statement format:

virtual function type function name (parameter list)=0;

After being declared as a pure virtual function, the implementation part of the function can no longer be given in the base class

Note: A pure virtual function ≠ a virtual function with an empty function body!

2. Abstract class

A class with pure virtual functions is an abstract class. An abstract class cannot be instantiated, that is, an object of an abstract class cannot be defined, only its pointers and references can be defined.

EG 8-6

#include <iostream>
using namespace std;
class base1
{
    public:
    virtual void show() =0;//pure virtual function definition
};
class base2:public base1
{
    public:
    void show(){cout<<"base2 is constructing"<<endl;} 
};
class derived:public base2
{
    public:
    void show(){cout<<"derived is constructing"<<endl;}
};
void fun(base1* ptr) 
{
    ptr->show(); 
}

int main()
{
    base2 b2;
    derived d1;
    fun(&b2);
    fun(&d1);
    return 0;
}

operation result

base2 is constructing
derived is constructing

8.7 Polymorphism

Polymorphic types are class types that have virtual functions. Destructors of polymorphic types are recommended to use virtual functions.

1. Runtime type identification

1.dynamic_cast

dynamic_cast can explicitly convert the base class pointer to the derived class pointer, and check whether the type of the object pointed to by the pointer is compatible with the target type of the conversion before conversion. And the type before conversion must be a pointer to a polymorphic type.

Explanation

#include <iostream>
#include <assert.h>//assert assertion header file
 
using namespace std;
 
// I am the parent
class Tfather
{
public:
	virtual void f() { cout << "father's f()" << endl; }
};
 
// I am a subclass
class Tson : public Tfather
{
public:
	void f() { cout << "son's f()" << endl; }
 
	int data; // I am an exclusive member of the subclass
};
 
int main()
{ 
	Tfather father;
	Tson son;
	son.data = 123;
 
	Tfather *pf;
	Tson *ps;
	//son to father
	/* Uplink conversion: no problem, polymorphism is valid (refer to the type compatibility rules in 7) */
	ps = &son;
	pf = dynamic_cast<Tfather *>(ps);
	pf->f();
 //child to child
	/* Downstream conversion (pf actually points to a subclass object): no problem */
	pf = &son;
	ps = dynamic_cast<Tson *>(pf);
	ps->f();
	cout << ps->data << endl;		// Access to subclass exclusive members works
 //From parent to child, because the subclass adds new members, it will fail and return a null pointer
	/* Downward conversion (pf actually points to the parent class object): contains unsafe operations, and dynamic_cast functions and returns NULL */
	pf = &father;
	ps = dynamic_cast<Tson *>(pf);
	assert(ps != NULL);			 	// Assertion violations prevent the following unsafe operations
	ps->f();
	cout << ps->data << endl;		// Unsafe operation, the object instance has no data member at all
	return 0;
}

operation result

son's f()
son's f()
123
  
Assertion failed: (ps != NULL), function main, file tempCodeRunnerFile.cpp, line 46.
zsh: abort      "/var/folders/45/m9lm0lfj5zj133tbhrqfqb_80000gn/T/"tempCodeRunnerFile

1.assert() function

If the value of the expression in the brackets is 1 (the condition is true), assert does not perform any operation;

If the expression inside the brackets is 0 (the condition is not true), the assert function will print an error message and terminate the program.

2. Explicit conversion from base class to derived class

If the derived class adds new members, the conversion will fail.

2.typeid

typeid is used to obtain information about a type, and get a constant reference of type_info type through typeid

Grammatical form:

typeid(expression) or typeid(type specifier)

EG 8-10

#include <iostream>
#include <typeinfo>
using namespace std;
class base
{
    public:
    virtual ~base(){}
};
class derived:public base
{

};
void fun(base *b)
{
    const type_info &info1=typeid(b);
    const type_info &info2=typeid(*b);
    cout<<"typeid(b):"<<info1.name()<<endl;
    cout<<"typeid(*b):"<<info2.name()<<endl;
    if(info2==typeid(base)) cout<<"This is a base class!"<<endl;
}
int main()
{
    base b;
    fun(&b);
    derived c;
    fun(&c);
    return 0;
}

operation result

typeid(b):P4base
typeid(*b):4base
This is a base class!
typeid(b):P4base
typeid(*b):7derived

Tags: C++ programming language

Posted by joopeon on Fri, 18 Nov 2022 06:17:45 +1030