#c #class #constructor #sfml
#c #класс #конструктор #sfml
Вопрос:
Итак, у меня есть класс RadianButtonComponent
, и я хочу, чтобы вектор содержал неопределенное количество объектов RadianButtonComponent
. Однако я хочу сделать что-то вроде — myButtons.push_back(RadianButtonComponent(font, "text", ...))
Однако, когда я делаю это, я теряю некоторую информацию. Я знаю, что подобный вызов конструктора в основном приведет к созданию временного объекта. Однако, когда я пытаюсь передать шрифт (который является указателем на адрес) Я теряю этот адрес, даже если указатель передается по ссылке из внешнего источника.
(Чтобы дать некоторый код, который поможет объяснить)
class RadianButtonComponent
{
private:
sf::Font myFont;
sf::Text myText;
/* ... */
public:
RadianButtonComponent();
RadianButtonComponent(sf::Fontamp; font, std::string text, ...)
{
myFont = font;
myText.setFont(myFont);
myText.setString(text);
}
~RadianButtonComponent();
};
int main()
{
sf::Font font;
font.loadFromFile("...");
//No memory lost, font address not removed.
RadianButtonComponent workingButton(font, "Text", ...);
//Memory lost, font address not found.
RadianButtonComponent brokenButton;
brokenButton = RadianButtonComponent(font, "Text", ...);
return 0;
}
Теперь я знаю, что когда конструктор вызывается подобным образом, он в основном создает временный объект класса, однако, если для этого временного объекта BrokenButton
установлен этот временный объект, а его указатель на шрифт установлен на адрес из адреса внешнего источника шрифта, он должен работать (по крайней мере, я так думаю), ноЯ все еще теряю адрес.
Почему это происходит и как это можно исправить? Есть ли что-то еще, что я должен знать о том, что происходит, когда конструктор вызывается подобным образом?
(Я чувствую, что я предоставил достаточно информации, но если вам нужно больше кода, чтобы помочь точно определить проблему, я обновлю его как можно скорее)
Ответ №1:
Из спецификации:
Важно отметить, что
sf::Text
экземпляр не копирует шрифт, который он использует, он только сохраняет ссылку на него. Таким образом, asf::Font
не должен быть уничтожен, пока он используется asf::Text
(т. Е. Никогда не пишите функцию, которая использует локальныйsf::Font
экземпляр для создания текста).
Итак, в конструкторе копирования вы должны скопировать шрифт, а затем сбросить шрифт myText нового объекта.
В качестве альтернативы отключите конструктор копирования и создайте вектор интеллектуальных указателей ваших кнопок.
Комментарии:
1. Разве я не делаю этого в приведенном выше коде? Вот почему я передаю внешний sf::Font, чтобы адрес все еще существовал. Не могли бы вы подробнее рассказать мне?
2. @Uulamock sf::Font хранится в объекте button в виде копии в местоположении A, а sf::Text ссылается на местоположение A. При копировании объекта button вторая кнопка теперь имеет sf::Font в местоположении B, но скопированный sf::Text по-прежнему ссылается на местоположение A. Вы должны
myText.setFont(myFont)
снова вызвать конструктор копирования / оператор присваивания, чтобы обновить его копией НОВОГО объекта sf::Font в местоположении B.3. Это, безусловно, имеет больше смысла, я никогда не узнавал о конструкторах копирования, когда задавал этот вопрос, и просто подумал, что обычный конструктор был только что вызван и что он автоматически устанавливает все переменные в новый. Спасибо за разъяснение. Это очень помогает.
4. @Uulamock Автоматически сгенерированный конструктор копирования копирует каждый элемент из одного в другой. Проблема в том, что он немного тупой и не понимает, что sf::Text должен ссылаться на sf::Font в новом объекте.
Ответ №2:
Вы не следуете «правилу 3». В вашем классе отсутствует определяемый пользователем оператор присваивания и конструктор копирования для обработки этих sf_Font
типов. Поэтому, когда вы делаете это:
RadianButtonComponent brokenButton;
brokenButton = RadianButtonComponent(font, "Text", ...); // assignment operator is called
Тогда все ставки отменяются, когда =
применяется, как вы делаете во второй строке выше. Поскольку ваше объяснение (и объяснение другого ответа) того, что такое an sf_Font
, ваш RadianButtonComponent может быть или не быть безопасно помещен в контейнер, такой как std::vector
. Объект должен иметь правильную семантику копирования заранее.
Другими словами, если эта программа работает некорректно, вам нужно исправить ее, чтобы она работала, прежде чем даже рассматривать возможность размещения ее в векторе:
int main()
{
RadianButtonComponent b1(font, "Text", ...);
RadianButtonComponent b2 = b1;
RadianButtonComponent b3(font, "Text2", ...);
b3 = b1;
}
Эта программа (и любая программа, которая может быть вариантом вышеупомянутой) должна иметь возможность компилироваться и запускаться без каких-либо недостатков (включая очистку памяти).
Если программа не работает, то предложение использовать вектор интеллектуальных указателей является возможным решением вместо a vector<RadianButtonComponent>
.
Комментарии:
1. Это, безусловно, мне очень помогает. Я не знал, что существует конструктор копирования, и думал, что при вызове как такового используется обычный конструктор. Если есть что-то о конструкторах копирования, о чем меня следует предупредить, или что-то еще, дайте мне знать, я начну еще некоторое исследование.
Ответ №3:
Если подумать об этой проблеме подробнее, хранение sf::Font внутри объекта — неправильный подход. Я полагаю, что у вас где-то есть глобальная std::map<std::string, sf::Font>
структура данных, в которой хранятся шрифты с их именем шрифта. (Вы также можете использовать идентификатор int или enum вместо строки). Конструктор кнопки принимает ссылку sf::Font, которая исходит из этой карты, но не копирует ее. Поэтому все объекты sf::Text будут ссылаться на экземпляр sf::Font, постоянно хранящийся на вашей карте.
Комментарии:
1. Это довольно хороший совет, спасибо за совет. Я все еще учусь создавать более чистый код, так что это действительно мне помогает.