You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// The Curiously Recurring Template Pattern (CRTP)template<classT>classBase{// methods within Base can use template to access members of Derived};classDerived:publicBase<Derived>{// ...};
protected: ~counter()// objects should never be removed through pointers of this type { --objects_alive; } }; template<typenameT>intcounter<T>::objects_created(0); template<typenameT>intcounter<T>::objects_alive(0);
Curiously recurring template pattern - spirits away
https://ift.tt/I2D8lQV
CRTP的全称为Curiously recurring template pattern,描述的是C++中的一种模板应用模式。其示例代码可以抽象为这样的:
这种继承模式看起来非常绕:父类居然需要知道子类的类型。但是由于C++中的模板对于类型来说并没有强加什么限制,只要知道名字就可以。而编译器parse到这个继承文法的时候,子类的类型已经注册,因此这样的继承关系是可行的。
这样的代码看上去蛋疼无比,但是这样的代码演变为一种模式,一定有其特别的地方。所以下文我们就来探究一下其特别之处。
在C++中,虚函数是利用虚函数表来实现的。当调用一个虚函数时,首先获得虚表指针,然后在虚函数表中查找对应的函数指针,最后根据得到的函数指针来进行调用。完整的执行流程中,涉及到多次指针跳转,所以虚函数的开销还是比较大的。而利用CRTP这种模板继承来实现虚函数则可以避免这种查表的负担,其事例代码如下:
这里使用的主要机制就是强制类型转换,如果不涉及到虚继承,强制类型转换基本可以在编译期完成。
有时情况下,我们需要知道特定类型的活跃对象数目。类似于引用计数,只不过是针对类型而言,而不是针对特定对象。通过继承一个
counter
类,来存储其活跃对象数和总创建对象数。通过这两个静态计数器,我们就可以得到类型X的创建销毁的统计信息。
上面的那个例子是针对特定类型的对象创建和销毁计数,事实上我们还可以将单一对象的引用计数也实现。其实也不是完整实现,而只是通过
shared_ptr
来实现自动生命周期管理,计数方面还是由shared_ptr
内部维持。事实上,如果我们只使用
shared_ptr
也能达到自动生命周期管理的效果。但是有这样的一种情况,shared_ptr
是无法满足的,即内部函数需要异步执行的时候。此时该内部异步函数需要获得当前对象的shared_ptr
拷贝,但是类内部对象是无法得到这个拷贝的,只能得到this
指针。而从this
指针我们无法推演出对应的shared_ptr
的地址。所以,为了实现这个功能,我们需要内部嵌入一个函数来获得该智能指针备份。这就是enable_shared_from_this
的典型应用场景,目前见过的都是在网络连接管理的时候使用,例如Boost.Asio
。这样,我们就可以从
this->shared_from_this
中获得原始的shared_ptr
的指针,当然前提是this
是通过shared_ptr
来管理的。其主要原理是
enable_shared_from_this<T>
中存储了一个weak_ptr<T>
。而shared_ptr
的构造函数中有一个是从weak_ptr<T>
来转换的,所以我们就可以通过shared_from_this
来从这个保存的weak_ptr<T>
来获得shared_ptr<T>
。之所以不保存shared_ptr
,是为了防止自引用。自引用的情况下,对象无法析构,从而导致资源泄漏。在
C++
中,实现不可继承类主要通过的是将构造函数私有化,同时定义一个静态函数来调用该构造函数。但是这种实现由一个问题,就是如果子类也定义一个静态函数来调用父类的静态函数的话,就可以绕过该限制。如果子类大小与父类大小等同的话,就不会出现任何问题。
所以,之前的解决方案并不是完美的。下面我们通过
CRTP
和友元类来实现完美的final
方案。如果有类
CSomeDerive
继承CNoDerive
,则无法构造。CSomeDerive
的构造函数调用过程如下:由于CFobidDeriveProvider
是从CFobidDeriveProviderBase
虚拟派生,在虚继承出现的继承层次中,总是在构造非虚基类之前构造虚基类,因而会跳过CNoDerive和CFobidDeriveProvider
的构造函数而直接调用CFobidDeriveProviderBase
的构造函数,但CSomeDerive
不是CFobidDeriveProviderBase
的友元,因此也无法调用CFobidDeriveProviderBase
的私有构造函数.故而编译错误。via spiritsaway.info https://ift.tt/xtvPQyO
November 13, 2024 at 09:46AM
The text was updated successfully, but these errors were encountered: