指针向量:为什么在外部更改指针不会更改向量元素?

Vector of pointers: Why does changing the pointer externally not change the vector element?

提问人:Tyler D 提问时间:9/17/2020 最后编辑:WaqarTyler D 更新时间:9/17/2020 访问量:1088

问:

请看下面的例子:

#include <iostream>
#include <string>
#include <vector>


class Base {
public:
    virtual void price() const = 0;
};

class D1 : public Base {
public:
    virtual void price() const {
        std::cout << "Price of D1\n";
    }
};

class D2 : public Base {
public:
    virtual void price() const {
        std::cout << "Price of D2\n";
    }
};

int main()
{
    std::vector<Base*> v;
    
    
    Base* b = new D1();
    v.push_back(b);
    v[0]->price(); // "Price of D1\n" - OK!
    
    b = new D2();
    v[0]->price(); // "Price of D1\n"?! Why not "Price of D2\n"
}

一个简单的基类有两个派生类。在我的 我声明了一个包含指向基类的指针的向量,并用指向 的对象的指针填充它。main()D1

当我更改为指向 D2 的指针时,为什么不相应地更改元素?他们不应该指向同一件事吗?bb = new D2();v[0]

C++ 指针

评论

0赞 Scheff's Cat 9/17/2020
指针本身就是一个值。如果将其推送到向量,则向量具有此指针的副本(但不是指针的副本)。因此,您可以在前一个值仍存储在向量中时为其分配一个新值。b
1赞 vahancho 9/17/2020
为了达到预期的结果,您应该修改存储在向量中的指针,即v[0] = new D2;

答:

3赞 Waqar 9/17/2020 #1

当我将 b 更改为指向 D2 的指针时,b = new D2();,为什么不相应地更改元素 v[0]?他们不应该指向同一件事吗?

因为将指针的副本存储在向量中。因此,如果您之后更改为指向其他内容,则不会对v.push_back(b);bbv[0]

您可以简化它并像这样查看它:

int *ptr = new int;
int *ptr_copy = ptr;

*ptr = 2; //both *ptr and *ptr_copy have value: "2"

ptr = new int; //ptr now points to some other memory
*ptr = 5;      //*ptr = 5, but *ptr_copy will still be "2"

现在,如果你真的希望在更改指针时也能反映这些变化,你需要另一个间接级别,即“指针到指针”:

int main()
{
    std::vector<Base**> v; //vector of Base**   
    
    Base* b = new D1();
    v.push_back(&b);
    (*v[0])->price(); // "Price of D1\n" - OK!
    
    b = new D2();
    (*v[0])->price(); // "Price of D2\n"
}

评论

0赞 Tyler D 9/17/2020
谢谢。如果是这样的话,那么我不应该通过说,例如,在我的示例中的什么位置来更改这两个指针吗?我试过了,但也没有用。*b = d22D2 d22;
1赞 Waqar 9/17/2020
是的,这通常有效,但在这里我们处理的是多态性。内存是用 初始化的,所以即使你这样做了,与内存关联的类型也不会改变。所以它不会起作用,因为认为它仍然指向“D1”类型的值。(我不确定,但在这种情况下,这可能是未定义的行为)D1*b = d22b
2赞 Waqar 9/17/2020
@TylerD当然改变的。可以这样想。 然后。这是否意味着现在是浮动?不。它仍然是一个.同样,在您的例子中,值是赋值的,但类型仍然是,因此将调用 'sv[0]int a = 10; float b = 1.;a = b;aintD1v[0]->price()D1price()
1赞 Waqar 9/17/2020
delete释放 指向的内存。因此,如果 和 指向相同的位置(您尚未重新签署它们),则内存将被释放,并且两者都将指向无法访问的内存。bbv[0]
1赞 Waqar 9/17/2020
@TylerD指针只是一个变量,就像其他任何变量一样。它有自己的地址,以及它指向的地址。对指针指向的内存所做的任何更改都将反映到指向该内存位置的其他指针。但是,对指针本身的更改(重新赋值)不会对其他指针产生任何影响。
1赞 lubgr 9/17/2020 #2

当您将指针插入到指针中时,会将该指针复制到容器中。后std::vector

std::vector<Base*> v;

Base* b = new D1();
v.push_back(b);

有两个指向实例的指针:和 。现在,如果你继续D1bvec[0]

b = new D2();

你只覆盖这两个中的一个,这显然是 ,但不是 .因此,访问首先为您提供了对存储在那里的指针的引用。bvec[0]vec[0]

评论

0赞 Tyler D 9/17/2020
如果是这样的话,那么我是否应该通过说,例如,在哪里?*b = d22D2 d22;
1赞 lubgr 9/17/2020
您可以通过以下方式更改存储在容器中的指针 (鉴于您事先有。v[0] = bb = new D2();
0赞 Tyler D 9/17/2020
但是当我说在哪里时会发生什么?为什么这不会改变 v[0]'?*b = d22;D2 d22;band
0赞 lubgr 9/17/2020
假设它更改了这些指针引用的实例。这与更改指针本身不同。随着您访问所指向的实际对象。*b
1赞 john 9/17/2020
@TylerD不会改变任何一个指针,而是会改变两个指针指向的内容,这是完全不同的事情。正是由于未能正确区分指针和指针所指向的内容,这是大多数新手对指针感到困惑的原因。*b = d22
3赞 Aziuth 9/17/2020 #3

我将尝试在较低的层面上解释它。

首先,了解指针本质上只是一个包含值的变量,而值恰好是内存中的地址。
让我们简化一下,假设每个类型都具有相同的大小,并且只有 5 个内存地址(在堆上)。(此外,我将忽略数组也将在堆上分配。
我们现在执行

Base* b = new D1();

假设分配的内存位于地址 3 处。 现在是一个仅保存值 3 的变量。我们的(堆)内存如下所示:b

0: ?
1: ?
2: ?
3: variable of type D1
4: ?

然后我们继续

v.push_back(b);

现在,数组包含一个值为 3 的条目。v

b = new D2();

我们现在分配了内存的新部分,假设在地址 1 处:

0: ?
1: variable of type D2
2: ?
3: variable of type D1
4: ?

b现在存储这个地址,即 b 的值为 1。如果我们看一下,我们还没有改变它。它包含一个值为 3 的条目。v

v[0]->price();

这为我们提供了一个值为 3 的指针,我们取消引用并打印它。根据上面的内存映射,那里存储的是 D1 类型的变量。

这为你澄清了吗?

我还稍微扩展了您的代码,以使用实际地址来演示这一点:
http://www.cpp.sh/3s4b4
(如果您运行该代码,请注意,彼此之后分配的地址往往几乎相似,通常仅相差一位数,因此请仔细观察。例如,我得到了0x1711260和0x1711220。


如果你真的需要做你所期望的,你可以存储一个向量,并将地址存储在向量中:Base**b

std::vector<Base**> v;

Base* b = new D1();
v.push_back(&b);
(*v[0])->price(); 

b = new D2();
(*v[0])->price(); 

http://www.cpp.sh/8c3i3 年实施。但如果你不是绝对需要,我不建议这样做,缺乏可读性和不必要的混淆。(请注意,在此示例中,第一个指针不会被删除,并且在更改后无法访问,因此我们会泄漏内存。b

2赞 john 9/17/2020 #4

试试这个变体

vector<int> v;
int b = 123;
v.push_back(b);
cout << v[0]; // prints 123

b = 456;
cout << v[0]; // still prints 123

希望这种变化不会改变是显而易见的,因为 的值是 的值的副本。但是,与您的代码进行比较,有什么区别?没有,完全一样的情况是复制的,改变不改变。您的案例涉及指针这一事实根本没有区别(在这方面),因为指针没有什么特别之处bv[0]v[0]bv[0]bbv[0]