从函数内更改对象状态

时间:2013-10-27 15:16:25

标签: c++ pointers reference pass-by-reference

我不得不简化一些代码来提出这个问题。但是,在下面的代码中,我没有声明x作为引用类型这一事实意味着一旦函数退出,我的减量更改将被“遗忘”吗?

解决此问题的最明智的方法是将x声明为AnotherClass& x

void MyClass::myFunc(unordered_map<int, AnotherClass>* dictionary, int z, int y){

    AnotherClass x = dictionary->at(z);

    //Does this change on x get "forgotten" in terms of what dictionary stores
    //once myFunc() has finished, because x is not a reference/pointer type?
    x.changeSomething(y--);
}


class MyClass{
    public:

    private:
        myFunc(unordered_map<int, AnotherClass>* dictionary, int z);
        unordered_map<int, AnotherClass>* dictionary

};

2 个答案:

答案 0 :(得分:3)

正确。 xdictionary元素的副本。您正在对副本应用更改,该副本是函数的本地副本。你应该在呼叫方看不到任何效果。您可以参考,也可以直接对at

的调用结果采取行动
dictionary->at(z).changeSomething(z--);

请注意,这与函数内部的代码无关。

答案 1 :(得分:2)

在编写Thing t = s;时,在Java或C#等语言中,实际上创建的别名t引用内存中与s引用的对象相同的对象。但是,在C ++中,值和别名是严格分开的:

  • Thing t = s;是关于制作s
  • 副本
  • Thing& t = s;是关于创建引用同一对象的别名而不是s(引用)
  • Thing* t = &s;是关于创建引用同一对象的别名而不是s(指针)

引用和指针之间的区别并不重要,重要的是副本和别名之间的区别。

  • 副本的更改是该副本的本地更改
  • 对象通过对别名的更改是该对象的本地对象,并且通过引用该对象的所有别名可见

根据你的例子:

// Fix 1: take dictionary by *reference* and not by *pointer*.
void MyClass::myFunc(std::unordered_map<int, AnotherClass>& dictionary, int z, int y){
    // Fix 2: dictionary.at(z) returns a "AnotherClass&"
    // that is an alias to the element held within the dictionary.
    // Make sure not to accidentally make a copy by using that "&"
    // for the type of x.
    AnotherClass& x = dictionary.at(z);

    // "x" is now a mere alias, the following call is thus applied
    // to the value associated to "z" within "dictionary".
    x.changeSomething(y--);
}

请注意,在这种情况下你可以写dictionary.at(z).changeSomething(y--);;但是有几个缺点:

  • 如果再次使用x多次,则将其命名为更清晰。
  • 如果调用的函数/方法有副作用,则调用次数很重要,需要加以控制。
  • 从性能的角度来看,一遍又一遍地避免不必要的计算同样的事情总是受到欢迎......但不要过于依赖性能;)