I'm trying to create a button class, with some text in it.
I encountered an issue with the text inside the button, it simply does not display. After much trouble debugging my code, I managed to isolate the faulty code portion.
(In examples below, I recreated the issue with a minimal code)
someText.h
class someText{
private:
Font _font;
Text _text;
public:
someText();
someText(string);
~someText();
void drawText(RenderWindow&);
};
someText.cpp
someText::someText() {}
someText::someText(string str) {
this->_font.loadFromFile("Manjari-Thin.otf");
this->_text.setFont(this->_font);
this->_text.setString(str);
this->_text.setFillColor(Color::Black);
}
someText::~someText(){};
void someText::drawText(RenderWindow& win) {
win.draw(this->_text);
}
main.cpp
int main()
{
//create a window
RenderWindow window(VideoMode(500, 500), "SFML", Style::Default);
window.setFramerateLimit(10);
string textValue[3] = {"un","dos","tres"};
someText textArray[3];
//create someText objects and store them in an array
for (int i = 0; i < 3; i++) {
someText obj(textValue[i]);
textArray[i] = obj;
}
//main loop
while (window.isOpen())
{
window.clear(Color::White);
//draw text
for (int i = 0; i < 3; i++) {
textArray[i].drawText(window);
}
window.display();
}
return 0;
}
In the main.cpp file, I create several someText objects (each object containing a Font object and a Text object) and store them in an array. Then, at the end of the code, I draw the someText objects from the array.
When I execute the program, a point appear instead of the text (meaning that the Text object is present)
I discovered that by creating a someText object after the loop, the text was written properly.
main.cpp
int main()
{
//create a window
RenderWindow window(VideoMode(500, 500), "SFML", Style::Default);
window.setFramerateLimit(10);
string textValue[3] = {"un","dos","tres"};
someText textArray[3];
//create someText objects and store them in an array
for (int i = 0; i < 3; i++) {
someText obj(textValue[i]);
textArray[i] = obj;
}
// vvv
// vvv added an object here
someText anotherObject("");
// ^^^
// ^^^
//main loop
while (window.isOpen())
{
window.clear(Color::White);
//draw text
for (int i = 0; i < 3; i++) {
textArray[i].drawText(window);
}
window.display();
}
return 0;
}
My guess is : Because the draw method, in main.cpp, take a Text object reference as parameter rather than a Text object, and as I was creating my someText objects in a loop (so the destructor was called at the end of each iteration), so, with no objects pointed by the reference, the draw method would just draw ... points.
I'm not 100% sure my conclusion is fully accurate (or even true), but I'm reaching my limits of C++ comprehension anyway.
So, my question is : How do I fix this, as cleanly as possible ? (As I don't want to have a useless line in my program because ... well it's useless)
You are victim of sf::Text
class behaviour:
It is important to note that the sf::Text instance doesn't copy the font that it uses, it only keeps a reference to it. Thus, a sf::Font must not be destructed while it is used by a sf::Text (i.e. never write a function that uses a local sf::Font instance for creating a text).
taken from sf::Text reference.
When the loop works, at line [1]
for (int i = 0; i < 3; i++) {
someText obj(textValue[i]);
textArray[i] = obj; // [1]
}
obj
has text which refers to font. While assignment, font and text are copied. text in target object will reference to the font of source object which at the end of loop is deleted. As a result you have dangling reference in sf::Text
instance when referencing to sf::Font
.
Instead of relying on defaulted generated assignment operator for someText
class which makes troubles you should define your own version:
someText& operator=(const someText& other) {
this->_font = other._font;
this->_text = other._text; // copies all old properties
this->_text.setFont(this->_font); // [2], update font refernece
return *this;
}
since [2] _text
in target object will reference to its own font instance not to other._font
which will be dangled.
Another solution could be to mark copy assignment operator and copy constructor as delete
functions. And stores someText instances by smart pointers like unique_ptr
/smart_ptr
- you will avoid implementation of copy operations.