Функция возврата ссылочного типа: как вернуть (необязательный) объект - PullRequest
0 голосов
/ 17 января 2019

У меня есть многопоточное приложение C ++, которое может вызывать из любого потока функцию, подобную следующей, чтобы получить объект из списка / вектора.

class GlobalClass{
public:
     MyObject* GlobalClass::getObject(int index) const
     {
          /* mutex lock & unlock */

          if (m_list.hasValueAt(index))
              return m_list[index];
          else
              return 0;
     }
List<MyObject*> m_list;
};

//Thread function
MyObject* obj = globalClass->getObject(0);
if (!obj) return;
obj->doSomething();

Примечание: здесь необходимо понять некоторые передовые практики, связанные с возвратом функций по ссылке, значению или указателю, поэтому простите некоторый псевдокод или пропущенные объявления (я использую блокировку / разблокировку, GlobalClass - это глобальный синглтон и т.д ...).

Проблема здесь в том, что если MyObject по этому индексу удалено внутри GlobalClass, в определенный момент я использую неверный указатель (obj).

Итак, я думал о возвращении копии объекта:

     MyObject GlobalClass::getObject(int index) const
     {
          /* mutex lock & unlock */

          if (m_list.hasValueAt(index))
              return MyObject(*m_list[index]);
          else
              return MyObject();
     }

Проблема здесь в том, что возвращаемый объект (MyObject) - достаточно большой объект, поэтому возврат копии неэффективен.

Наконец, я хотел бы вернуть ссылку на этот объект (лучше константную ссылку):

     const MyObject& GlobalClass::getObject(int index) const
     {
          /* mutex lock & unlock */

          if (m_list.hasValueAt(index))
              return *m_list[index];
          else{
              MyObject* obj = new MyObject();
              return *obj ;
          }
     }

Учитывая, что мой список не может содержать объект по этому индексу, я ввел утечку памяти.

Как лучше всего с этим справиться? Должен ли я вернуться к возвращению копии, даже если она менее эффективна, или что-то мне не хватает при возврате ссылки?

Ответы [ 3 ]

0 голосов
/ 17 января 2019

Похоже, что ваши проблемы связаны с тем, что ваша программа является многопоточной - другой путь вперед (и для необработанного указателя или справочной версии, возвращающей std::optional: единственный путь вперед, возможно, без полного редизайна), это то, что вам нужно выставить мьютекс за пределы области действия функции, чтобы выполнить то, что вам нужно. Это можно сделать несколькими способами, однако самый простой способ проиллюстрировать это следующим образом:

/*mutex lock*/
const MyObject& obj = globalClass.get(index);
/*do stuff with obj*/
/*mutex unlock*/
0 голосов
/ 17 января 2019

Если вы хотите избежать копирования объекта, есть только два возможных случая:

  1. Запись m_list, возвращаемая getObject, может / может быть удалена другим потоком одновременно. Если вы не скопировали этот объект заранее, вы ничего не сможете сделать в getObject, чтобы предотвратить внезапное зависание ссылки / указателя в другом потоке. Тем не менее, вы можете сделать каждую запись m_list равной std::shared_ptr<MyObject> и вернуть ее напрямую. Управление памятью будет происходить автоматически (но остерегайтесь потенциальных издержек при подсчете ссылок shared_ptr, а также возможности взаимоблокировок).

  2. У вас есть (или добавлен) какой-то механизм, который гарантирует, что объекты могут быть удалены только из m_list, если ни один другой поток в настоящее время не содержит какой-либо указатель / ссылку на них. Это очень сильно зависит от вашего алгоритма, но может, например, можно пометить объекты только для удаления, а затем удалить их позже в синхронном разделе.

0 голосов
/ 17 января 2019

У вас есть несколько вариантов:

  1. Используйте std::shared_ptr, если «Get» передает владение объектом вызывающей стороне. Таким образом, объект не может выйти из области видимости. Конечно, звонящий не знает, когда это произойдет.
  2. Используйте std::weak_ptr. Это имеет то же значение 1., но ptr можно сбросить. В этом случае вызывающая сторона может определить, был ли удален объект.
  3. Используйте std::optional, как предлагается в комментарии, и верните копию или ссылку. Использование ссылочного типа в качестве необязательного аргумента не устраняет проблему удаления объекта, поэтому ссылка также может стать недействительной. Копия позволит избежать этого, но, как сказано, она может быть слишком дорогой.

Читая через строки, вы, кажется, предполагаете, что вызывающий абонент будет использовать указатель сразу после вызова и в течение ограниченного промежутка времени. Итак, 1. и 2. эквивалентны и, кажется, соответствуют вашим потребностям.

См. это введение в интеллектуальные указатели для получения более подробной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...