指针的矢量:为什么外部更改指针不会更改矢量元素

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

本文关键字:指针 元素 为什么 外部      更新时间:2024-03-29

请查看以下示例:

#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 D1n";
}
};
class D2 : public Base {
public:
virtual void price() const {
std::cout << "Price of D2n";
}
};
int main()
{
std::vector<Base*> v;


Base* b = new D1();
v.push_back(b);
v[0]->price(); // "Price of D1n" - OK!

b = new D2();
v[0]->price(); // "Price of D1n"?! Why not "Price of D2n"
}

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

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

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

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

Base* b = new D1();

假设分配的内存位于地址3。CCD_ 6现在是一个简单地保持值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后无法访问,因此我们会泄漏内存。(

当我将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 D1n" - OK!

b = new D2();
(*v[0])->price(); // "Price of D2n"
}

尝试此变体

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]。您的案例涉及指针这一事实(在这方面(没有任何区别,因为指针没有什么特别之处。

将指针插入指针的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]会首先为您提供对存储在那里的指针的引用。