C ++返回在堆上创建并修改它的对象,不会改变它吗?

时间:2016-04-03 23:27:36

标签: c++ pointers c++14 memory-address

我有以下代码

#include <iostream>
#include <vector>
using namespace std;

struct foo {
    struct bar {
        int foobar;
    };

    vector<bar*> barp;

    bar & insert(int v){
        bar * b = new bar();
        (*b).foobar = v;
        barp.push_back(b);
        return *b;
    }

    void edit(bar & b, int v){
        b.foobar = v;
    }
};

int main() {
    foo f;
    foo::bar b = f.insert(5);
    f.edit(b, 10);
    std::cout << (b.foobar == 10?"true":"false") << std::endl;//now ok
    std::cout << (b.foobar == 5?"true":"false") << std::endl;//now ok
    std::cout << ((*f.barp[0]).foobar == 10?"true":"false") << std::endl;//after edit, still returns false
    std::cout << ((*f.barp[0]).foobar == 5?"true":"false") << std::endl;//after edit, returns true
    return 0;
}

有人可以解释一下,为什么“b.foobar”不会改为10? b是否与f.barp [0]中保存的地址相同?

编辑:感谢您的回答,添加对void edit(bar&amp; b,int v)的引用当然有意义。但是添加对“bar&amp; insert”的引用似乎没有任何改变,因为(* f.barp [0])。foobar在编辑后保持不变,即使insert现在应该返回引用。

4 个答案:

答案 0 :(得分:3)

  

b的地址与f.barp[0]中保存的地址相同吗?

不,它没有。您可以通过插入值按值返回对象,即

bar insert(int v)
^^^

按值返回意味着副本:使用复制构造函数构造新的bar,并丢弃原始的 *

要获得您期望的结果,请从bar&返回insert

bar& insert(int v) {
// ^
}

同样,如果您希望所有函数都在同一个对象上工作,edit应该通过引用获取其参数。

* 如果不是将指针存储在集合中,这也会造成内存泄漏。您的程序也有内存泄漏,但您可以通过删除barp向量中的对象来修复它。

答案 1 :(得分:1)

因为您按参数将参数传递给edit(),而不是通过引用传递。

edit()最终会修改原始对象的副本,然后立即被丢弃。

此外,您还要从insert()返回记录的副本。

您的insert()必须返回指针,而不是新记录的副本。您还必须将指针传递给edit()

答案 2 :(得分:0)

  

b是否与f.barp [0]中保存的地址相同?

不,它没有。 每次您使用bar而没有其他资格作为参数或返回类型,您要求制作副本。在这种情况下,这意味着 b中的变量mainb中的变量edit都是副本。

对于这个玩具示例,您可以使程序按照您的预期执行这些更改:

- bar insert(int v){
+ bar &insert(int v){

- void edit(bar b, int v){
+ void edit(bar &b, int v){

- foo::bar b = f.insert(5);
+ foo::bar &b = f.insert(5);

如果您不明白这是做什么或为什么,请参阅C ++教科书以获得“参考”的解释。

(如果您之前从未见过上述- / +符号,则表示“将标有减号的每一行替换为标有加号的相应行。”)< / p>

答案 3 :(得分:0)

foo::bar b = f.insert(5);

这个b对象与* f.barp [0]不同,因为它是从返回的引用中创建的。 要使其工作,请将此行更改为

foo::bar& b = f.insert(5);

这样您就可以创建对刚刚插入向量的对象的引用。