What is CDO?

The whole studio, for many months, is trying to figure out what is a purpose of Class Default Object (CDO)?

In the documentation I can see:

This structure initializes the
properties from an archetype or class
default object (CDO) after the
constructor returns. It is used to
guarantee that every UObject that is
constructed has the properties
initialized. The FObjectInitializer
can be used as demonstrated in the
following constructor to skip any
initialization of properties present
in the constructor when they have
already been set by an Archetype or
CDO.

So I think that CDO is used for fast instances creation without initizalizing their properties (by some kind of copying?). But when the instance of the object or actor is created I can see that the whole constructor with property initialization is running again, so what’s the point?

It is also confusing when I was trying to create new objects inside the constructor and the pointer was always pointing to the object’s instance from CDO, not from the instance (I know I should do this in PostInitializeComponents but still - it is confusing).

So… If anyone can enlighten us what’s CDO and what should we know about it to not make any stupid mistake, that would be great :slight_smile:

4 Likes

Did you ever manage to figure out the exact definition for CDO then?

CDO is Class Default Object, it’s a master copy of object for specific class contained in reflection system which in this case it’s contained in class representing the class which is UClass. CDO contains object defaults and make them accessible very easily since C++ don’t have such a feature. It is also used to hold default values of variables in virtual classes created from blueprints for example.

CDO is created when engine is initialized when it generates UClass objects for each class, then it naturally executes constructor setting default variables. Thats also reason why you should not use objects or gameplay code in constructor, because constructor is executed early in engine initiation stage where most objects don’t exist, not to mention level is most likely won’t be even loaded at that point. You should use different start up events which both UObject and AActor contains for many stages of it’s creation process, you can read about those here:

You can access CDO of each class via this function in UClass of specific class, starting 4.9 you will also able to access defaults from CDO in blueprint too (in more protected way of corse):

So in short, because of this setup, constructor can be only used for defaults that will be contained in CDO

21 Likes

Wow, it seems I forgot to notice that the question is out of date, but Your answer is very helpful and informative. Thanks!

In the studio we realized the CDO might be created, because the editor has to show us somehow actors and other objects defined in the code. That’s why we can set up meshes and materials from the code in the constructor and see them in the editor in action.

We’ve learned that it is safier to write some kind of Init function in the object and treat it like a classic c++ constructor (in this way we can also pass arguments!).

Also, we’ve started with UE 4.6 where we could use new operator and we didn’t know it might cause some problems.

Anyway, we don’t have problems with CDO anymore, I think we’ve finally understand the magic behind uobject constructor. Thanks :slight_smile:

Yup! Check the answer :slight_smile:

Hey thanks for that :slight_smile:

I have been doing some testing and have found that most of what has in his post is true with some differences.

CDO is created when engine is initialized when it generates UClass objects for each class,
This is true. An instance for each UClass is created during engine initialization and is assigned to be the CDO for that UClass.

then it naturally executes constructor setting default variables and it will never call constructor again

This seems to be false in my testing. The constructor of a given UClass object is called every time a UObject is created. However in these new instances the variables of the object will be overwritten by the variables from the CDO or from a previously serialized version of the object (e.g. an AActor in a level).

I created a simple test Actor (which of course ultimately derives from UObject) called “ARandomPropertyActor” and placed 11 of them into a new level. The constructor of this actor sets MySpecialProperty to a random integer and prints it to the log. On BeginPlay for the actor I also print out each instances MySpecialProperty as well as getting the CDO for the UClass and printing it out.

The funny thing I find is that when I place new instances of ARandomPropertyActor into the level, their constructor is called and their MySpecialNumber is set to the random value. (the CDO is not used as a template).

I’m still trying to get the bigger picture here but thought I would post what I’ve know so far as there is very little documentation (apart from the source code) on the UObject lifecycle at runtime and in the editor.

3 Likes

I observed that the constructor of a UCLASS is run again during game start (as @tmek mentioned above). Is this only the case in editor builts or also in packages games, that first the constructor for the CDO is run and a second time for the actual object?

You right sorry, this answer is from when i still didn’t fully understood it and thought that engine just do some memory copy that avoids constructor, i corrected my answer also extend it with latest docs. But most things i said didnt change. CDO is created in packaged games too, it’s part of reflection system which sits in core of engine, so it’s everywhere.

Since this was necro’d, I have to ask. Is it possible yet to get CDO in blueprints, or do I have to do it through C++ still? We talked about having it in blueprint functionality about 4 years ago, so I thought I would check if I was a madman for not being able to find it.

yes you can from class (purple) :wink:

Where are the CDO’s values stored? In what specific file?

they are not stored as CDO is generated every time the engine starts

1 Like

I found an odd behaviour when hitting the “Compile” button for a blueprint.
seems that the actor spawned in the blueprint viewport is reset by copying the CDO values on it.
this leads to an issue that is bothering me since a couple days: the AttachedChildren array of my root scene component, that before compiling wasn’t empty, now it is still not empty, but contains a null value for each previously contained component.
the components were spawned dynamically in the postinitializecomponents() method of my actor.
how can i explain this behaviour?
thank you!

In case of C++ class they are generated from constructor, engine simply saves raw version of object once constructed as CDO, engine assumes that object in this state is it’s default state.

In case of blueprint they are stored in blueprint asset and loaded when UClass for blueprint is created, blueprints are based out of C++ classes so it takes CDO of blueprint’s C++ base class and alter it with default properties in blueprint, if you blueprint based out of other blueprint it simply stack the defaults.

I think I was referring to the scenario where one adds “config = Engine, defaultconfig” to the UCLASS definition, adds “config” to uproperties of that class, and expose class in for example Editor’s project settings or custom detail view using GetMutableDefault. That actually causes the default object to save values to disk right, which overrides constructor initialization? …Anyway I found the file at Config\DefaultEngine.ini

@Shadowriver May I ask when is UClass for blueprint created? Can you share some insights of related source code?
If I create a blueprint class, but no one is referring to it, will the UClass for it be created?

Blueprint generated classes (BPGC) are not always loaded at all times. Using FindObject with a blueprint class path might return null if the package is not loaded at this time. If nothing references your uasset, chances are it will never be loaded.

But as long as your uasset package exists you can load it by path using LoadObject (or other more recent sync/async loading techniques), eg :

UClass* MyClass = LoadObject<UClass>(nullptr, TEXT("/Game/Path/BP_Thing.BP_Thing_C");

BPGCs and their CDOs are serialized into uasset packages when you compile blueprint in editor or package the game.
Upon loading package, the Linker resolves dependencies and de-serializes package contents, thus creating the BPGC and CDO contained within.

Objects contained in the package are created in FLinkerLoad::CreateExport, but at this point they are just an empty shell. Their data is deserialized from disk at a later point when FLinkerLoad::Preload is called.

That is of course assuming you are using traditional sync loading. I’m not familiar with the more recent loading techniques but they probably rely on async loader in AsyncLoading.cpp or AsyncLoading2.cpp, which seem to add a whole new layer on top of LinkerLoad.

4 Likes