Unreal Object의 특징
CDO(Class Default Object) : 객체의 초기 값을 자체적으로 관리합니다.
Reflection : 객체 정보를 런타임에서 실시간 조회가 가능합니다.
GC(Garbage Collection) : 참조되지 않는 객체를 메모리에서 자동 해제할 수 있습니다.
Serialization : 객체와 속성 정보를 통으로 안전하게 보관하고 로딩합니다.
Delegate : 함수를 묶어서 효과적으로 관리하고 호출할 수 있습니다.
Replication : 네트워크 상에서 객체간에 동기화를 시킬 수 있습니다.
Editor Integration : 언리얼 에디터 인터페이스를 통해 값을 편집할 수 있습니다.



실제로 Definitions.MyGame.h의 소스 파일을 보면 #define MYGAME_API DLLEXPORT라고 적혀 있는 것을 알 수 있습니다.
플랫폼에 따라서 dllexport문법이 다르기 때문에 플랫폼에 따라서 분리되어 있습니다.
기본 생성자이며, CDO를 생성할 때 이 생성자 코드가 한번 실행됩니다.
멤버 변수 위에 UPROPERTY 매크로를 얹어주면 이 변수는 앞으로 언리얼 엔진의 관리를 받게 됩니다. 향후 언리얼 오브젝트가 소멸되더라도 언리얼 엔진의 관리를 받아 해당 멤버 변수의 메모리도 같이 자동으로 소멸되며, 메모리 사용량도 체크할 수 있습니다.
멤버 함수 위에 UFUNCTION 매크로를 얹어주면 블루프린트와 연동되게 할 수 있으며 딜리게이트나 리플리케이션과 같은 함수를 사용할 수 있어서 멤버 함수의 활용폭이 늘어납니다.

//class.h
class COREUOBJECT_API UClass : public UStruct
{
...
/** The class default object; used for delta serialization and object initialization */
UObject* ClassDefaultObject;
...
일반적인 Constructor form은 다음과 같습니다.
UMyObject::UMyObject()
{
// Initialize Class Default Object properties here.
}
This constructor initializes the Class Default Object (CDO), which is the master copy on which future instances of the class are based.
UMyObject::UMyObject(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// Initialize CDO properties here.
}
//ObjectMacro.h
#define DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) \
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass; }
#define DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(TClass) \
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }
//class.h
class COREUOBJECT_API UClass : public UStruct
{
public:
UClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
protected:
/**
* Get the default object from the class, creating it if missing, if requested or under a few other circumstances
* @return the CDO for this class
**/
virtual UObject* CreateDefaultObject();
}
만약 특수한 상황으로 인해서 CDO가 초기화가 안되었거나 Missing이 되었을 경우 다음과 같은 함수를 호출한다.
//class.cpp
UObject* UClass::CreateDefaultObject()
{
...
if ( ClassDefaultObject == NULL )
{
...
ClassDefaultObject = StaticAllocateObject(this, GetOuter(), NAME_None, EObjectFlags(RF_Public|RF_ClassDefaultObject|RF_ArchetypeObject));
...
//CDO Constructor Call
(*ClassConstructor)(FObjectInitializer(ClassDefaultObject, ParentDefaultObject, false, bShouldInitializeProperties));
...
}
return ClassDefaultObject;
}
실제로 Actor를 복제하는 SpawnActor함수에 전달하는 파라미터 FActorSpawnParameters를 보면 Template이 NULL일 경우 CDO를 이용하여 Actor를 Spawn한다는 것을 알 수 있다.
//World.h
...
AActor* UWorld::SpawnActor( UClass* Class, FTransform const* UserTransformPtr, const FActorSpawnParameters& SpawnParameters )
{
...
AActor* const Actor = NewObject<AActor>(LevelToSpawnIn, Class, NewActorName, ActorFlags, Template, false/*bCopyTransientsFromClassDefaults*/, nullptr/*InInstanceGraph*/, ExternalPackage);
...
return Actor;
}
...
//LevelActor.cpp
struct ENGINE_API FActorSpawnParameters
{
...
/* An Actor to use as a template when spawning the new Actor.
The spawned Actor will be initialized using the property values of the template Actor.
If left NULL the class default object (CDO) will be used to initialize the spawned Actor. */
AActor* Template;
...
//UObjectGlobals.h
/**
* This struct is used for passing parameter values to the StaticConstructObject_Internal() method. Only the constructor parameters are required to
* be valid - all other members are optional.
*/
struct FStaticConstructObjectParameters
{
/** The class of the object to create */
const UClass* Class;
/** The object to create this object within (the Outer property for the new object will be set to the value specified here). */
UObject* Outer;
/** The name to give the new object.If no value(NAME_None) is specified, the object will be given a unique name in the form of ClassName_#. */
FName Name;
/** The ObjectFlags to assign to the new object. some flags can affect the behavior of constructing the object. */
EObjectFlags SetFlags = RF_NoFlags;
/** The InternalObjectFlags to assign to the new object. some flags can affect the behavior of constructing the object. */
EInternalObjectFlags InternalSetFlags = EInternalObjectFlags::None;
/** If true, copy transient from the class defaults instead of the pass in archetype ptr(often these are the same) */
bool bCopyTransientsFromClassDefaults = false;
/** If true, Template is guaranteed to be an archetype */
bool bAssumeTemplateIsArchetype = false;
/**
* If specified, the property values from this object will be copied to the new object, and the new object's ObjectArchetype value will be set to this object.
* If nullptr, the class default object is used instead.
*/
UObject* Template = nullptr;
/** Contains the mappings of instanced objects and components to their templates */
FObjectInstancingGraph* InstanceGraph = nullptr;
/** Assign an external Package to the created object if non-null */
UPackage* ExternalPackage = nullptr;
COREUOBJECT_API FStaticConstructObjectParameters(const UClass* InClass);
};
template< class T >
FUNCTION_NON_NULL_RETURN_START
T* NewObject(UObject* Outer, FName Name, EObjectFlags Flags = RF_NoFlags, UObject* Template = nullptr, bool bCopyTransientsFromClassDefaults = false, FObjectInstancingGraph* InInstanceGraph = nullptr)
FUNCTION_NON_NULL_RETURN_END
{
if (Name == NAME_None)
{
FObjectInitializer::AssertIfInConstructor(Outer, TEXT("NewObject with empty name can't be used to create default subobjects (inside of UObject derived class constructor) as it produces inconsistent object names. Use ObjectInitializer.CreateDefaultSubobject<> instead."));
}
FStaticConstructObjectParameters Params(T::StaticClass());
Params.Outer = Outer;
Params.Name = Name;
Params.SetFlags = Flags;
Params.Template = Template;
Params.bCopyTransientsFromClassDefaults = bCopyTransientsFromClassDefaults;
Params.InstanceGraph = InInstanceGraph;
return static_cast<T*>(StaticConstructObject_Internal(Params));
}
//UObjectGlobals.cpp
UObject* StaticConstructObject_Internal(const FStaticConstructObjectParameters& Params)
{
const UClass* InClass = Params.Class;
UObject* InOuter = Params.Outer;
const FName& InName = Params.Name;
EObjectFlags InFlags = Params.SetFlags;
UObject* InTemplate = Params.Template;
...
UObject* Result = NULL;
...
Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, Params.InternalSetFlags, bCanRecycleSubobjects, &bRecycledSubobject, Params.ExternalPackage);
...
if (!bRecycledSubobject)
{
STAT(FScopeCycleCounterUObject ConstructorScope(InClass->GetFName().IsNone() ? nullptr : InClass, GET_STATID(STAT_ConstructObject)));
//CDO Constructor Call
(*InClass->ClassConstructor)( FObjectInitializer(Result, InTemplate, Params.bCopyTransientsFromClassDefaults, true, Params.InstanceGraph) );
}
...
return Result;
}
As the night progressed, Ella found herself increasingly absorbed by the game’s interactive features. She began experimenting with different betting strategies, such as betting on combinations of numbers and adjusting https://icecassinos.com.br/ her bets based on previous outcomes. To her astonishment, a series of lucky spins led her to a significant win, surpassing her initial expectations. The thrill of watching the roulette ball land on her chosen numbers, combined with the excitement of her growing winnings, turned an ordinary evening into an unforgettable experience. Ella’s story is a testament to how a little curiosity and risk-taking can lead to unexpected rewards.