c + + knowledge points (constantly updated)

Posted by mpharo on Tue, 21 Sep 2021 09:44:36 +0200

const

class A {
private:
	const int a;//Constant object members can be initialized using the initialization list or in class
public:
	//Constructor
	A() :a(0) {};
	A(int x) :a(x) {};//Initialization list

	//const can be used to distinguish overloaded functions
	int getValue();//Ordinary member function
	int getValue() const;//A constant member function must not modify the value of any data member in the class
};

void function()
{
	//object
	A b;//Ordinary object, you can call all member functions
	const A a;//Constant object. Only constant member functions can be called
	const A *p = &a;//Pointer variable, pointing to constant object
	const A &q = a;//References to constant objects

	//Pointer
	char greeting[] = "Hello";
	char *p1 = greeting;//Pointer variable, pointing to character array variable 			 Note: the pointer to the array does not need to add &, it is the address itself
	const char* p2 = greeting;//Pointer variable, pointing to character array constant 		 Note: const is followed by char, indicating that char cannot be changed
	char* const p3 = greeting;//Pointer constant, pointing to character array variable 		 Note: const is followed by p3 pointer, indicating that the pointer cannot be changed
	const char* const p4 = greeting;//Pointer constant, pointing to character array constant
}

//function
void function1(const int Var);//The parameters passed are immutable within the function
void function2(const char* Var);//What the parameter pointer refers to is a constant
void function3(char* const Var);//The parameter pointer is a constant
void function4(const int& Var);//Reference parameters are constants within a function

//Function return value
const int function5();//Returns a constant
const int* function6();//Returns a pointer variable that points to a constant. Use const int* p =function6();
int* const function7();//Returns a constant needle that refers to a vector. Use int* const p =function7();

Without const &a, a reference is only an alias of an object, not an object, and cannot be modified with const

static

  1. Modify the ordinary variable, modify the storage area and life cycle of the variable, so that the variable is stored in the static area, and allocate space before the main function runs. If there is an initial value, initialize it with the initial value. If there is no initial value, the system initializes it with the default value.

  2. Modifies an ordinary function to indicate the scope of the function. It can only be used in the file that defines the function. When developing a project with multiple people, in order to prevent duplicate names with functions in other people's namespaces, the function can be positioned as static.

  3. Modify the member variable, modify the member variable so that all objects only save one variable, and you can access the member without generating an object.

  4. Modify the member function so that it can be accessed without generating an object, but non static members cannot be accessed in the static function.

class Point  
{  
public:   
    void init()  
    {    
    }  
    static void output()  
    {  
    }  
};  
void main()  
{  
    Point::init();  //error
    Point::output();  //Succeeded, a member function that can be accessed without generating an object
}
class Point  
{  
public:   
    void init()  
    {    
    }  
    static void output()  
    {  
        printf("%d\n", m_x);  //Error, non static members cannot be accessed within static functions.
        //Because the static member function belongs to the whole class, the space has been allocated before the class instantiates the object, and the non static members of the class must have memory space after the class instantiates the object. Therefore, this call makes an error, just like using it in advance without declaring a variable. Conversely, non static member functions of a class can be called with static member functions.
    }  
private:  
    int m_x;  
};  
void main()  
{  
    Point pt;  
    pt.output();  
}

this pointer

this pointer is a special pointer implied in each non static member function, pointing to the object that calls the member function.
It is often necessary to explicitly use this pointer: 1. Implement the chain reference of the object; 2. Avoid assignment to the same object; 3. Implement some data structures, such as list.

Inline inline function

Without entering the function, directly execute the function body to improve efficiency
Functions defined within a class, except virtual functions, are all inline functions by default
It is equivalent to copying the inline function body to the call point of the inline function, which consumes memory space

class A {
	int doA() { return 0; }//Class definition, implicit inlining
};

class B {
	int doB();
};
inline int B::doB() { return 0; }//Class definition, explicit inlining

Inlining of virtual functions
Virtual functions can be inline functions, but they cannot be inline when virtual functions are polymorphic. Because polymorphism is executed at run time and introversion is executed at compile time, the compiler does not know which code to call at run time.
The only time inline virtual works is when the compiler knows which class the called object is, which only happens when the compiler has an actual object rather than a reference or pointer to the object

class Base {
public:
	inline virtual void who()
	{
		cout << "I am Base\n";
	}
	virtual ~Base() {}
};

class Derived:public Base{
public:
	inline void who()//Implicit inlining without writing inline
	{
		cout << "I am Dervied\n";
	}
};
int main() {
	//The virtual function here is called through the specific object b of the Base class, and the compilation time can be determined, so it is inline
	Base b;
	b.who();

	//The virtual functions here are called through pointers and are polymorphic. They can only be determined during operation, so they cannot be inlined
	Base *ptr = new Derived();
	ptr->who();

	//Because Base has virtual destructor, delete calls the destructor of derived class derived first, then calls the destructor of base class Base to prevent memory leak.
	delete ptr;
	ptr = nullptr;

	system("pause");
	return 0;
}

volatile

volatile is a type modifier. Its declared type indicates that it can be modified by some unknown factors (operating system, hardware, other threads). Such objects should not be optimized at compile time.
The variable declared by volatile keyword must be taken from memory every time
const can be volatile
The pointer can be volatile

volatile int i = 0;
volatile char* vpch;
char* volatile pchv;

assert()

Is a macro, not a function.
The function is to terminate the program execution if the condition returns an error

#define NDEBUG //Add this line before #include < cassert > to disable assert
#include <cassert>
assert(p != NULL);//Error returned when p is null

sizeof()

Use the array to get the space occupied by the whole array
Use the pointer to get the space occupied by the pointer itself

#pragma pack(n)

Set structs, unions, and class member variables to n-byte alignment

#pragma pack(push) / / save its status
#pragma pack(4) / / set to 4-byte alignment

struct test {
	char m1;//4 1 + 3 alignment
	double m4;//Multiples of 8 and 4 do not need to be changed 
	int m3;//4
	//4 + 8 + 4 = 16 bytes
};

#pragma pack(pop);// Restore alignment

Bit domain

Class defines a bit field for non static member functions. How many binary bits does it occupy, which can save some memory
Pointers and fetch addresses cannot be applied to bit fields

Bit mode 2;//mode takes two places

extern C

The variable modified by extern is of type extern
The variable modified by extern "C" indicates that the current code is processed in C language
Prevent c + + code modification and c language mismatch

#ifdef __cplusplus
extern "C"{
#endif
void *meset(void*,int,size_t);
#ifdef __cplusplus
}
#endif

Struct and typedef struct

c medium

typedef struct Student{
	int age;
} S;
//Equivalent to
struct Student{
	int age;
};
typedef struct Student S;

void Student(){}//Does not conflict with struct

In c + +

struct Student{
	int age;
};
void f1(Student me);//struct can be omitted
void f2(struct Student me);
typedef struct Student{
	int age;
}S;
void Student(){}//Correct. After that, Student only represents this function, not the structure
void S(){}//Error S is an alias for struct Student
int main(){
	Student();//Call function
	struct Student me;
	//or S me;
	return 0;
}

struct & class

struct is suitable to be regarded as the implementation of data structure, and class is suitable to be regarded as the implementation of object
Default access permissions: class is private and struct is public

union Union

A special class that saves space. It can have many data structures, but only one data member has a value at the same time, and other data members become undefined.
The default access permission is public
Can contain constructors and destructors
Cannot contain members of reference type
Cannot inherit from other classes and cannot be used as a base class
Cannot contain virtual functions
Anonymous unions can directly access union members in the scope where they are defined
Anonymous union cannot contain protect or private members
The global anonymous union must be static

//Consortium
union UnionTest
{
	UnionTest() :i(10) {};
	int i;
	double d;
};

//Global static anonymous union
static union {
	int i;
	double d;
};

int main()
{
	UnionTest u;

	//Local anonymous union
	union {
		int i;
		double d;
	};

	cout << u.i << endl;//Output UnionTest consortium 10

	::i = 20;
	cout << ::i << endl;//Output global static anonymous consortium 20

	i = 30;
	cout << i << endl;//Output local anonymous consortium 30
	return 0;
}

c + + object-oriented features

Encapsulation: encapsulates properties and methods into a structure using function pointers
Inheritance: structure nesting
Polymorphism: the function pointers of parent and child classes are different

Express keyword

explicit modifies the constructor to prevent implicit conversion and copy initialization
When explicit is used to modify the conversion function, it can prevent implicit conversion, except by context

In the following context, the type bool is expected, and if bool t(e) is declared; A good structure performs implicit conversion (that is, consider an implicit conversion function such as explicit T::operator bool() const. This expression e is called bool in context.
Control expressions of if, while and for;
Built in logical operators&& Operands of and 𞓜;
Conditional operator?: The first operand of the;
static_ Predicate in assert declaration;
Expression in noexcept specifier;

struct A {
	A(int) {}
	operator bool()const { return true; }
};
struct B {
	explicit B(int) {}
	explicit operator bool()const { return true; }
};
void doA(A a) {}
void doB(B b) {}

int main() {
	A a1(1);//Direct initialization
	A a2 = 1;//Replication initialization
	A a3{ 1 };//Direct list initialization
	A a4 = { 1 };//Copy list initialization
	A a5 = (A)1;//static_cast display conversion
	doA(1);//Implicit conversion from int to A
	if (a1);//Implicit conversion from A to bool of conversion function A::operator bool()
	bool a6(a1);//Implicit conversion from A to bool of conversion function A::operator bool()
	bool a7 = a1;//Implicit conversion from A to bool of conversion function A::operator bool()
	bool a8 = static_cast<bool>(a1);//static_cast is initialized directly

	B b1(1);
	B b2 = 1;//Error explicit cannot copy initialization
	B b3{ 1 };
	B b4 = { 1 };//Error explicit cannot copy list initialization
	B b5 = (B)1;
	doB(1);//Error explicit cannot convert int to B implicitly
	if (b1);//Conversion by context
	bool b6(b1);//Conversion by context
	bool b7 = b1;//Error explicit cannot be implicitly converted
	bool b8 = static_cast<bool>(b1);//static_cast is initialized directly
	return 0;
}

Friend friend classes and friend functions

Access to private members
Destroy encapsulation
Friend relationship is not transitive
Friendship is one-way
The form and number of friend declarations are not limited

class CCar
{
private:
    int price;
    friend class CDriver;  //Declare CDriver as a friend class
};
class CDriver
{
public:
    CCar myCar;
    void ModifyCar()  //Refitted car
    {
        myCar.price += 1000;  //Since CDriver is a friend class of CCar, its private members can be accessed here
    }
};
class CCar;  //Declare the CCar class in advance for later CDriver classes to use
class CDriver
{
public:
    void ModifyCar(CCar* pCar);  //Refitted car
};
class CCar
{
private:
    int price;
    friend int MostExpensiveCar(CCar cars[], int total);  //Declare friends
    friend void CDriver::ModifyCar(CCar* pCar);  //Declare friends
};
void CDriver::ModifyCar(CCar* pCar)
{
    pCar->price += 1000;  //Value increase after vehicle modification
}
int MostExpensiveCar(CCar cars[], int total)  //Looking for the price of the most expensive car
{
    int tmpMax = -1;
    for (int i = 0; i<total; ++i)
        if (cars[i].price > tmpMax)
            tmpMax = cars[i].price;
    return tmpMax;
}

using

  1. using statement
    A using declaration can only introduce one member of a namespace at a time
    using namespace_name::name
  2. using declaration of constructor
    The derived class can reuse the constructor defined by its direct base class, and the compiler generates the derived class constructor corresponding to each constructor
class Derived:Base{
public:
	using Base::Base;
}

Use the indication of using as little as possible to pollute the namespace (too many imported names may have duplicate names and be overwritten)

using namespace std;

Multi use declaration

int x;
std::cin>>x;
std::cout<<x<<std::endl;

or

using std::cin;
using std::cout;
using std::endl;
int x;
cin>>x;
cout<<x<<endl;

: range resolution operator

  1. Global scope character:: Name: used before type name (class, class member, member function, variable, etc.), indicating that the scope is a global namespace
  2. Class scope symbol class::name: used to represent a specific class when specifying the scope of the type
  3. Namespace scope character namespace::name: used to represent a namespace when specifying the scope of a type
int count = 11;//overall situation::

class A {
public:
	static int count;//class_name::count
};
int A::count = 21;

void fun() {
	int count = 31;//The count of the initialization part is 31
	count = 32;//Set the local count to 32
}

int main() {
	::count = 12;//Set the global count to 12
	A::count = 22;//Set the count of class A to 22
	fun();//The local count of the function is 32
	return 0;
}

enum enumeration type

  1. Scoped enumeration type
enum class open_modes{input,output,append};
  1. Scopeless enumeration type
enum color{red,yellow,green};
enum{floatPrec=6,double=10};

decltype

Used to check the type and value classification of the declaration type or expression of the entity
decltype(expression)

template <typename It>
auto fcn(It beg, It end)->decltype(*beg) Tail return allows us to declare the return type after the parameter list
{
	//Processing sequence
	return *beg;//Returns a reference to an element in a sequence
}
//In order to use parameter template members, you must use typename
template <typename It>
auto fcn2(It beg,It end)->typename remove_reference<decltype(*beg)>::type
{
	return *beg;//Returns a copy of an element in a sequence
}
#include <string>
using namespace std;
class Student{
public:
    static int total;
    string name;
    int age;
    float scores;
};
int Student::total = 0;
int  main(){
    int n = 0;
    const int &r = n;
    Student stu;
    decltype(n) a = n;  //n is of type int, and a is derived as of type int
    decltype(r) b = n;     //r is const int & type, and b is derived as const int & type
    decltype(Student::total) c = 0;  //total is a member variable of type int of class Student, and c is deduced as type int
    decltype(stu.name) url = "abcdefg";  //total is a string type member variable of class Student, and url is deduced as string type
    return 0;
}

quote

  1. lvalue reference
    General reference, which generally represents the identity of the object
  2. rvalue reference
    An R-value reference is a reference that must be bound to an R-value (a temporary object and an object to be destroyed). It generally represents the value of an object.

Right value reference can realize move semantics and Perfect Forwarding. Its main purpose is two aspects:
Eliminate unnecessary object copies when two objects interact, save computing and storage resources and improve efficiency.
It can define generic functions more concisely and clearly.

  1. Reference fold
    X & &, X & &, X & & & can be folded into x&
    X & & & & folds to X&&

macro

#define xxx
#define Max(x,y) ((x)>(y)?(x):(y))
The macro definition can realize functions similar to functions, but it is not a function after all, and the "parameter" in the bracket in the macro definition is not a real parameter. When the macro is expanded, the "parameter" is replaced one-to-one.

Member initialization list

High efficiency, less one call to the default construction process

In some cases, the initialization list must be used:
Constant members, because constants can only be initialized and cannot be assigned, they must be placed in the initialization list
Reference type. The reference must be initialized at the time of definition and cannot be re assigned, so it should also be written in the initialization list
There is no class type with a default constructor, because using the initialization list you don't have to call the default constructor to initialize

initializer_list initialization

#include <iostream>
#include <vector>
#include <initializer_list>

template<class T>
struct S {
	std::vector<T> v;
	S(std::initializer_list<T> l) :v(l) {
		std::cout << "constructed with a" << l.size() << "-element list";
	}

	void append(std::initializer_list<T>l) {
		v.insert(v.end(), l.begin(), l.end());
	}

	std::pair<const T*, std::size_t>c_arr()const {
		return{ &v[0],v.size() };//c_arr().first  ==&v[0]   c_arr().second  ==v.size()
	}
};

template <typename T>
void templated_fn(T) {}

int main() {
	S<int>s1 = { 1,2,3,4,5 };//Replication initialization
	S<int>s2{ 1,2,3,4,5 };//Direct initialization
	s1.append({ 6,7,8 });//List initialization

	std::cout << "The vector size now is" << s.c_arr().second << "ints:\n";

	for (auto n : s.v) {
		std::cout << n << ' ';
	}
	std::cout << "\n";

	std::cout << "Range-for over brace-init-list: \n";
	
	for (int x : {-1, -2, -3}) {
		std::cout << x << ' ';
	}
	std::cout << '\n';

	auto al = { 10,11,12 };
	std::cout << "The list bound to auto has size() = " << al.size() << '\n';

	//    templated_fn({1, 2, 3}); //  Compilation error! '{1, 2, 3}' is not an expression,
								 // It has no type, so T cannot be deduced
	templated_fn<std::initializer_list<int>>({ 1, 2, 3 }); // OK
	templated_fn<std::vector<int>>({ 1, 2, 3 });           // Also OK
}

object-oriented

encapsulation

Encapsulate objective things into abstract classes. Classes can make their own data public, hidden or public to friends
keyword:
public: can be accessed by any entity
private: can be accessed by this class or friends
protected: can only be accessed by this class and subclasses

inherit

Base class (parent) – > derived class (child)

polymorphic

Based on encapsulation and inheritance
Classification and Implementation

  1. Overload polymorphism: function overload, operator overload
  2. Subtype polymorphism: virtual function
  3. Parameter polymorphism: class template, function template
  4. Forced polymorphism: basic type conversion, custom type conversion

Static polymorphism

function overloading

class A
{
public:
	void do(int a);
	void do(int a,int b);
};

Dynamic polymorphism

Virtual function: virtual modified member function
Dynamic binding: dynamic binding occurs when a virtual function is called using a reference or pointer to a base class

The object of the derived class can be assigned to the pointer or reference of the base class, and vice versa
Ordinary functions (non class member functions) cannot be virtual functions
A static function cannot be a virtual function
The constructor cannot be a virtual function (because the virtual table pointer is not in the memory space of the object when calling the constructor, and the virtual table pointer cannot be formed until the constructor call is completed)
The destructor can be a virtual function to solve the memory leakage problem that the base class pointer points to the subclass and is deleted
Inline functions cannot be virtual functions that represent polymorphism

class Shape
{
public:
	//virtual Shape();  error constructor cannot be a virtual function
	virtual double calcArea() {}
	virtual ~Shape() {};//Virtual functions can be virtual functions
};

class Circle :public Shape
{
public:
	virtual double calcArea() {};
};

class Rect :public Shape
{
public:
	virtual double calcArea() {};
};
int main()
{
	Circle *c1 = new Circle();
	Shape* s1 = c1;//A parent class pointer can point to a child class

	Shape* s2 = new Circle();
	s2->calcArea();

	delete c1;//c1 is the pointer to the subclass. Only its own destructor is called during destructor
	c1 = nullptr;
	delete s1;//s1 is a pointer to the parent class. After destructing the subclass, we call the destructor of the parent class to prevent memory leakage.
	s1 = nullptr;
	delete s2;
	s2 = nullptr;

	Shape* S3 = new Shape();
	//Rect*r1 = S3;  error a subclass pointer cannot point to a parent class
	//Rect*r2 = new Shape();  error
}

Pure virtual function

In the base class, the virtual function cannot be given a meaningful implementation, but it is declared as a pure virtual function, and its implementation is left to the derived class of the base class
virtual int A()=0;
The last "= 0" does not mean that the return value of the function is 0. It only plays a formal role and tells the compilation system that "this is a pure virtual function";

Virtual function, pure virtual function
If a virtual function is declared in a class, the function is implemented, even if it is empty. Its function is to enable the function to be overridden in its subclass. In this way, the compiler can use late binding to achieve polymorphism. A pure virtual function is just an interface, a declaration of a function, which should be left to be implemented in a subclass.
Virtual functions can not be overridden in subclasses; However, pure virtual functions must be implemented in subclasses to instantiate subclasses.
The class of virtual function is used for "implementation inheritance". It inherits the implementation of the parent class while inheriting the interface. Pure virtual functions focus on the unity of the interface, and the implementation is completed by subclasses.
A class with pure virtual functions is called an abstract class. This class cannot directly generate objects, but can only be used after it is inherited and its virtual functions are rewritten. After the abstract class is inherited, the subclass can continue to be an abstract class or an ordinary class.
A virtual base class is a base class in virtual inheritance

Virtual function pointer

Virtual function table: store the virtual function pointer in the read-only data section of the program. If the derived class implements a virtual function of the base class, the virtual function pointer of the original base class will be overwritten in the virtual table and created according to the class declaration during compilation.
Virtual function pointer: refers to a virtual function in an object containing a virtual function class, which is determined at run time.

Virtual inheritance

Virtual inheritance, virtual function
Similarities:

  1. Both use virtual pointers (both occupy the storage space of the class) and virtual tables (both do not occupy the storage space of the class)

Differences:

  1. Virtual inheritance
    The virtual base class still exists in the inherited class and only occupies storage space
    The virtual base class table stores the offset of the virtual base class from the directly inherited class
  2. virtual function
    Virtual functions do not occupy storage space
    The virtual function table stores the virtual function address

Template class, member template, virtual function

Class templates can have virtual functions
The member template function of a class cannot be a virtual function

Abstract class, interface class, aggregation class

Abstract class: a class containing pure virtual functions
Interface class: an abstract class containing only pure virtual functions
Aggregation class: users can directly access its members, and has a special initialization syntax form. Meet the following characteristics:
All members are public
No constructor defined
There is no in class initialization
There are no base classes and no virtual functions

Memory allocation and management

  1. malloc,calloc,realloc,alloca
    malloc: request memory of the specified number of bytes. The initial value in the requested memory is uncertain.
    calloc: allocate memory that can hold the specified number of objects of the specified length. Each bit of the requested memory is initialized to 0.
    realloc: change the previously allocated memory length (increase or decrease). When increasing the length, it may be necessary to move the contents of the previously allocated area to another area large enough, while the initial value in the new area is uncertain.
    alloca: request memory on the stack. When the program is out of the stack, it will automatically release memory.

  2. malloc free allocate free memory

char *str=(char*)malloc(100);
assert(str!=nullptr);//Confirm whether the application is successful
free(str);
str =nullptr;
  1. new delete
    new/new[] first calls malloc to allocate memory and then calls the constructor.
    delete/delete [] call the destructor first and then free the space
int main()
{
	T* t= new T();
	delete t;
	return 0;
}

new can calculate the required space to allocate by itself, and malloc needs to input the number of bytes of the requested space by itself

  1. Locate new
    Pass the address parameter to new to create an object in the specified memory area in advance
new (place_address) type
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] { braced initializer list }
int main(){
	char buffer[512];
	int *p1 = new (buffer) int[10];//location
	int *p2 = new int[10];//routine
}

Define a class that can only generate objects on the heap (stack)

Only on the heap
Method: set the destructor to private

Only on the stack
Method: overload new and delete as private

Smart pointer

c++11 has three kinds of smart pointers

shared_ptr implements the concept of shared ownership. Multiple smart pointers point to the same object, and the object and its related resources will be released when "the last reference is destroyed".

weak_ptr allows you to share but not own an object. Once the last smart pointer that owns the object loses ownership, any weak_ptr will be automatically empty. It can break the ring reference and solve the deadlock problem

unique_ptr implements the concept of exclusive ownership or strict ownership to ensure that only one smart pointer can point to the object at the same time. You can transfer ownership. Once you own a destroyed or programmed empty, or start owning another object, the previously owned object will be destroyed and any corresponding resources will be released. It is particularly useful for avoiding memory leaks, such as forgetting delete after new. unique_ptr is used to replace the old version of auto_ptr

Cast operator

  1. static_cast
    Conversion for non polymorphic types
    Do not perform runtime type checking (conversion security is not as good as dynamic_cast)
    It is usually used to convert numeric data types (such as float - > int)
    The pointer can be moved throughout the class hierarchy. It is safe for a subclass to be converted to a parent class (up conversion), and it is unsafe for a parent class to be converted to a child class (because a child class may have fields or methods that are not in the parent class)
    Up conversion is an implicit conversion.

  2. dynamic_cast
    Conversion for polymorphic types
    Perform line runtime type check
    Applies only to pointers or references
    Conversion of ambiguous pointers will fail (return nullptr) without throwing an exception
    The pointer can be moved throughout the class hierarchy, including up conversion and down conversion

  3. const_cast
    Used to delete const, volatile, and__ unaligned attribute (such as converting const int type to int type)

  4. reinterpret_cast
    Simple reinterpretation for bits
    Abuse reinterpret_ The cast operator can be very risky. One of the other cast operators should be used unless the required conversion itself is low-level.
    It is allowed to convert any pointer to any other pointer type (such as char * to int * or One_class * to Unrelated_class *, but it is not safe in itself)
    It also allows any integer type to be converted to any pointer type and vice versa.
    reinterpret_cast operators cannot lose const, volatile, or__ unaligned attribute.
    reinterpret_ A practical use of cast is in hash functions, that is, mapping values to indexes by making two different values hardly end in the same index.

  5. bad_cast
    Dynamic failed because cast to reference type failed_ Cast operator raises bad_cast exception.
    bad_cast usage

try {  
    Circle& ref_circle = dynamic_cast<Circle&>(ref_shape);   
}  
catch (bad_cast b) {  
    cout << "Caught: " << b.what();  
} 

Runtime type information (RTTI)

typeid,type_info usage

#include <iostream>
using std::cout;

class Flyable {
public:
	virtual void takeoff() = 0;
	virtual void land() = 0;
};

class Bird :public Flyable {
public:
	void foraging() {}
	virtual void takeoff() {}
	virtual void land() {}
	virtual ~Bird() {}
};

class Plane :public Flyable {
public:
	void carry() {}
	virtual void takeoff() {}
	virtual void land() {}
};

class type_info {
public:
	const char* name()const;
	bool operator ==(const type_info & rhs)const;
	bool operator !=(const type_info & rhs)const;
	int before(const type_info & rhs)const;
	virtual ~type_info() {};
};

void doSomething(Flyable *obj) {
	obj->takeoff();
	cout << typeid(*obj).name() << endl;//typeid allows you to determine the type of the object at runtime and output class Bird or class Plane. If you want to obtain the data type of the derived class through the pointer of the base class, the base class must have a virtual function
	if (typeid(*obj)==typeid(Bird))
	{
		Bird *bird = dynamic_cast<Bird*>(obj);//Object conversion
		bird->foraging();
	}

	obj->land();
}

int main() {
	Bird *b = new Bird();
	doSomething(b);
	delete b;
	b = nullptr;
	return 0;
}

Topics: C C++ C#