Дизайн класса: как вернуть shared_ptr: ссылку или копию

Это сценарий: у меня есть класс с именем Program, который содержит три shared_ptr: вершина, геометрия и фрагментный шейдер. Когда создается объект Shader, он создает шейдер с помощью glCreateShader и также компилирует его.

Деструктор шейдера автоматически вызывает glDeleteShader. Так что проблема в том, что если я сделаю следующее:

  1. Создать шейдерный объект;
  2. Скопируйте это;
  3. Уничтожь копию.

Также оригинальная копия становится недействительной, потому что, когда копия уничтожена, она вызывает glDeleteShader. Я считаю, что это проблема дизайна.

Поэтому я избежал этой проблемы, просто используя указатели. Теперь класс Program содержит шейдеры. Я создал метод, который возвращает shared_ptr объектам вершин, геометрии и фрагментов Shader. Я сомневаюсь: я должен вернуть shared_ptr следующим образом:

const shared_ptr<Shader>& getVertex_shader_ptr() const
{
    return vertex_shader_ptr;
}

Или вот так:

shared_ptr<Shader> getVertex_shader_ptr() const
{
    return vertex_shader_ptr;
}

Я боюсь, что проблема, которую я описал выше, возникает снова: shared_ptr освобождается и делает шейдер OpenGL недействительным.

4 ответа

Решение

Если ваш шейдер не имеет значения NULL, вы должны просто вернуть ссылку на него:

const Shader& getVertex_shader() const
{
    return vertex_shader;
}

Если ваш шейдер имеет значение NULL, но только ваша программа отвечает за его удаление, просто верните указатель:

const Shader* getVertex_shader_ptr() const ...

Семантика общего указателя предназначена для случаев, когда нет четкого владения шейдером. Если это так, то вернуть по значению. Семантика заключается в том, что две части программы заинтересованы в том, чтобы объект не очищался, и им обоим нужен способ сохранить его живым.

shared_ptr<Shader> getVertex_shader_ptr() const ...

В любом случае здесь хорошо.

Это член, поэтому безопасно возвращать ссылку на него, пока вызывающая сторона не удержит его ссылкой, а затем уничтожит Program,

Если вы вернулись по неконстантной ссылке, то вызывающий абонент может позвонить reset на вашем указателе, который будет вызывать glDeleteShader если нет других копий указателя. Тем не менее, он будет вести себя так, как вы хотите - Shader будет уничтожено только тогда, когда больше нет ссылок на него.

Я бы лично вернул shared_ptr по стоимости, но это всего лишь личные предпочтения.

РЕДАКТИРОВАТЬ: Предполагая, что вы имели в виду, что вы беспокоитесь о правильности, когда вы копируете Program (не Shader), об этом тоже не беспокойся; два Programs будет иметь shared_ptrs для того же Shader который не будет уничтожен, пока оба Programс. Это может или не может быть то, что вы хотите (вы можете выделить совершенно отдельный Shader на копии) но это безопасно.

Я бы предпочел вернуть по стоимости. Если вы вернетесь по ссылке, вы можете столкнуться с возможностью повисшей ссылки на shared_ptr, если в какой-то момент экземпляр будет уничтожен, а некоторая переменная все еще будет содержать ссылку на shared_ptr.

Это именно то, чего умные указатели должны были избегать, но их подсчет ссылок работает безопасно только в том случае, если вы не вернете их путем копирования. Это сделает недействительным весь смысл использования shared_ptr здесь.

Не зная о проблемах общего указателя, я нашел этот пост Тиаго Макиейры (предположительно сотрудником Qt/Trolltech) довольно полезным для получения лучшего представления о том, возвращать ли значение или const&: http://lists.trolltech.com/qt-interest/2007-11/thread00209-0.html

Цитируя из этого поста, вот основной аргумент для возврата значения из const функция:

Причина, почему это:

T at(const Key& k) const;

вместо:

const T &at(const Key &k) const;

потому что так везде в Qt. Мы нигде не возвращаем refs-to-const. Чтобы вернуть ref-to-const, нам нужно, чтобы где-то существовала переменная, которая предписывает внутреннюю структуру класса. Возвращая значение, мы можем делать все, что захотим внутри.

Я не уверен, хотя, если это применимо к вашей проблеме... (Все еще учусь сам;))

Другие вопросы по тегам