Когда возвращать указатель, скаляр и ссылку в C ++?

Я перехожу с 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;
}

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

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

33
задан clstaudt 23 April 2012 в 11:38
поделиться

5 ответов

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

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

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

24
ответ дан 27 November 2019 в 19:30
поделиться

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

3
ответ дан 27 November 2019 в 19:30
поделиться

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

Пример

 main()
   {

       equity trader;

       isTraderAllowed(trader);

       ....
    }

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

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

0
ответ дан 27 November 2019 в 19:30
поделиться

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

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

int adad=5;
int & reference=adad;  

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

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

0
ответ дан 27 November 2019 в 19:30
поделиться

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

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

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

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

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

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

2
ответ дан 27 November 2019 в 19:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: