Вопрос

Я пытаюсь создать неизменяемый тип (класс) на 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top