Возвращает ссылку на объект, а не копия



во время рефакторинга некоторого кода я наткнулся на некоторые методы getter, которые возвращают std::string. Что-то вроде этого например:



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


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

571   12  

12 ответов:

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

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

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

вообще-то, другой вопрос конкретно С возвращением строку не по ссылке, это тот факт, что 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.

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

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

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.

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

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

Если вы беспокоитесь о производительности, то критерии (

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

Итак,различия между возвращением копии и возвращением ссылки находятся:

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

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

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

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

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

взгляните на ответ Димы на связанная с этим потенциальная, но маловероятная проблема.

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

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

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

посмотреть C++ lite FAQ об этом.

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

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

зависит от того, что вам нужно сделать. Может быть, вы хотите, чтобы все вызывающие изменить возвращаемое значение без изменения класса. Если вы вернете ссылку const, которая не будет летать.

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

Я обычно возвращаю const& если я не могу. QBziZ дает пример того, где это имеет место. Конечно, QBziZ также утверждает, что std::string имеет семантику копирования при записи, что редко бывает правдой сегодня, поскольку COW включает в себя много накладных расходов в многопоточной среде. Возвращая const & вы возлагаете ответственность на вызывающего абонента, чтобы сделать правильную вещь со строкой на их конце. Но поскольку вы имеете дело с кодом, который уже используется, вы, вероятно, не должны изменять его, если профилирование не показывает, что копирование этой строки вызывает огромные проблемы с производительностью. Затем, если вы решите изменить его, вам нужно будет проверить thouroughly, чтобы убедиться, что вы ничего не сломали. Надеюсь, другие разработчики, с которыми вы работаете, не делают отрывочных вещей, как в ответе Димы.

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

Comments

    Ничего не найдено.