Подтвердить что ты не робот

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

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

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

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

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

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

  • Возвращает указатель
  • Возвращает скаляр

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

Пример

 main()
   {

       equity trader;

       isTraderAllowed(trader);

       ....
    }

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

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

Ответ 5

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

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

int adad=5;
int & reference=adad;  

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

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