如何在C ++中按值传递引用?

时间:2009-03-16 00:41:26

标签: c++ reference const immutability

我正在尝试用C ++创建一个不可变类型(类),

我这样做是为了让所有方法“aka成员函数”不修改对象并返回一个新实例。

我遇到了很多问题,但它们都围绕着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系列模板。

更新

感谢您的回复,我意识到指针无法逃避。 (参见我的other question,这实际上就是对此的跟进)

7 个答案:

答案 0 :(得分:4)

根据定义,赋值不是常量操作吗?

你看起来像是在尝试为const引用分配一些东西,这完全违背了const引用的想法。

我认为您可能正在寻找指针而不是参考。

答案 1 :(得分:4)

在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) {}
 };

另请注意,应用于引用的const使得引用的对象不变,而不是引用。引用总是const。

答案 2 :(得分:3)

它在C ++中不起作用。

当您传递对象的引用时,实际上是在对象的内存中传递地址。引用也不能重新定位到其他对象,因此C ++格言“引用是对象”。您必须制作副本才能对其进行修改。 Java将在幕后为您完成此操作。 C ++,你只需要复制它。

答案 3 :(得分:1)

您是否忘记将您调用的方法设置为const?

编辑:所以,修复了const。

也许你应该做像

这样的事情
Imm & tmp = p_im.someOtherOp();

然后对tmp变量进行进一步操作。

如果将变量或参数设置为const&你无法分配它。

答案 4 :(得分:0)

您需要制作传入参数的新副本。你可以用几种等价的方式做你想做的事: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)
}

两种形式都是等同的。

答案 5 :(得分:0)

选中此项以了解临时生命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();
}

答案 6 :(得分:0)

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