指针向量:为啥在外部更改指针不会更改向量元素?
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 = d22
D2 d22;
在我的示例中来更改两个指针吗?我试过了,但也没有用。
是的,这通常可以工作,但这里我们正在处理多态性。内存使用D1
初始化,因此即使您使用*b = d22
,与内存关联的type 也不会改变。所以它不起作用,因为b
认为它仍然指向“D1”类型的值。 (我不确定,但在这种情况下,这可能是未定义的行为)
@TylerD v[0]
是当然改变了。像这样想。 int a = 10; float b = 1.;
然后a = b;
。这是否意味着现在a
是一个浮点数?不,它仍然是int
。同样,在您的情况下,分配了值,但类型仍然是D1
,因此v[0]->price()
将调用D1
的price()
delete
将 deallocate b
指向的内存。因此,如果b
和v[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
实例的指针:b
和 vec[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
不会改变任何一个指针,它会改变两个指针指向的内容,这是完全不同的事情。大多数新手对指针的困惑是无法正确区分指针和指针所指向的内容。以上是关于指针向量:为啥在外部更改指针不会更改向量元素?的主要内容,如果未能解决你的问题,请参考以下文章