Как передавать ссылки по значению в C ++?
-
19-08-2019 - |
Вопрос
Я пытаюсь создать неизменяемый тип (класс) на C ++,
Я сделал это так, что все методы, "также известные как функции-члены", не изменяют объект и вместо этого возвращают новый экземпляр.
Я сталкиваюсь с кучей проблем, но все они вращаются вокруг ссылочных типов в C ++.
Одним из примеров является передача параметров того же типа класса по ссылке:
Imm Imm::someOp( const Imm& p_im ) const
{
...
p_im = p_im.someOtherOp(); //error, p_im is const, can't modify it!
...
}
Ошибка вызвана передачей значения по ссылке.Если бы вместо этого я передавал ссылку по значению, то строка ошибки выше не была бы ошибкой!
Рассмотрим пример Java / C #
class Imm
{
...
Imm someOp( Imm p_im )
{
....
p_im = p_im.someOtherOp(); //ok, you're not modifying the
//original object, just changing the local reference
....
}
....
}
Как я могу сделать что-то подобное на C ++?Я знаю, что могу использовать указатели, но тогда я сталкиваюсь со всей этой неразберихой с управлением памятью.Я не хочу беспокоиться о том, кому принадлежат ссылки на объекты.
В идеале я хотел бы спроектировать класс так, чтобы он был похож на неизменяемые строки в python;вы можете использовать их, даже не замечая и не зная, что они неизменяемы, и они просто ведут себя так, как вы ожидаете;они просто работают.
Редактировать
Конечно, я могу обойти это, передавая по значению или используя временную переменную (именно этим я сейчас и занимаюсь).О чем я спрашиваю, так это о том, "как передавать ссылки по значению в C ++".
Я ожидаю, что ответ будет вращаться вокруг чего-то в STL, в настоящее время я изучаю семейство шаблонов smart_ptr.
Обновить
Спасибо за ответы, я понимаю, что от указателей никуда не деться.(смотрите мой другой вопрос, что на самом деле является продолжением этого)
Решение
В Java и C # вы на самом деле не имеете дело со ссылкой - они больше похожи на дескрипторы или указатели.Ссылка в C ++ на самом деле является другим именем исходного объекта, а не указателем на него (хотя она может быть реализована с помощью указателя).Когда вы присваиваете значение ссылке, вы присваиваете значение самому объекту.Существует путаница в том, что для инициализации ссылки вы можете использовать =
символ, но это инициализация, а не присвоение.
Imm im, im2, im3;
Imm &imr = im; // initialize a reference to im
imr = im2; // assign im2 to imr (changes the value of im as well)
Imm *imp = &im; // initialize a pointer to the address of im
imp = &im3; // assign the address of im3 to imp (im is unnaffected);
(*imp) = im2; // assign im2 to imp (modifies im3 as well).
Если вы специально хотите передать "ссылки по значению", то вы, по сути, запрашиваете противоречие в терминах.Ссылки, по определению, передаются по ссылке.Как указывалось в другом месте, вы можете передать указатель по значению или же прямое значение.Если вы действительно хотите, вы можете сохранить ссылку в классе и передать ее по значению:
struct ImmRef
{
Imm &Ref;
ImmRef(Imm &ref) : Ref(ref) {}
};
Обратите также внимание, что константа, применяемая к ссылке, делает постоянным объект, на который ссылается объект, а не ссылку.Ссылки всегда являются постоянными.
Другие советы
Разве присвоение, по определению, не является постоянной операцией?
Вы выглядите так, будто пытаетесь присвоить что-то постоянной ссылке, что полностью опровергает идею постоянной ссылки.
Я думаю, что, возможно, вы ищете указатель вместо ссылки.
В C ++ это так не работает.
Когда вы передаете ссылку на объект, вы фактически передаете адрес в памяти объекта.Ссылки также не могут быть перенесены на другие объекты, отсюда поговорка C ++ "ссылка - ЭТО объект". Вы должны сделать копию, чтобы изменить ее.Java сделает это за вас за кулисами.C ++, вам просто нужно скопировать его.
Вы не забыли установить метод, который вы вызываете, как const ?
Редактировать:итак, с исправленной константой.
Может быть, вам следует сделать что-то вроде
Imm & tmp = p_im.someOtherOp();
Затем выполните дальнейшие операции с переменной tmp.
Если вы задаете переменную или параметр как const &, вы просто не можете присвоить ей значение.
Проверьте это, чтобы узнать о временном сроке службы http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
Imm Imm::someOp( const Imm& p_im ) const
{
...
//Imm& im = p_im.someOtherOp(); // will *not* work
const Imm& im = p_im.someOtherOp(); // will work, but you get a const reference
...
}
Но вы можете использовать boost::shared_ptr
shared_ptr<Imm> Imm::someOtherOp() const
{
shared_ptr<Imm> ret = new Imm;
...
return ret;
}
shared_ptr<Imm> Imm::someOp(const share_ptr<Imm>& p_im) const
{
shared_ptr<Imm> im = p_im->someOtherOp();
}
Вам нужно создать новую копию вашего входящего аргумента.Вы можете делать то, что хотите, несколькими эквивалентными способами:1) вы могли бы передавать данные по значению:
Imm Imm::someOp( Imm im ) const {
im = im.someOtherOp(); // local im is a copy, original im not modified
return im; // return by value (another copy)
}
или 2) вы можете передать по ссылке и сделать копию явно:
Imm Imm::someOp( const Imm & im ) const {
Imm tmp = im.someOtherOp(); // local tmp is a copy
return tmp; // return by value (another copy)
}
обе формы эквивалентны.
В C ++ есть кое-что получше, чем неизменяемые типы—const
.Один тип может быть изменяемым или нет, в зависимости от ваших потребностей.Тем не менее, существуют полезные шаблоны для работы с копиями с коротким сроком службы (почти):
void f(const X &x) {
// Trivial case: unconditional copy
X x2=transform(x);
// Less trivial: conditional copy
std::optional<X> maybe;
const X &use=need_copy ? maybe.emplace(transform(x)) : x;
use.go(); // whichever, x or *maybe
} // *maybe destroyed iff created, then x2 destroyed
std::unique_ptr
может использоваться аналогичным образом до C ++ 17, хотя затем функция, конечно, может выдавать std::bad_alloc
.