我试图访问私有向量成员。我很好奇为什么当我将指针指向返回向量时它不起作用(最后一种情况(
std::vector<double>* ptr3 = &(obj.GetVector());
std::cout << ptr3->at(0) << 'n'; //does not work
但是,当我指向新创建的矢量时,它起作用了。
std::vector<double> vec3(obj.GetVector());
std::cout << vec3[3] << 'n'; //work. get 1.4
std::vector<double>* ptr1 = &vec3;
std::cout << ptr1->at(0) << 'n'; //work. get 1.1
区别在于跳过一行(首先将其分配给向量(
当该方法直接返回指针并将其分配给另一个指针时,它也起作用了。
std::vector<double>* ptr2 = obj.GetVector2();
std::cout << ptr2->at(1) << 'n'; //work. get 1.2
你能教/解释一下我的这种行为吗?谢谢。
上面讨论的案例代码如下所示。
#include <iostream>
#include <vector>
class AClass {
public:
AClass(): vec1({1.1,1.2,1.3,1.4}) {}
std::vector<double> GetVector() {
return vec1;
}
std::vector<double>* GetVector2() {
return &vec1;
}
private:
std::vector<double> vec1;
};
int main() {
AClass obj;
std::vector<double> vec2;
vec2 = obj.GetVector();
std::cout << vec2[2] << 'n'; //work. get 1.3
std::vector<double> vec3(obj.GetVector());
std::cout << vec3[3] << 'n'; //work. get 1.4
std::vector<double>* ptr1 = &vec3;
std::cout << ptr1->at(0) << 'n'; //work. get 1.1
std::vector<double>* ptr2 = obj.GetVector2();
std::cout << ptr2->at(1) << 'n'; //work. get 1.2
std::vector<double>* ptr3 = &(obj.GetVector());
std::cout << ptr3->at(0) << 'n'; //does not work
//get
//"Unhandled exception at 0x760FC54F in access_vector.exe"
//"exception: std::out_of_range at memory location 0x0033FA90
std::system("pause");
}
> 问题是AClass::GetVector()
返回向量vec1
的副本,而不是AClass
类中声明的实际向量vec1
。
所以这行:
std::vector<double>* ptr3 = &(obj.GetVector());
分配临时副本的地址,如果在该行后使用ptr3
,则会导致未定义的行为,因为该副本现已消失。
一种解决方案是返回对向量的引用,而不是副本:
std::vector<double>& GetVector() {
return vec1;
}
这样做的要点是,如果要使用原始向量,请返回对该向量的引用,而不是副本。
std::vector<double> GetVector() {
return vec1;
}
上面的代码片段中实际发生的事情是,您正在返回向量的副本。
然后你尝试这样做:
std::vector<double>* ptr3 = &(obj.GetVector());
让我们试着看看这里发生了什么。
首先,您获得向量的副本:
obj.GetVector()
然后,您将获得副本的地址:
&(obj.GetVector())
然后将此地址分配给ptr3
。然后表达式结束,副本永远不会存储在任何地方(请记住,您只是存储了它的地址,而不是对象本身(。由于它是临时的,因此在表达式的末尾被销毁。
最后,您尝试使用刚刚获得的地址:
ptr3->at(0)
但是由于对象被销毁,地址现在指向垃圾。这就是您收到错误的原因。
AClass::GetVector()
返回向量的副本,因此std::vector<double>* ptr3 = &(obj.GetVector());
调用该函数,该函数创建一个副本作为临时副本,然后获取该临时地址并将其存储在ptr3
中,之后临时被销毁。所以你有一个悬空的指针 - 一个指向不再存在的对象的指针。
直接返回指针是有效的,因为指针直接指向仍然存在的数据成员;将复制的矢量存储到局部然后获取指向它的指针也是有效的,因为同样,你得到的指针指向一个对象的指针,其生存期至少与指针一样长。