Дизайн класса: как вернуть 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 недействительным.

1

Решение

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

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

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

const Shader* getVertex_shader_ptr() const ...

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

shared_ptr<Shader> getVertex_shader_ptr() const ...
5

Другие решения

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

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

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

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

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

2

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

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

2

Не зная ничего о лежащих в основе проблемах с указателями общего пользования, я нашел этот пост Тиаго Макиейры (предположительно сотрудника 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, мы требуем, чтобы переменная существовала где-то,
который требует внутренней структуры класса. Возвращая
ценность, мы можем сделать все, что мы хотим внутри.

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

1
По вопросам рекламы [email protected]