Рекомендуемый способ возврата указателя из функции C ++? - PullRequest
4 голосов
/ 21 июня 2011

У меня есть указатель, который передается ряду функций, где одна функция возвращает адрес и назначает этот адрес указателю.После поиска помощи по Stackoverflow метод collect_file_path имеет параметр типа QStringList**.Хотя я понимаю, что происходит, я не сталкивался с этой нотацией ни в одном из моих учебников, плюс он выглядит уродливо.

Я хотел бы получить совет / отзыв о том, как другие программисты реализовали бы то, что я сделал в приведенном ниже коде.Не могли бы вы использовать QStringList** или какой-то другой метод?

Надеюсь, то, что я спрашиваю, имеет смысл.Мой текущий код:

void ThreadWorker::run()
{
  QStringList* file_list;
  collect_file_paths(&file_list);
}

void ThreadWorker::collect_file_paths(QStringList** file_list)
{
  DirectorySearch ds;
  *file_list = ds.get_file_names(_strPath);
}

QStringList* DirectorySearch::get_file_names(QString path)
{
  QStringList *file_names = new QStringList;
  traverse(path, file_names);
  return file_names;
}

Спасибо

Ответы [ 7 ]

3 голосов
/ 21 июня 2011

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

По этим причинам лучше обернуть указатели в интеллектуальный указатель, например std::auto_ptr (unique_ptr в будущем стандарте C ++) или boost::shared_ptr (также доступен как std::shared_ptr, скоро в стандартной библиотеке рядом с вами) , Эти оболочки безопасно позаботятся об удалении завернутого указателя, когда придет время.

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

2 голосов
/ 21 июня 2011

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

void ThreadWorker::run()
{
  QStringList file_list = collect_file_paths();
}

QStringList ThreadWorker::collect_file_paths()
{
  DirectorySearch ds;
  return ds.get_file_names(strPath_);  // you should not use leading underscore in c++
}

QStringList DirectorySearch::get_file_names(QString path)
{
  QStringList file_names;
  traverse(path, &file_names);
  return file_names;
}

http://cpp -next.com / Архив / 2009/08 / хотите-скорость-передача по значению /

Btw. Я думаю, что у Qt Containers есть копия при оптимизации записи, поэтому копия будет дешевой даже без оптимизации компилятора.

Если вы не доверяете (очень распространенной) оптимизации компилятора, такой как RVO (комментарий heishe), и не используете объекты копирования при записи, такие как QStringList, вы должны создать экземпляр в методе run () и передать ссылка на другие функции. Это не так ясно, как моя рекомендованная методика, но все равно безопасно для исключений (по крайней мере, основная гарантия).

void ThreadWorker::run()
{
  QStringList file_list;
  collect_file_paths(file_list);
}

void ThreadWorker::collect_file_paths(QStringList& file_list)
{
  DirectorySearch ds;
  ds.get_file_names(strPath_, file_list);  
}

void DirectorySearch::get_file_names(QString path, QStringList& file_list)
{
  traverse(path, &file_list);
}

Третье решение - вернуть интеллектуальный указатель, такой как std::unique_ptr<QStringList>. Но я не вижу никакой причины для дополнительного динамического выделения в этом примере.

2 голосов
/ 21 июня 2011

Просто верните по значению. Компилятор может RVO, NRVO и вы можете swaptimize.

2 голосов
/ 21 июня 2011

Вы также можете передать ссылку на указатель, что больше в стиле C ++:

void ThreadWorker::collect_file_paths(QStringList*& file_list) <-- see *&
{
}

Вам не нужно передавать адрес file_list сейчас:

collect_file_paths(file_list);  // simply pass it

Но вместо этого я все равно рекомендовал бы следующий подход (который проще):

void ThreadWorker::run()
{
  QStringList* file_list = collect_file_paths();
}

QStringList* ThreadWorker::collect_file_paths()
{
  DirectorySearch ds;  //<---- ds uninitialized
  return ds.get_file_names(_strPath);  // for static function use ClassName::method() style
}
0 голосов
/ 21 июня 2011

Предполагая, что пользователь имеет доступ к заголовку, но не к реализации, он / она не знает, как обращаться с указателем. Если ваша функция является источником (он же назначает указатель с новым), вы должны вернуть auto_ptr. auto_ptr: стандартный, никогда не выбрасывает и специально разработан для этой работы.

Вы можете посмотреть на это http://www.gotw.ca/publications/using_auto_ptr_effectively.htm

0 голосов
/ 21 июня 2011

Если вы выделили память с помощью оператора new, вы можете просто вернуть указатель.Также не забывайте использовать delete для выделенной памяти.Хорошее место для этого обычно было бы в методе деструктора (не в этом случае, так как вы используете только память в методе выполнения).

void ThreadWorker::run()
{
  QStringList* file_list;
  file_list = ds.get_file_names(_strPath);

  //here we do something with file_list
  //...

  //Free the memory. You have to do this if the object pointed by file_list is not
  //used anywhere else.
  delete file_list;
}

QStringList* DirectorySearch::get_file_names(QString path)
{
  QStringList *file_names = new QStringList;
  traverse(path, file_names);
  return file_names;
}
0 голосов
/ 21 июня 2011

Может показаться уродливым, но в некоторых API не так уж и редко передавать двойной указатель.

В вашем случае это кажется ненужным.Почему collect_file_paths не просто возвращает указатель тоже?

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