1
\$\begingroup\$

I'm making a game with C++ and SDL2. I have a class called "text" and a class called "buttonObj"

The constructor of "text" is a void function that takes an SDL_Color variable.

The first part of buttonObj looks like this:

string textContent;
text buttonText;
text highlighted_buttonText;

//Constructor
buttonObj(string tex, SDL_Color notHighlighted, SDL_Color highlighted) : 
  textContent( tex ), 
  buttonBase( img ), 
  buttonText( notHighlighted ), 
  highlighted_buttonBase( hI ), 
  highlighted_buttonText( highlighted )
{}

Currently, this works, but I don't exactly know WHY it works. With a constructor initializer, the value outside the parenthesis gets assigned to the value within the parenthesis. So, how come an SDL_Color (i.e "notHighlighted") can get assigned to a text variable? (i.e buttonText)

I assume this is because with a class, the variable inside the parenthesis is a parameter of the constructor of that class. So, since the constructor of text takes an SDL_Color variable, the program treats "notHighlighted" as a parameter, rather than the regular one-line initializer. Right?

Speculation Aside, this works. From my console, I can see two "text" instances are constructed, one for "highlighted" and one for "notHighlighted". Once the texts are constructed, they remain as they should and render as they should later on in the code.

However, this isn't what I want... Instead of passing an SDL_Color into the constructor and having buttonObj turn the color into text, (via constructing it) I want to load some text beforehand (elsewhere during runtime) and pass the text into buttonObj directly. Like so:

string textContent;
text buttonText;
text highlighted_buttonText;

//Constructor
buttonObj(string tex, text notHighlighted, text highlighted) : 
  textContent( tex ), 
  buttonBase( img ), 
  buttonText( notHighlighted ), 
  highlighted_buttonBase( hI ), 
  highlighted_buttonText( highlighted )
{}

According to my knowledge of what one line initializers are SUPPOSED to do, "highlighted_buttonText" should become equal to the parameter "highlighted." So "highlighted_buttonText" is set to whatever the parameter "highlighted" is, since they're both of type "text"

However, this doesn't work. In my console, I see that the texts are constructed and then destructed! As if something is calling the destructor... this means that the variables don't work fine later on in the code. (And then I get a bunch of exceptions in the library I'm using for text rendering. It's a text chaching library based on SDL_TTF called SDL_FontCache)

I know this is a hard issue to explain and I might not be describing it well. But please, any advice would be awesome! Weird problems like this irk me...

\$\endgroup\$
2
\$\begingroup\$

With your first chunk of code, you use the initializer list to construct the class variables. So in this case, a copy of notHighlighted is passed to the constructor of buttonText during the construction of the instance of buttonObj. The values are not assigned, they're copied. For trivial objects like SDL_Color, it's not really an issue.

In your second code chunk, you get stuff deleted because the copy constructor of buttonText is called to construct buttonText from notHighlighted. Once the copy is done (i.e. once buttonText is constructed from notHightlighted), the copy (which is notHighlighted) is deleted (i.e. the destructor is called). If notHighlighted had references to stuff allocated by SDL, and the destructor has SDL deallocate that stuff, and the copy constructor of text did not make proper clones of these items, the references pointed by buttonText will be no longer valid.

To avoid that, you have a couple of options:

  • Make sure that text creates appropriate copies of referenced SDL objects when copy-constructed.
  • Use raw pointers and make sure you know when you should delete the text. (This is doable but requires great care to avoid memory leaks.)

    string textContent;
    text* buttonText;
    text* highlighted_buttonText;
    
    //Constructor
    buttonObj(string tex, text* notHighlighted, text* highlighted) : 
      textContent( tex ), 
      buttonBase( img ), 
      buttonText( notHighlighted ), 
      highlighted_buttonBase( hI ), 
      highlighted_buttonText( highlighted )
    {}
    
  • Use shared pointers so that the object is properly deleted when no longer in use. (This is one of the favoured methods.)

    string textContent;
    std::shared_ptr<text> buttonText;
    std::shared_ptr<text> highlighted_buttonText;
    
    //Constructor
    buttonObj(string tex, std::shared_ptr<text> notHighlighted, std::shared_ptr<text> highlighted) : 
      textContent( tex ), 
      buttonBase( img ), 
      buttonText( notHighlighted ), 
      highlighted_buttonBase( hI ), 
      highlighted_buttonText( highlighted )
    {}
    

I see that the texts are constructed and then destructed

Instead of just spamming the console with "text::ctor" and "text::dtor" when constructed and destructed, also output the address of the object (i.e. the this pointer) you'll see a bit more what object is actually being constructed and destructed.

And as always, even if outputing data to the console is one of the best way to debug, a good step-by-step debugger is very nice to have and use in such cases.

\$\endgroup\$
  • \$\begingroup\$ Hmmmm.. okay, I'm going to go with the shared_ptr method. So I have other functions within the "text" class, for example text::draw(). When I changed text to shared_ptr<text> in the manner you described, these functions no longer worked. (class "std::shared_ptr<text>" has no member "draw") How do I keep the "text" functions working, but still use this method? Thanks so much! \$\endgroup\$ – Eyesight Technology Jan 18 '18 at 20:25
  • \$\begingroup\$ @EyesightTechnology perhaps editing the question and adding the core parts of the class text, and how the buttonObj are used would help give you a better answer on this. \$\endgroup\$ – Vaillancourt Jan 18 '18 at 21:02
  • \$\begingroup\$ buttonObj has another function, which simply does buttonText.draw(). The "draw()" function is a member of the text class. However, the compiler seems to make a distinction between "shared_ptr<text>" and just regular "text", because it claims that "draw()" is not a function in the "shared_ptr<text>" class. "draw()" is a function of the "text" class, but I guess not a function of "shared_ptr<text>". See above comment for exact error message. \$\endgroup\$ – Eyesight Technology Jan 18 '18 at 21:23
  • \$\begingroup\$ Yes, you have to treat shared_ptr<text> as a pointer, so you have to use buttonText->draw() instead of buttonText.draw(). \$\endgroup\$ – Vaillancourt Jan 18 '18 at 21:27
  • \$\begingroup\$ Ah yes! Of course! So now I'm trying to pass a "text" into a new "buttonObj" obviously by passing it into the constructor. I realize that this isn't working because I must now pass a "shared_ptr<text>" into the constuctor instead of just a normal "text" But, this doesn't work either, because I don't know how to construct the "shared_ptr<text>" pointer... I need to construct it beforehand, like so: shared_ptr<text> textptr(color2); buttonObj button(textptr); \$\endgroup\$ – Eyesight Technology Jan 18 '18 at 21:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.