Virtual Function
参考:Virtual Functions and Runtime Polymorphism in C++ | Set 1 (Introduction) - GeeksforGeeks
Virtual Function: a member function which is declared in the base class using the keyword virtual and is re-defined (overridden) by the derived class
Polymorphism: ability to take many forms. It occurs if there is a hierarchy of classes which are all related to each other by inheritance.In C++ what this means is that if we call a member function then it could cause a different function to be executed instead depending on what type of object invoked it.
Virtual functions are resolved at runtime. It is called according to the type of object instance pointed or referenced, not according to the type of the pointer or reference.
vptr and vtable
When a method is declared virtual in a class, compiler creates a virtual table (aka VTable) and stores addresses of virtual methods in that table. A virtual pointer (aka VPtr) is then created and initialized to point to that VTable. A VTable is shared across all the instances of the class, i.e. compiler creates only one instance of VTable to be shared across all the objects of a class. Each instance of the class has its own version of VPtr.
However, if a virtual routine is called many times, each time its address needs to be resolved by looking through VTable using Vptr. Extra indirection (pointer dereference) for each call to a virtual method makes accessing VTable a costly operation and it is better to avoid it as much as we can.
Curiously Recurring Template Pattern(CRTP)
To avoid Vtable and Vptr, CRTP is a design pattern which a class X derives from a class template instantiation using X itself as template argument.
// Image program (similar to above) to demonstrate
// working of CRTP
#include <chrono>
#include <iostream>
using namespace std;
typedef std::chrono::high_resolution_clock Clock;
// To store dimensions of an image
class Dimension {
public:
Dimension(int _X, int _Y)
{
mX = _X;
mY = _Y;
}
private:
int mX, mY;
};
// Base class for all image types. The template
// parameter T is used to know type of derived
// class pointed by pointer.
template <class T>
class Image {
public:
void Draw()
{
// Dispatch call to exact type
static_cast<T*>(this)->Draw();
}
Dimension GetDimensionInPixels()
{
// Dispatch call to exact type
static_cast<T*>(this)->GetDimensionInPixels();
}
protected:
int dimensionX, dimensionY;
};
// For Tiff Images
class TiffImage : public Image<TiffImage> {
public:
void Draw()
{
// Uncomment this to check method dispatch
// cout << "TiffImage::Draw() called" << endl;
}
Dimension GetDimensionInPixels()
{
return Dimension(dimensionX, dimensionY);
}
};
// There can be more derived classes like PngImage,
// BitmapImage, etc
// Driver code
int main()
{
// An Image type pointer pointing to Tiffimage
Image<TiffImage>* pImage = new TiffImage;
// Store time before virtual function calls
auto then = Clock::now();
// Call Draw 1000 times to make sure performance
// is visible
for (int i = 0; i < 1000; ++i)
pImage->Draw();
// Store time after virtual function calls
auto now = Clock::now();
cout << "Time taken: "
<< std::chrono::duration_cast<std::chrono::nanoseconds>(now - then).count()
<< " nanoseconds" << endl;
return 0;
}
基类和继承类的指针问题
参考: 18.1 — Pointers and references to the base class of derived objects | Learn C++ (learncpp.com)
A derived object contains a base part and a Derived part.
#include <iostream>
int main()
{
Derived derived{ 5 };
// These are both legal!
Base &rBase{ derived };
Base *pBase{ &derived };
std::cout << "derived is a " << derived.getName() << " and has value " << derived.getValue() << '\n';
std::cout << "rBase is a " << rBase.getName() << " and has value " << rBase.getValue() << '\n';
std::cout << "pBase is a " << pBase->getName() << " and has value " << pBase->getValue() << '\n';
return 0;
}
output:
derived is a Derived and has value 5
rBase is a Base and has value 5
pBase is a Base and has value 5
It’s not possible to call Derived::getValueDouble() using rBase or pBase. They are unable to see anything in Derived.