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

Posted

技术标签:

【中文标题】指针向量:为啥在外部更改指针不会更改向量元素?【英文标题】:Vector of pointers: Why does changing the pointer externally not change the vector element?指针向量:为什么在外部更改指针不会更改向量元素? 【发布时间】:2020-09-17 08:53:52 【问题描述】:

请看下面的例子:

#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 对象的指针填充它。

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

【问题讨论】:

指针本身就是一个值。如果将其推送到向量,则向量具有此指针的副本(但不是指针对象)。因此,您可以为 b 分配一个新值,而它的前者仍存储在向量中。 为了达到预期的效果,您应该修改存储在向量中的指针,即v[0] = new D2; 【参考方案1】:

我会尝试在较低的层次上解释它。

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

Base* b = new D1();

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

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

然后我们继续

v.push_back(b);

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

b = new D2();

我们现在分配了一个新的内存部分,比如地址 1:

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

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

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】:

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

因为v.push_back(b); 在您的向量中存储了指针b副本。因此,如果您之后将b 更改为指向其他内容,它对v[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"

【讨论】:

谢谢。如果是这种情况,那么我不应该通过说*b = d22D2 d22; 在我的示例中来更改两个指针吗?我试过了,但也没有用。 是的,这通常可以工作,但这里我们正在处理多态性。内存使用D1 初始化,因此即使您使用*b = d22,与内存关联的type 也不会改变。所以它不起作用,因为b 认为它仍然指向“D1”类型的值。 (我不确定,但在这种情况下,这可能是未定义的行为) @TylerD v[0] 当然改变了。像这样想。 int a = 10; float b = 1.; 然后a = b;。这是否意味着现在a 是一个浮点数?不,它仍然是int。同样,在您的情况下,分配了值,但类型仍然是D1,因此v[0]-&gt;price() 将调用D1price() deletedeallocate b 指向的内存。因此,如果bv[0] 指向同一个位置(您没有重新分配它们),那么内存将被释放并且两者都将指向不可访问的内存。 @TylerD 指针只是一个变量,就像其他变量一样。它有自己的地址和指向的地址。指针指向的内存的任何更改都将反映到指向该内存位置的其他指针。但是对指针本身的更改(重新分配)不会对其他指针产生任何影响。【参考方案3】:

试试这个变化

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

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

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

【讨论】:

【参考方案4】:

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

std::vector<Base*> v;

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

您有 两个 指向 D1 实例的指针:bvec[0]。现在如果你继续

b = new D2();

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

【讨论】:

如果是这种情况,那么我不应该通过说*b = d22 where D2 d22;来更改两个指针吗? 您可以更改存储在容器中的指针,例如v[0] = b(假设你事先有b = new D2(); 但是当我说*b = d22; where D2 d22; 时会发生什么?为什么这不会同时改变band v[0]`? 假设它改变了这些指针引用的实例。这与更改指针本身不同。使用*b,您可以访问被指向的实际对象。 @TylerD *b = d22 不会改变任何一个指针,它会改变两个指针指向的内容,这是完全不同的事情。大多数新手对指针的困惑是无法正确区分指针和指针所指向的内容。

以上是关于指针向量:为啥在外部更改指针不会更改向量元素?的主要内容,如果未能解决你的问题,请参考以下文章

通过指针或引用将映射传递给函数?

为啥需要两个范围 for 循环来更改 C++ 中向量的这些元素?

指针复杂度的排序向量

为啥我指向向量第一个元素的指针会丢失?

C++简单实现vector

如何防止从非常量访问指针中过滤指针向量的函数?