UE4 C++反射从入门到原理剖解(UE4 4.26)

UE4 C++反射具体应用案例

获取某类的所有对象(加载到内存)

加载到内存指的是包括蓝图资产, 在关卡里等等已经加载到内存的对象,如果从内存卸载掉的不算。

案例一:获取AMyActor的所有对象

	//获取对应基类的对象
	TArray<UObject*> result;
	GetObjectsOfClass(AMyActor::StaticClass(), result);

	for(auto& Object : result)
	{
		UE_LOG(LogTemp, Warning, TEXT("AMyActor=%s"), *Object->GetName());
	}

案例二:获取UEnum的所有对象

	TArray<UObject*> result1;
	GetObjectsOfClass(UEnum::StaticClass(), result1);
	for(auto& Object : result1)
	{
		UE_LOG(LogTemp, Warning, TEXT("UEnum=%s"), *Object->GetName());
	}

案例三: 获取所有反射结构体(UScriptStruct)对象

	TArray<UObject*> result2;
	GetObjectsOfClass(UScriptStruct::StaticClass(), result2);
	for(auto& Object : result2)
	{
		UE_LOG(LogTemp, Warning, TEXT("UScriptStruct=%s"), *Object->GetName());
	}

访问UClass/UScriptStruct/UEnum/UFunction

简介

UClass存储了UObject类信息,如类名字, 类有多少个变量, 变量名字, 变量类型等等

UScriptStruct存储了USTRUCT()结构体的信息, 如结构体名字, 结构体有多少个变量, 变量名字, 变量类型等等

UFunction存储了函数的各种信息, 如函数的名字,函数存在多少个参数, 各个参数的类型又是什么等等

下面以UTestObject, FMyStruct, EMyEnum为案例

UENUM()
enum class EMyEnum : uint8
{
	Sucess,
	failed
};


USTRUCT()
struct FMyStruct
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere)
	int32 a = 3;

	UPROPERTY(EditAnywhere)
	float b = 2;

	UPROPERTY(EditAnywhere)
	FString C = "1243";
};


UCLASS(meta = (DisplayName = "MyTestObject"))
class UTestObject : public UObject
{
	GENERATED_BODY()

	UTestObject();
	
public:
	UPROPERTY(EditAnywhere)
	bool boolVarible = false;

	UPROPERTY(EditAnywhere)
	EMyEnum EnumVarible = EMyEnum::failed;
	
	UPROPERTY(EditAnywhere)
	int32 int32Varible = 15;

	UPROPERTY(EditAnywhere)
	float floatVarible = 3.1314f;

	UPROPERTY(EditAnywhere)
	FString stringVarile = "TestString";
	
	UPROPERTY(EditAnywhere)
	FVector VectorVarible = FVector::UpVector;

	UPROPERTY(EditAnywhere)
	FSoftObjectPath SoftPathVarible;
	
	UPROPERTY(EditAnywhere)
	UStaticMesh* Mesh;
	
	UPROPERTY(EditAnywhere)
	FMyStruct MyStruct;

	UPROPERTY(EditAnywhere)
	TArray<int32> IntArray = {2, 3, 6};

	UPROPERTY(EditAnywhere)
	TMap<int32, FString> DataMap = {{1, "a"},{2, "b"}};

	
	UFUNCTION(CallInEditor)
	void TestA(int32 A);
	
	UFUNCTION(CallInEditor)
	float TestB(int32 A, float B, UStaticMesh* InMesh, TArray<int32> Values);
};

访问UClass以及相关Property

访问各种类型的UPROPERTY成员变量

	UTestObject* testObject = NewObject<UTestObject>();
	testObject->AddToRoot();
	
	UClass* TestObjectClass = testObject->GetClass();
	// 或者 UClass* Class = UTestObject::GetClass();
	UE_LOG(LogTemp, Warning, TEXT("UTestObject Class=%s"), *TestObjectClass->GetName());
	
	for (TFieldIterator<FProperty> P(TestObjectClass); P; ++P)
	{
		FProperty* PropertyPtr = *P;
		if(!PropertyPtr)
			continue;

		FFieldClass* PropertyClass = PropertyPtr->GetClass();
		//FFieldClass* BoolClass FBoolProperty::StaticClass();
		const FString& PropertyName = PropertyPtr->GetName();
		const FString& PropertyClassName = PropertyClass->GetName();

		// bool 类型
		if(PropertyPtr->IsA(FBoolProperty::StaticClass()))
		{
			FBoolProperty* BoolProperty = CastFieldChecked<FBoolProperty>(PropertyPtr);
			if(BoolProperty)
			{
				bool* ValuePtr = BoolProperty->ContainerPtrToValuePtr<bool>(testObject);
				if(ValuePtr)
				{
					UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %d"), *PropertyClassName, *PropertyName, (*ValuePtr) == true ? 1 : 0);
				}
			}
		}
		// UENUM标注的枚举体
		else if(PropertyPtr->IsA(FEnumProperty::StaticClass()))
		{
			FEnumProperty* EnumProperty = CastFieldChecked<FEnumProperty>(PropertyPtr);
			if(EnumProperty)
			{
				uint8* ValuePtr = EnumProperty->ContainerPtrToValuePtr<uint8>(testObject);
				if(ValuePtr)
				{
					UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %d"), *PropertyClassName, *PropertyName, (*ValuePtr));
				}
			}
		}
		// 数值类型(int8, int16, int32, int64, byte, uint16, uint32, uint64, float, double)
		else if(PropertyPtr->IsA(FNumericProperty::StaticClass()))
		{
			if(PropertyPtr->IsA(FInt8Property::StaticClass()))
			{
			}
			else if(PropertyPtr->IsA(FInt16Property::StaticClass()))
			{
			}
			else if(PropertyPtr->IsA(FIntProperty::StaticClass()))
			{
				FIntProperty* IntProperty = CastFieldChecked<FIntProperty>(PropertyPtr);
				if(IntProperty)
				{
					int32* ValuePtr = IntProperty->ContainerPtrToValuePtr<int32>(testObject);
					if(ValuePtr)
					{
						UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %d"), *PropertyClassName, *PropertyName, (*ValuePtr));
					}
				}
			}
			else if(PropertyPtr->IsA(FInt64Property::StaticClass()))
			{
			}
			else if(PropertyPtr->IsA(FByteProperty::StaticClass()))
			{
			}
			else if(PropertyPtr->IsA(FUInt16Property::StaticClass()))
			{
			}
			else if(PropertyPtr->IsA(FUInt32Property::StaticClass()))
			{
			}
			else if(PropertyPtr->IsA(FUInt64Property::StaticClass()))
			{
			}
			else if(PropertyPtr->IsA(FFloatProperty::StaticClass()))
			{
				FFloatProperty* FloatProperty = CastFieldChecked<FFloatProperty>(PropertyPtr);
				if(FloatProperty)
				{
					float* ValuePtr = FloatProperty->ContainerPtrToValuePtr<float>(testObject);
					if(ValuePtr)
					{
						UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %f"), *PropertyClassName, *PropertyName, (*ValuePtr));
					}
				}
			}
			else if(PropertyPtr->IsA(FDoubleProperty::StaticClass()))
			{
				
			}
		}
		// FString类型
		else if(PropertyPtr->IsA(FStrProperty::StaticClass()))
		{
			FStrProperty* StringProperty = CastFieldChecked<FStrProperty>(PropertyPtr);
			if(StringProperty)
			{
				FString* ValuePtr = StringProperty->ContainerPtrToValuePtr<FString>(testObject);
				if(ValuePtr)
				{
					UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %s"), *PropertyClassName, *PropertyName, *(*ValuePtr));
				}
			}
		}
		// FText类型
		else if(PropertyPtr->IsA(FTextProperty::StaticClass()))
		{
		}
		// FName类型
		else if(PropertyPtr->IsA(FNameProperty::StaticClass()))
		{
		}
		// USTRUCT标注结构体类型, 比如FVector, FVector2D, FSoftObjectPath, FMyStruct等等
		else if(PropertyPtr->IsA(FStructProperty::StaticClass()))
		{
			FStructProperty* StructProperty = CastFieldChecked<FStructProperty>(PropertyPtr);
			if(StructProperty)
			{
				if(StructProperty->Struct == TBaseStructure<FVector>::Get())
				{
					FVector* ValuePtr = StructProperty->ContainerPtrToValuePtr<FVector>(testObject);
    				if(ValuePtr)
    				{
    					UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %s"), *PropertyClassName, *PropertyName, *(*ValuePtr).ToString());
    				}
				}
				else if(StructProperty->Struct == TBaseStructure<FSoftObjectPath>::Get())
				{
					FSoftObjectPath* ValuePtr = StructProperty->ContainerPtrToValuePtr<FSoftObjectPath>(testObject);
					if(ValuePtr)
					{
						UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %s"), *PropertyClassName, *PropertyName, *(*ValuePtr).ToString());
					}
				}
				else if(StructProperty->Struct == TBaseStructure<FMyStruct>::Get())
				{
					
				}
			}
		}
		// UObject指针类型, 比如UStaticMesh*
		else if(PropertyPtr->IsA(FObjectProperty::StaticClass()))
		{
			FObjectProperty* ObjectProperty = CastFieldChecked<FObjectProperty>(PropertyPtr);
			if(ObjectProperty)
			{
				UObject* Object = ObjectProperty->GetObjectPropertyValue_InContainer(testObject);
				if(ObjectProperty->PropertyClass.Get() == UStaticMesh::StaticClass())
				{
					FString Value = Object ? Object->GetName() : FString("NullObbject");
					UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s -- Value: %s"), *PropertyClassName, *PropertyName, *Value);
				}
			}
		}
		// Array
		else if(PropertyPtr->IsA(FArrayProperty::StaticClass()))
		{
			FArrayProperty* ArrayProperty = CastFieldChecked<FArrayProperty>(PropertyPtr);
			if(ArrayProperty)
			{
				void* ArrayVariblePtr = ArrayProperty->ContainerPtrToValuePtr<void>(testObject);
				// 数组元素属性
				FProperty* InnerProperty = ArrayProperty->Inner;
				UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s, InnerProperty: %s"), *PropertyClassName, *PropertyName, *InnerProperty->GetClass()->GetName());

				for(int32 DimIndex = 0; DimIndex < ArrayProperty->ArrayDim; DimIndex++)
				{
					FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayVariblePtr);
					const int32 NumberOfItems = ArrayHelper.Num();
					
					for(int32 ArrayIndex = 0; ArrayIndex < NumberOfItems; ArrayIndex++)
					{
						if(InnerProperty->IsA(FIntProperty::StaticClass()))
						{
							uint8* ElementRawPtr = ArrayHelper.GetRawPtr(ArrayIndex);
							int32* ValuePtr = (int32*)(ElementRawPtr);
							if(ValuePtr)
							{
								UE_LOG(LogTemp, Warning, TEXT("PropertyName: %s[%d], --Value: %d"), *PropertyName, ArrayIndex, *ValuePtr);
							}
						}
					}
				}

			}
		}
		else if(PropertyPtr->IsA(FMapProperty::StaticClass()))
		{
			FMapProperty* MapProperty = CastFieldChecked<FMapProperty>(PropertyPtr);
			if(MapProperty)
			{
				void* MapVariblePtr = MapProperty->ContainerPtrToValuePtr<void>(testObject);
				FProperty* KeyProperty = MapProperty->KeyProp;
				FProperty* ValueProperty = MapProperty->ValueProp;
				UE_LOG(LogTemp, Warning, TEXT("PropertyType : %s, PropertyName: %s, KeyProperty: %s, ValueProperty: %s"), *PropertyClassName,
					*PropertyName, *KeyProperty->GetClass()->GetName(), *ValueProperty->GetClass()->GetName())

				FScriptMapHelper Helper(MapProperty, MapVariblePtr);
				const int32 Num = Helper.Num();
				for (int32 DynamicIndex = 0; DynamicIndex < Num; ++DynamicIndex)
				{
					if (Helper.IsValidIndex(DynamicIndex))
					{
						uint8* KeyElementRawPtr = Helper.GetKeyPtr(DynamicIndex);
						int32* KeyPtr = (int32*)(KeyElementRawPtr);

						uint8* ValueElementRawPtr = Helper.GetValuePtr(DynamicIndex);
						FString* ValuePtr = (FString*)(ValueElementRawPtr);

						if(KeyPtr && ValuePtr)
						{
							UE_LOG(LogTemp, Warning, TEXT("Key : %d, Value: %s"), *KeyPtr, *(*ValuePtr))
						}
					}
				}
			}
		}
		
	}

访问UScriptStruct以及相关Property

UScriptStruct保持了USTRUCT结构体的信息

遍历成员变量方式基本和UClass一样

	UScriptStruct* MyScriptStruct = TBaseStructure<FMyStruct>::Get();
	for (TFieldIterator<FProperty> P(MyScriptStruct); P; ++P)
	{
		//其他和UClass遍历属性一致
	}

访问UEnum

	UEnum* EnumClass = StaticEnum<EMyEnum>();
	int32 NumEnum = EnumClass->NumEnums();
	UE_LOG(LogTemp, Warning, TEXT("MyEnumName: %s"), *EnumClass->GetName());
	for(int32 Index = 0; Index < NumEnum; Index++)
	{
		FName EnumName = EnumClass->GetNameByIndex(Index);
		UE_LOG(LogTemp, Warning, TEXT("Enum[%d]: %s"), Index, *EnumName.ToString())
	}

访问UFunction以及相关Property

在上面的UTestObject定义两个UFUNCTION函数TestATestB

获取UObject或者UScriptStruct的UFunction,并获取函数的各种参数(Property/Param)

	UTestObject* testObject = NewObject<UTestObject>();
	testObject->AddToRoot();
	
	UClass* TestObjectClass = testObject->GetClass();
	
	//遍历一个类/结构体的函数
	for (TFieldIterator<UFunction> F(TestObjectClass); F; ++F)
	{
		UFunction* Fun = *F;
		if(!Fun)
			continue;

		FString PrintText;
		FString FuncName = Fun->GetName();
		PrintText = FString::Printf(TEXT("FuncName: %s: "), *FuncName);

		// 遍历一个函数参数类型
		for (TFieldIterator<FProperty> FuncP(Fun); FuncP; ++FuncP)
		{
			FProperty* Property = *FuncP;
			if(!Property)
				continue;

			FString PropertyName = Property->GetName();
			FString PropertyType = Property->GetClass()->GetName();
			PrintText = FString::Printf(TEXT("%s \nPropertyName: %s    PropertyType:%s"), *PrintText, *PropertyName, *PropertyType);
		}

		UE_LOG(LogTemp, Error, TEXT("%s"), *PrintText);
	}

如果函数存在返回值, 则函数最后一个参数就是返回值.

调用函数(ProcessEvent)

利用相同的Struct内存布局,来传参. 当然网上有更通用的调用方法,我这里只是一个实例。

	// 调用UFunction
	UFunction* TestAFunction = TestObjectClass->FindFunctionByName("TestB");
	if(TestAFunction)
	{
		struct TempStruct
		{
			int32 A;
			float B;
			UStaticMesh* InMesh;
			TArray<int32> Values;
			float ReturnValue; // last is return value
		};
		
		TempStruct Temp{1, 2.564f, nullptr, {100, 124, 5}};
		testObject->ProcessEvent(TestAFunction, &Temp);
		UE_LOG(LogTemp, Error, TEXT("ReturnValue = %f"), Temp.ReturnValue);
		
	}

UE4 C++反射的实现原理剖解

CPP反射总体流程

 说明一下UE4 CPP反射宏(UClass, UPROPERTY, UFUNCTION)是空宏,最后根本没有参与到C++编译当中,具体见ObjectMacros.h

UBT

UBT全称UnrealBuildTool, 用在建立项目配置,如模块依赖,包含文件目录,设置项目优化配置项等,作用类似于premake, cmake这些东西,就我看到的代码而言,UE4 C++ 反射和UBT几乎没什么关系。UnrealBuildTool.exe文件Engine\Source\Programs\UnrealBuildTool\Development

UHT

UHT 全称UnrealHeaderTool, UE4 C++反射依赖于 UHT这个工具。UHT用在创建新文件xxx.generated.h 和 xxx.gen.cpp , 其中generated.h 主要是相关函数的声明, gen.cpp是具体反射数据的指定(类元数据, 属性反射数据,函数反射数据等)

UnrealHeaderTool.exe文件Engine\Binaries\Win64\

UE4 C++反射原理剖解

上面说了C++反射代码的使用以及C++反射流程。下面具体分析反射实现细节。UE4的实现C++反射原理的机制看起来有点复杂,实际很简单,用一句话概括:利用宏标记,用UHT进行字符串分析宏标记来生成对应相应的硬编码CPP代码(xxx.generated.h和xxx.gen.cpp), 在CPP硬编码中手动指定属性反射数据,函数反射数据,类反射数据。

UE4 反射类结构

 拿UClass(UStruct)为例子, 这里UField* Children存储了一个类所有反射函数数据的节点,UField*是一个链表结构,一个UField*节点就是一个UFunction

 同样的, FField* ChildPerperties存储了一个类所有反射变量数据的节点,FField*是一个链表结构,一个FField*节点就是一个FProperty

宏标记

UE4提供了UPROPERTY来标记属性的反射数据,用UFUNCTION标记函数的反射数据,用UCLASS标记类的反射数据。

下面拿例子 ATestActor来说明UE4 C++反射的实现

(1)UClass存储了继承UObject的类反射信息, 用UCLASS宏来标记

(2)UScriptStruct存储了FXXX的结构体反射信息, USTRUCT宏来标记

 

(3)UFunction存储了函数的反射信息, 用UFUNCTION宏来标记

(4)FProperty存储属性的反射信息, 用UPROPERTY宏来标记

UClass类的具体机制

UClass的经典引用判断一个对象是不是一个类                                                   

从上图UClass直接代表一个类的类数据, 同个类的类对象的UClass都是同一个, 存储了反射属性数据和反射函数数据等等。这里有个疑惑:UClass是怎么构造出来的?ATestActor::StaticClass() 哪里来的?答案很简单,用GENERATED_BODY 将 “TestActor.generated.h” 和 “TestActor.gen.cpp” 解析的代码包含进行UClass的构造。当然具体过程很长,下面我慢慢分析

GENERATED_BODY

首先看看ObjectMacros.h, 里面很多宏用于实现UE4 C++的反射机制

再看 “TestActor.generated.h” 和TestActor.h

可以知道 CURRENT_FILE_ID 为 TestReflect_Source_TestReflect_TestActor_h。而__LINE__就是GENERATED_BODY所在行号12。   所以GENERATED_BODY 相当于TestReflect_Source_TestReflect_TestActor_h_12_GENERATED_BODY。   

这个宏其实在 TestActor.generated.h 也定义了,看看相关代码​​​​​​

TestReflect_Source_TestReflect_TestActor_h_12_PRIVATE_PROPERTY_OFFSETTestReflect_Source_TestReflect_TestActor_h_12_SPARSE_DATA,TestReflect_Source_TestReflect_TestActor_h_12_RPC_WRAPPERS 等都是在TestActor.generated.h 声明的宏,展开来就是一堆代码, 拿TestReflect_Source_TestReflect_TestActor_h_12_RPC_WRAPPER。举个例子

也就是说GENERATED_BODY的本质就是替换宏,把 宏 TestReflect_Source_TestReflect_TestActor_h_12_GENERATED_BODY展开的C++代码放到TestActor类里, 具体展开就是

代码细节很多,很多具体宏没展开,比如 DECLARE_CLASS, DECLARE_SERALIZER.

这里重点讲解的宏是DECLARE_CLASS,UClass*的生成和StaticClass()函数就和DECLARE_CLASS息息相关

可以看到StaticClass()函数是怎么来的

这里GetPrivateStaticClass()怎么来的?在TestActor.gen.cpp的

 

 水落石出, UClass*和StaticClass静态函数是怎么来的已经一目了然!

这个还有个很小的点, 多年前面试官喜欢问的,一个UObject怎么访问其母类的方法属性, 写过UE4 C++代码都知道是Super::XXX, 从上面DECLARE_CLASS 展开看到就是一个宏替换而已

类(UClass)元数据的注册

我们看到了UClass*的创建, 但是UClass的元数据呢?首先元数据本质就是Key-Vakue形式的值,可以和TMap<FString, String> 等同。类元数据在xxx.gen.cpp硬编码进行的,看TestActor.gen.cpp代码。

上面的Z_Construct_UClass_ATestActor_Statics 是个结构体,包含了各种反射数据(类,属性,函数),可以看到都是静态变量, 这样不用实例化结构体就能保存反射数据了。这里有个小细节,WITH_METADATA说明元数据都是只在编辑器下存在。可以看到反射数据就是硬编码指认到静态变量,有个问题怎么把这个数据放到UClass*里,并且是什么时候放入到的?继续往下看,在TestActor.gen.cpp代码底部

把各种静态反射数据(类元数据,属性元数据,函数元数据) 填充到FClassParams ClassParams里 

 然后用FClassParams的数据对ATestActor的UClass进行赋值, 比如类元数据,属性数据,函数数据等。

 

有个问题是Z_Construct_UClass_ATestActor函数什么时候执行? 答案在TestActor.gen.cpp代码的最底部

这个本质上是个静态回调函数, 看看下面代码

每当一个模块(dll)加载进来,对应的UObject的UClass注册流程就开始进行了 

属性反射

属性反射数据主要包括属性的名字, 属性的指针偏移(UE4的属性访问是通过变量相对于类的偏移量), 属性元数据等。其实属性反射实现的过程和类的元数据很类似, 都在TestActor.gen.cpp文件里硬编码的.

const UE4CodeGen_Private::FFloatPropertyParams Z_Construct_UClass_ATestActor_Statics::NewProp_a = { "a", nullptr, (EPropertyFlags)0x0010000000000001, UE4CodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, 1, STRUCT_OFFSET(ATestActor, a), METADATA_PARAMS(Z_Construct_UClass_ATestActor_Statics::NewProp_a_MetaData, UE_ARRAY_COUNT(Z_Construct_UClass_ATestActor_Statics::NewProp_a_MetaData)) };

STRUCT_OFFSET(ATestActor, a) 是求变量a在类ATestActor的偏移字节数, 这点比较巧妙,利用偏移量达到访问属性的目的。最终一样PropPointers是放到ClassParams里,最终被注册到UClass*数据了,从上图可以看出有多少个反射属性就对应多少对硬编码的属性数据(属性元数据以及属性基本信息).

函数反射

函数反射和属性反射一样简单,函数反射主要包括函数的名字,函数的访问,函数的元数据等。

这里函数的各种数据和属性一样的硬编码的,在上面宏展开的代码可以看到

DECLARE_FUNCTION(execTestFunc)

进一步看

在TestActor.gen.cpp可以看到

对应的宏不细说,就是执行了TestFunc, 绕了一圈

这里函数指针的注册到UClass在StaticRegisterNativesATestActor中执行的

至于StaticRegisterNativesATestActor是在上面IMPLAMENT_CLASS展开构造函数中进行的。

至于函数的元数据, 和属性元数据构造过程差不多。

 函数的元数据最终也到了ClassParams 

UE4 C++ 反射 VS RTTR C++ 反射

我之前一篇博客 RTTR反射原理剖解 分析了另一个C++反射框架 RTTR, 总体上是利用模板元编程进行类型萃取,而整个框架是非侵入式,而UE4的C++反射实现大体是预生成代码加上宏代码替换,硬编码来实现C++反射,从实现上我个人觉得RTTR更灵活(非侵入式),也更优雅(模板推导),当然RTTR明显使用比UE4更多的模板代码,可能会造成代码更慢编译速度问题。

资料参考

【1】《InsideUE4》UObject(十三)类型系统-反射实战

【2】  RTTR(Run Time Type Reflection) C++反射原理实现剖解

  • 12
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 没有明显的问题。 这段代码似乎没有任何明显的语法错误或逻辑错误。它似乎是在定义一个名为“MemoryLeakFunction”的函数,在函数中创建了一个新的 XXX_Class 对象,然后调用了这个对象的 DoSomething 方法,最后以 return 语句结束函数的执行。 但是,这段代码存在内存泄漏的风险。在函数执行完毕后,分配给 XXX_Class 对象的内存并没有被释放,这意味着这块内存将永远无法再次使用。如果这个函数被频繁调用,那么将会产生大量的内存泄漏。 为了避免这种情况,应在函数执行完毕后使用 delete 语句释放分配给 XXX_Class 对象的内存,例如: void MemoryLeakFunction() { XXX_Class * pObj = new XXX_Class(); pObj->DoSomething(); delete pObj; return; } ### 回答2: 这段代码存在内存泄漏的问题。 在这段代码中,使用了new操作符动态创建了一个XXX_Class的对象,并将其赋值给指针pObj。然后通过pObj调用了DoSomething()函数。这部分代码看似没有问题,但问题出在函数的最后。 在函数的最后,没有对动态分配的内存进行释放。这意味着在函数执行完毕后,动态分配的内存没有被释放,导致发生了内存泄漏。 为了解决这个问题,可以在函数的最后加上delete操作符,释放动态分配的内存。修改后的代码如下: void MemoryLeakFunction() { XXX_Class * pObj = new XXX_Class(); pObj->DoSomething(); delete pObj; return; } 这样一来,在函数执行完毕后,动态分配的内存会被正确释放,避免了内存泄漏的问题。 ### 回答3: 这段代码可能存在内存泄漏的问题。 在函数中,会创建一个新的XXX_Class对象并将其分配到堆上,然后调用该对象的DoSomething()方法。然而,在函数返回之前,没有对该对象进行内存释放或者析构,导致该对象所占用的内存无法被回收,从而产生内存泄漏的问题。 为了解决这个问题,可以在函数末尾添加delete语句来释放对象所占用的内存:delete pObj;。这样可以确保在函数结束时,已经不再需要的对象会被正确释放,避免内存泄漏的发生。 修正后的代码如下: void MemoryLeakFunction() { XXX_Class * pObj = new XXX_Class(); pObj->DoSomething(); delete pObj; return; }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值