Question about Best Practice Scoping

I found myself confused by the scoping of our struct in this lecture. As I am interpreting the solution shown, we are scoping the struct definition/declaration of FBullCowCount to the global namespace.

First, am I right, or is there some magic here I am not picking up on?

Second, if I am right, would it be better practice to scope FBullCowCount internal to UBullCowCartridge? This was my intuition and how I implemented it before watching the lecture through.

Or, alternatively, is scoping this struct to global ok because we expect no other implementation file or header file to ever #include BullCowCartridge.h and so don’t have to worry about naming conflicts?

If we were worried about naming conflicts, and didn’t want to tie our struct definition to our class, would this be an appropriate place to assign this struct a custom namespace?

For practice I implemented the following three architectures (mostly to prove to myself they were all valid and wouldn’t generate a compile error):

  1. FBullCowCount declared/defined in the header global namespace (outside of the class), and used to instantiate a local variable (same as lecture solution).
  2. FBullCowCount declared in the header, inside class UBullCowCartridge, and defined in the implementation file using scope resolution. Then used to instantiate a local variable for computation.
  3. FBullCowCount declared/defined in the header inside class UBullCowCartridge, then a separate member variable of type FBullCowCount declared inside the header. This member variable was then used in the calculations.

Since all three work, there has to be a “preferred” style in C++, where the struct definition has appropriate scope. My vote is 3, but I would be very interested in other people’s thoughts.

  1. Correct.
  2. Depends on the scope of the project I would say. If this were a library and FBullCowCount is only an implementation detail then I would put it inside the class.
    However this project is the BullCowGame, I don’t think there’s anything wrong with putting it in the global namespace. Maybe you want to have a score board, it would make sense to keep it globally defined.

Out of the three listed only 1 and 2.
3 Doesn’t make sense since it’s only used for that computation and immediately used. The compiler would most likely optimise the variable away and just use registers. It can’t do that if it’s a member variable as it would then be part of the UBullCowCartridge object.
If it was a FString/TArray or anything else that stored a buffer then that might make sense as a member variable as it could then reuse that buffer.

1 Like

Great reply! And very much appreciated.

A few follow up thoughts (not to be argumentative, but to make sure I am understanding the design philosophy correctly).

My reasoning for leaning towards not global scoping the struct was primarily the project architecture itself. It was built with a base Cartridge class our BullCowCartridge inherits from. I was envisioning this project was designed to have multiple child cartridges the player could choose from, in which case our struct is so closely tied to our BullCowCartridge class as to not justify a global scope.

Seem reasonable?

I do however understand your argument for global scope in this tutorial project, since we never follow through with a second game cartridge.

General question: any good references on mem alloc best practices in UE4 game design you could point me to? Your point on the issues with the member variable implementation is an excellent one, and hadn’t occurred to me.

I would not consider that “closely”. I would say that’s loosely coupled. FBullCowCount is related to the class but it’s not dependant on any of its state it could be replaced with TPair<int32. int32> for example.

I would only really use nested types if they are tightly coupled to the class and I would want to hide it even exists.

For example a linked list implementation would look a bit like

template <class T>
class list
    class node
        node* next;
        node* prev;
        T data;
    node* head;
    // ...

Users shouldn’t need to know the node type even exists. It’s purely an implementation detail and serves no other purpose other than implementing the linked list.

Whereas FBullCowCount is not tightly coupled with the UBullCowCartridge class. I could create a separate board that holds the previous guesses and bull cow counts. It only exists to have good names for the members becauselike I said, I could just use TPair instead.

Within the C++ Standard Library, off the top of my head I can only think of iterators being nested classes; and those would also be tightly coupled to the container it is an iterator for.
(though they might not actually be implemented that way see: SCARY)

Well you shouldn’t really be dynamically allocating memory yourself in Unreal so I would guess the best practice is “don’t”. The point I made is just C++ in general.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms