Where you do the craeteChar() call like in setup() vs somewhere else is not the issue.
The issue is likely what Paul mentioned as every hd44780 "LiquidCrystal" type library I've seen, other than the hd44780 library, has what I consider to be a bug in createChar().
Once you create a character using createChar(), the LCD is in the wrong data mode until you call setcursor(), clear(), home().
Any additional writes done prior to doing one of those calls will not be displayed on the display and will corrupt custom character memory instead.
The hd44780 library does not have this issue.
Here is the details of what/why/how it works:
A hd44780 display has two memories.
Display Data RAM, (DDRAM) and Character Generator RAM (CGRAM)
DDRAM holds the character codes for displaying on the display.
CGRAM holds the custom character data.
The hd44780 has two modes, DDRAM mode, and CGRAM mode.
When in DDRAM mode, all writes go do DDRAM which is what shows up on the display.
When in CGRAM mode, all writes go to CGRAM for custom characters.
The lcd defaults to DDRAM mode.
To enter CGRAM mode, you send a command (set CGRAM address) to tell it where you want to start writing the custom character data. This is what the createChar() function does.
The LCD will remain in CGRAM mode until you issue a Set DDRAM address (which is what setCursor() does), or until it receives a clear display command (which is what clear() does) or a Return Home command (which is what home() does).
All the "LiquidCrystal" libraries out there (except hd44780), don't do anything after they send the custom data so the LCD is still in CGRAM mode when createChar() returns.
If you then call write(), attempting put more characters on the display, it will write the data to the CGRAM instead of DDRAM which not only does not put the characters on the display but corrupts the CGRAM.
The hd44780 library puts the LCD back into DDRAM mode before returning from createChar().
If you are using an LCD device that does not support reading, then the hd44780 library will put the cursor at 0,0 just before returning from createChar() since it can't ask the LCD where the cursor is.
If you have a LCD that supports reading, like an LCD with a PCF8574 backpack, the hd44780 library can ask the LCD where the cursor is and put the cursor back to where it was before createChar() was called.
What is nice about using an LCD with a PCF8574 based backpack with the hd44780 library, is that you can easily change a custom character like an icon. All you have to do is call creatChar() again an it will change on the character on display.
You don't have to set the cursor to the right position, you don't have to write it again.
The createChar() updates the character on display and can you can continue writing to the display where you previously left off.