Когда возвращать указатель, скаляр и ссылку в C ++? - PullRequest
32 голосов
/ 07 августа 2010

Я перехожу с Java на C ++ и меня немного смущает гибкость языка.Один момент заключается в том, что существует три способа хранения объектов: указатель, ссылка и скаляр (сохранение самого объекта, если я правильно его понимаю).

Я склонен использовать ссылки там, где это возможно, потому что это каккак можно ближе к Java.В некоторых случаях, например, для методов получения производных атрибутов, это невозможно:

MyType &MyClass::getSomeAttribute() {
    MyType t;
    return t;
}

Это не компилируется, поскольку t существует только в области действия getSomeAttribute(), и если я возвращаю ссылку на него, он никуда не укажет, прежде чем клиент сможет его использовать.

Поэтому у меня остается два варианта:

  1. Возвращать указатель
  2. Возвращать скаляр

Возвращение указателя будет выглядеть так:

MyType *MyClass::getSomeAttribute() {
    MyType *t = new MyType;
    return t;
}

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

Альтернативой может быть возвращение самого объекта (скаляр):

MyType MyClass::getSomeAttribute() {
    MyType t;
    return t;
}

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

Какой самый распространенный / элегантный подход для решения этой проблемы?

Ответы [ 5 ]

24 голосов
/ 07 августа 2010

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

Я думаю, что причина, по которой вы редко видите, что люди делают это, заключается в том, что вы смотрите на неправильный код C ++. ;) Большинству людей, пришедших с Java, неудобно делать что-то подобное, поэтому они звонят по номеру new. И затем они получают утечки памяти повсюду, должны проверить на NULL и все другие проблемы, которые могут вызвать. :)

Также стоит отметить, что ссылки на C ++ имеют мало общего с ссылками на Java. Ссылка в Java намного больше похожа на указатель (его можно переустановить или установить в NULL). На самом деле единственные реальные отличия заключаются в том, что указатель может также указывать на значение мусора (если он неинициализирован или указывает на объект, вышедший из области видимости), и что вы можете выполнять арифметику указателя для указателя в массив. Ссылки C ++ - это псевдоним для объекта. Ссылка на Java не ведет себя так.

3 голосов
/ 07 августа 2010

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

2 голосов
/ 07 августа 2010

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

Но современные компиляторы очень хороши, чтобы этого не произошло. Стандарты C ++ прямо заявляют, что компилятору разрешается исключать копии при определенных обстоятельствах. Конкретный экземпляр, который был бы уместен в приведенном вами примере кода, называется «оптимизация возвращаемого значения».

Лично я возвращаюсь по (обычно константной) ссылке, когда возвращаю переменную-член, и возвращаю какой-то объект умного указателя какого-то вида (часто ::std::auto_ptr), когда мне нужно что-то динамически выделить. В противном случае я возвращаю по значению.

У меня также очень часто есть const ссылочные параметры, и это очень часто встречается в C ++. Это способ передать параметр и сказать, что «функция не имеет права касаться этого». В основном параметр только для чтения. Он должен использоваться только для объектов, которые являются более сложными, чем одно целое число или указатель.

Я думаю, что одно большое отличие от Java в том, что const важен и используется очень часто. Научись понимать это и сделай своим другом.

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

0 голосов
/ 08 августа 2010

Точка, касающаяся передачи по значению или ссылке:
Учитывая оптимизацию, предполагая, что функция inline , если ее параметр объявлен как "const DataType objectName", что DataType может быть чем угодно, даже примитивамибудет задействована копия объекта;и если его параметр объявлен как «const DataType & objectName» или «DataType & objectName», то снова DataType может быть чем угодно, даже примитивами, не будет задействовано ни присвоение адреса, ни указатель.В обоих предыдущих случаях входные аргументы используются непосредственно в коде сборки.

Точка, касающаяся ссылок:
Ссылка не всегда является указателем, как, например, если в теле функции есть следующий код,ссылка не является указателем:

int adad=5;
int & reference=adad;  

Точка относительно возврата по значению:
, как упоминали некоторые люди, при использовании хороших компиляторов с возможностью оптимизации возврат по значению любого типа не вызовет дополнительногоcopy.

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

0 голосов
/ 07 августа 2010

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

Пример

 main()
   {

       equity trader;

       isTraderAllowed(trader);

       ....
    }

    bool isTraderAllowed(const equity& trdobj)
    {
             ... // Perform your function routine here.
    }

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

...