以上两篇文章里面简单介绍了TSharedPtr和TSharedRef的构造方式,因为TWeakPtr的构造相对简单,所以就不单独开一篇文章来介绍了。
在介绍UE智能指针的使用场景之前,有必要对TWeakPtr进行一下特殊介绍,因为TWeakPtr的相关操作,都不是通过它本身进行的,必须要借助Pin函数得到TSharedPtr,通过这个TSharedPtr进行操作。C++11提供的weak_ptr,也要通过lock函数得到shared_ptr,再用shared_ptr操作。Why?因为如果直接使用weak_ptr,那如果在使用期间目标被销毁了,weak_ptr继续操作,就会导致崩溃。而先转成shared_ptr,就会先强制引用,从而目标不会被销毁,操作就会变得安全。
针对智能指针,主要有以下操作场景。
1、判断智能指针指向的目标是否有效。
调用函数IsValid,TSharedPtr TSharedRefr都实现了这个函数。
2、操作目标指针,我们可以像使用原生指针一样操作智能指针
struct FSharedTest
{
bool bFoo;
};
TSharedPtr< FSharedTest, Mode > SharedArray( new FSharedTest() );
// TSharedPtr使用起来像原生指针一样
SharedArray->bFoo = true;
TSharedPtr TSharedRef都可以这么操作。所以虽然TSharedRef起名像是智能引用,但使用起来就是个指针。
3、取消强引用。
对于TSharedPtr而言,变量出了生命域后,会自动将强引用计数-1,但如果想手动取消的话,可以调用TSharedPtr::Reset函数。
对于TSharedRef而言,没有Reset函数!只能等变量生命结束后,自动减少引用计数!原因还是在于TSharedRef的定位为一个始终有效的智能指针,如果手动Reset,那不变成无效了嘛!
4、判断两个智能指针是否相等。直接使用==操作两个智能指针即可,如果两个指针引用的Object相同,则返回true,否则返回false。下图可以看出,UE4重载了很多方法,用来进行指针间的相等判断。
5、解引用。解引用得到的是一个引用类型。
struct FSharedTest
{
bool bFoo;
};
TSharedPtr< FSharedTest, Mode > SharedArray( new FSharedTest() );
//解引用得到的类型是引用,而不是值!
( *SharedArray ).bFoo = false;
6、指针转换,Cast。使用StaticCast进行downcast,使用CastCast去除const属性。
// Test casting
class FBase
{
bool bFoo;
};
class FDerived: public FBase
{ };
{
//父向子Cast,用StaticCast
TSharedPtr< FBase, Mode > DerivedAsBasePtr( new FDerived() );
TSharedPtr< FDerived, Mode > DerivedPtr( StaticCastSharedPtr< FDerived >( DerivedAsBasePtr ) );
}
{
//子向父Cast,不需要!直接赋值即可!
TSharedPtr< FDerived, Mode > DerivedPtr( new FDerived() );
TSharedPtr< FBase, Mode > BasePtr( DerivedPtr );
}
TSharedPtr< const float, Mode > FloatPtrB( new float( 2.0f ) );
TWeakPtr< float, Mode > WeakFloat;
//使用ConstCast去除Const属性
WeakFloat = ConstCastSharedPtr< float >( FloatPtrB );
cast也有几个函数版本,分别用于处理TSharedPtr TSharedRef
7、对外传递引用。需要继承TSharedFromThis,AsShared返回声明的类型,SharedThis返回实际类型。关于这两个函数的区别,官网介绍代码如下
class FRegistryObject;
class FMyBaseClass: public TSharedFromThis<FMyBaseClass>
{
virtual void RegisterAsBaseClass(FRegistryObject* RegistryObject)
{
// Access a shared reference to 'this'.
// We are directly inherited from <TSharedFromThis> , so AsShared() and SharedThis(this) return the same type.
TSharedRef<FMyBaseClass> ThisAsSharedRef = AsShared();
// RegistryObject expects a TSharedRef<FMyBaseClass>, or a TSharedPtr<FMyBaseClass>. TSharedRef can implicitly be converted to a TSharedPtr.
RegistryObject->Register(ThisAsSharedRef);
}
};
class FMyDerivedClass : public FMyBaseClass
{
virtual void Register(FRegistryObject* RegistryObject) override
{
// We are not directly inherited from TSharedFromThis<>, so AsShared() and SharedThis(this) return different types.
// AsShared() will return the type originally specified in TSharedFromThis<> - TSharedRef<FMyBaseClass> in this example.
// SharedThis(this) will return a TSharedRef with the type of 'this' - TSharedRef<FMyDerivedClass> in this example.
// The SharedThis() function is only available in the same scope as the 'this' pointer.
TSharedRef<FMyDerivedClass> AsSharedRef = SharedThis(this);
// RegistryObject will accept a TSharedRef<FMyDerivedClass> because FMyDerivedClass is a type of FMyBaseClass.
RegistryObject->Register(ThisAsSharedRef);
}
};
class FRegistryObject
{
// This function will accept a TSharedRef or TSharedPtr to FMyBaseClass or any of its children.
void Register(TSharedRef<FMyBaseClass>);
};