Лучший способ вернуть объект в С++?
Я довольно noobish, когда дело доходит до С++, что лучший способ вернуть объект? Я исхожу из мира сценариев, где объекты всегда являются ссылками, и я пытаюсь достичь такого же понятия... Я основываю это на Когда пройти по ссылке и когда pass by pointer в С++?, где один пользователь заявил: "Хорошее эмпирическое правило:" Используйте ссылки, когда можете, и указатели, когда вам нужно ".
// basic layer class
class Layer { private: Channel channel; // NEVER NULL };
// return object by pointer
Channel *Layer::getChannel() {
return &channel;
};
// return by reference
Channel& Layer::getChannel() {
return channel;
};
Проблема со второй версией заключается в том, что компилятор примет эту строку:
Channel channel = layer.getChannel(); // creates a copy BAD
когда это должно быть:
Channel &channel = layer.getChannel(); // reference good
Есть ли способ принудительно выполнить вызов второго варианта, чтобы заставить его не создавать новый канал, или это первый вариант в любом случае, даже если он никогда не будет NULL?
Ответы
Ответ 1
Вам нужно настроить сам класс Channel
, чтобы он не был скопирован. Если он скопирован, пользователь может его скопировать, и вы ничего не можете предотвратить.
Если копирование не является значимой операцией, вы можете "отключить" его. Просто определите конструктор копирования (Channel(const Channel&)
), а оператор присваивания (Channel& operator=(const Channel&)
) будет закрытым. Тогда любая попытка копирования класса приведет к ошибке компиляции.
На стороне примечания, как уже упоминалось, С++ - это не языки сценариев, с которыми вы знакомы. Все это не ссылка, и вы только настраиваете себя на мир боли, притворяясь иначе. В С++ принято выделять объекты в стеке и передавать объекты по значению, а не передавать ссылки и указатели.
Ответ 2
Возвращение ссылки (или ссылки на константу) является обычным способом для метода getter, чтобы дать вызывающему пользователю прямой доступ к переменной-члену, поэтому я бы рекомендовал вторую версию getChannel()
.
Если вы хотите запретить вызывающим абонентам создавать несоответствующие копии Channel
, вы можете сделать это, сделав его конструктор копирования закрытым. (Если вы хотите, чтобы все не делало копии, даже Channel
, вы можете объявить конструктор частным, а затем не выполнять его.) Но вы должны сделать это только в том случае, если сделать копию на самом деле будет бессмысленной, например. если класс представляет собой некоторый базовый ресурс, который нельзя скопировать. Не запрещайте копирование только потому, что вы считаете, что вызывающему абоненту не нужно; что решение вызывающего абонента сделать.
Ответ 3
Верните копию самого объекта, когда копирование не дорого для ваших целей, и когда вам не нужно менять оригинал. Это должно быть значение по умолчанию.
Channel Layer::getChannel() { return channel; };
Возврат по ссылке или указателю при дорогостоящем копировании или когда вы захотите изменить значение. Возвращаясь по ссылке, вы можете сделать следующее:
layer.getChannel().clear();
И пусть он действует на канал, который в этом слое.
Возврат указателя аналогичен возврату ссылки, за исключением того, что он дает вам немного больше гибкости, поскольку указатель может вообще не указывать на какой-либо объект. Я часто указываю, когда хочу, чтобы я мог использовать "канал" в другом классе. Я бы сделал
class MyClass
{
// ...
void setChannel(Channel *pC) { m_pChannel = pC; }
private:
Channel * m_pChannel; // pointer to a channel that came from layer
}
Ответ 4
Вы не можете остановить вызывающего абонента для создания нового экземпляра, даже если вы используете версию-указатель-указатель.
Channel* channel = new Channel(*layer.getChannel());
Я знаю, что есть способ достичь этой цели. (Например, делая Canle ctor частным, так что только статическая функция-член или его функции-друзья могут его создать.) Однако я не думаю, что это вопрос вашего вопроса.
Дело в том, что когда вы делаете функцию-член, возвращающую ссылку или указатель, вы предоставляете параметры вызывающего абонента, который он может выбрать , хочет ли он скопировать или ссылаться на нее. Кроме того, вы можете сделать свое намерение более понятным, добавив const
, чтобы сделать его доступным только для чтения.
В вашем случае я бы пошел на reference-return-версию, поскольку канал не может быть нулевым. Если вы не хотите, чтобы они меняли переменную-член, возвращайте ссылку на const. Помните, что нет единственного лучшего способа определить тип возвращаемого значения, поскольку он зависит от того, что вы хотите сказать. Надеюсь, поможет!:)
Ответ 5
Поскольку вы возвращаете ссылку на объект, вы предоставляете пользователям прямого доступа к объекту, и если вы собираетесь это сделать, почему вы делаете объект закрытым? Просто сделайте это общедоступным.
Ответ 6
Самое главное - поддерживать читаемость с помощью кода, который вокруг вас. "Когда в Риме делайте, как делают римляне". это важно. Вы пишете его один раз, но каждый, кто должен поддерживать ваш код, должен его прочитать. Если вдруг ваш код следует различным рекомендациям, чем всем окружающим, это означает, что им нужно сначала выяснить свой стиль, а затем выяснить, что вы делаете...
Один из подходов, который я видел, очень хорошо работает с указателями на то, что вы меняете, и ссылается на ссылки, которые вы не делаете:
class Passenger {
...
};
class Car {
public:
int speed() const { return speed_; }
void set_speed(int speed) { speed_ = speed; }
const Passenger& passenger() const { return pass_;}
Passenger* mutable_passenger() { return &pass_; }
private:
int speed_;
Passenger pass_;
};
Клиенты этого класса могут делать:
const Passenger& pass = car.passenger(); // no copy, but don't need to deal with NULL ptrs.
Другие ответы, предлагающие сделать копию ошибки компиляции, являются хорошими.