C++虚函数

本文详细介绍了C++中的虚函数和运行时多态性,强调了虚函数在解决类型动态绑定时的运行时开销。同时,通过CRTP(Curiously Recurring Template Pattern)设计模式展示了如何避免使用虚表和虚指针来提升性能。文章还探讨了基类和继承类指针的问题,解释了如何通过基类指针调用派生类的方法。
摘要由CSDN通过智能技术生成

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值