Возврат константной ссылки на объект вместо копии - PullRequest
69 голосов
/ 25 сентября 2008

Во время рефакторинга некоторого кода я наткнулся на некоторые методы получения, которые возвращают std :: string. Примерно так, например:

class foo
{
private:
    std::string name_;
public:
    std::string name()
    {
        return name_;
    }
};

Конечно, получателю лучше вернуть const std::string&? Текущий метод возвращает копию, которая не так эффективна. Возврат константной ссылки вместо этого вызовет какие-либо проблемы?

Ответы [ 12 ]

55 голосов
/ 25 сентября 2008

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

foo *pFoo = new foo;
const std::string &myName = pFoo->getName();
delete pFoo;
cout << myName;  // error! dangling reference

Однако, поскольку ваша существующая функция возвращает копию, вы не нарушите существующий код.

28 голосов
/ 23 февраля 2011

На самом деле, другой проблемой , в частности с возвратом строки , а не по ссылке, является тот факт, что std::string предоставляет доступ через указатель на внутренний const char* через c_str () метод. Это вызвало у меня многочасовую головную боль отладки. Например, допустим, я хочу получить имя из foo и передать его JNI для использования для создания jstring для последующей передачи в Java, а name() возвращает копию, а не ссылку. Я мог бы написать что-то вроде этого:

foo myFoo = getFoo(); // Get the foo from somewhere.
const char* fooCName = foo.name().c_str(); // Woops!  foo.name() creates a temporary that's destructed as soon as this line executes!
jniEnv->NewStringUTF(fooCName);  // No good, fooCName was released when the temporary was deleted.

Если ваш собеседник будет делать подобные вещи, может быть лучше использовать какой-нибудь умный указатель или константную ссылку, или, по крайней мере, иметь неприятный заголовок комментария с предупреждением над именем foo.name ( ) метод. Я упоминаю JNI, потому что бывшие Java-кодеры могут быть особенно уязвимы для такого типа цепочек методов, которые могут казаться в противном случае безвредными.

17 голосов
/ 26 сентября 2008

Одна проблема для возврата константной ссылки будет, если пользователь закодирует что-то вроде:

const std::string & str = myObject.getSomeString() ;

При возврате std::string временный объект будет оставаться живым и прикрепляться к str, пока str не выйдет из области видимости.

Но что происходит с const std::string &? Я предполагаю, что у нас будет постоянная ссылка на объект, который может умереть, когда его родительский объект освободит его:

MyObject * myObject = new MyObject("My String") ;
const std::string & str = myObject->getSomeString() ;
delete myObject ;
// Use str... which references a destroyed object.

Таким образом, я предпочитаю возврат константной ссылки (потому что, в любом случае, мне просто удобнее посылать ссылку, чем надеяться, что компилятор оптимизирует дополнительные временные), если соблюдается следующий контракт: хотят, чтобы это было за пределами существования моего объекта, они копируют его до уничтожения моего объекта "

10 голосов
/ 26 сентября 2008

Некоторые реализации std :: string совместно используют память с семантикой копирования при записи, поэтому возврат по значению может быть почти таким же эффективным, как возврат по ссылке и , вам не нужно беспокоиться о проблемах жизни (среда выполнения делает это за вас).

Если вы беспокоитесь о производительности, то оцените ее (<= не могу не подчеркнуть это достаточно) !!! Попробуйте оба подхода и измерьте усиление (или его отсутствие). Если кто-то лучше и вам действительно все равно, тогда используйте его. Если нет, то предпочтите побочную стоимость защиты, которую он предлагает, по сравнению с другими проблемами. </p>

Вы знаете, что они говорят о предположениях ...

7 голосов
/ 31 марта 2009

Хорошо, поэтому разница между возвратом копии и возвратом ссылки:

  • Производительность : возврат ссылки может быть или не быть быстрее; это зависит от того, как std::string реализован вашей реализацией компилятора (как указали другие). Но даже если вы возвращаете ссылку, присваивание после вызова функции обычно включает в себя копию, как в std::string name = obj.name();

  • Безопасность : Возврат ссылки может вызвать или не вызвать проблемы (висящая ссылка). Если пользователи вашей функции не знают, что они делают, сохраняя ссылку как ссылку и используя ее после того, как предоставляющий объект выходит из области видимости, тогда возникает проблема.

Если хотите, быстро и безопасно используйте boost :: shared_ptr . Ваш объект может хранить строку как shared_ptr и возвращать shared_ptr. Таким образом, копирование объекта не будет происходить, и это всегда безопасно (если только ваши пользователи не вытащат необработанный указатель с помощью get() и не сделают что-нибудь с ним после того, как ваш объект выйдет из области видимости).

4 голосов
/ 25 сентября 2008

Я бы изменил его, чтобы он возвращал const std :: string &. Вызывающий, вероятно, все равно сделает копию результата, если вы не измените весь вызывающий код, но это не вызовет проблем.

Одна потенциальная морщина возникает, если у вас есть несколько потоков, вызывающих name (). Если вы вернете ссылку, но затем измените базовое значение, то значение вызывающего абонента изменится. Но существующий код в любом случае не выглядит потокобезопасным.

Взгляните на ответ Димы на предмет возможной, но маловероятной проблемы.

3 голосов
/ 25 сентября 2008

Вполне возможно, что вы могли бы что-то сломать, если вызывающий действительно хотел получить копию, потому что они собирались изменить оригинал и хотели сохранить его копию. Однако более вероятно, что он действительно должен просто возвращать константную ссылку.

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

2 голосов
/ 25 сентября 2008

Имеет ли это значение? Как только вы используете современный оптимизирующий компилятор, функции, которые возвращают по значению, не будут включать копию, если они не семантически необходимы для этого.

См. FAQ по C ++ lite по этому вопросу.

1 голос
/ 25 сентября 2008

Шансы довольно хорошие, что типичное использование этой функции не прекратится, если вы перейдете к константной ссылке.

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

0 голосов
/ 20 марта 2017

Возвращение ссылки на член обнажает реализацию класса. Это может помешать изменить класс. Может быть полезно для частных или защищенных методов, если необходима оптимизация. Что должен возвращать получатель C ++

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