C вызов конструктора для передачи нового объекта приводит к отсутствию данных

#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 экземпляр не копирует шрифт, который он использует, он только сохраняет ссылку на него. Таким образом, a sf::Font не должен быть уничтожен, пока он используется a sf::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. Это довольно хороший совет, спасибо за совет. Я все еще учусь создавать более чистый код, так что это действительно мне помогает.