在std::向量上循环的不同方法以及获取指向其元素的指针的问题
issue with different methods to loop over a std::vector and getting pointer to its elements
让我在前言中说,我不是这里的c++专家,所以如果我想弄清楚的对大多数人来说是微不足道的,我提前道歉。我曾试图在其他帖子中找到解决方案,但这次没有成功。
在文章的最后,你可以找到独立的代码,它再现了我将要详细介绍的两个问题[*]。
我使用的是一个类A
,其中包含一个std::vector<float>
作为成员,可以通过const方法访问它。
- 问题1:矢量填充后,我尝试用两种不同的方式用循环打印出它的内容
环路#1
for(const auto& af : a.vec()){
std::cout << af << " (address=" << &af << ")n";
}
回路#2
for(unsigned int i=0; i<a.vec().size(); ++i){
const auto& af = a.vec().at(i);
std::cout << af << " (address=" << &af << ")n";
}
这两种方法似乎产生了两种不同的结果。循环#1按预期工作。当使用循环#2时,我得到一些向量元素的值现在(打印为)零,但内存地址仍然是正确的。天真地,我期望这两种方法能给出相同的结果。它们不同的原因是什么?
- 问题#2:给定上面的浮动向量
vec
和用户选择的值a
,我试图获得一个指向vec
的元素的指针,该元素的值更接近a
。在下面的代码示例中,有一个例程试图实现这一点。后一个例程实际上返回了正确的指针(我将其保存为const float*
对象),但当取消引用时,打印出的值为零(类似于问题#1的循环#2中发生的情况)。我不明白为什么会发生这种情况,因为原始向量仍然存在,内存地址是正确的且非空的,但当取消引用时,值为零,这与指向的向量元素的值不同
下面我写了一些说明来重现这两个问题。
欢迎对这两个问题出现的原因(和/或以下实施的一般建议)进行深入了解。
感谢
[*]我正在SLC6中用g++4.9.1版本编译下面的代码(test.cc
),执行g++ test.cc -o test -std=c++0x
。
#include <iostream>
#include <vector>
#include <cmath>
#include <limits>
class A {
public:
explicit A() {}
virtual ~A() {}
std::vector<float> a_vec() const { return a_vec_; }
void add_obj(const float f){
a_vec_.push_back(f);
return;
}
private:
std::vector<float> a_vec_;
};
template<typename T>
const T * closest0(const float& f, const std::vector<T>& fvec){
const T* next=0;
float deltarmin = std::numeric_limits<float>::infinity();
for(unsigned int i=0; i<fvec.size(); ++i) {
const T & fi = fvec[i];
const float dr = fabs(fi-f);
if(dr < deltarmin){
deltarmin = dr;
next = &fi;
}
}
return next;
}
int main(int argc, char** argv){
if(argc-1 != 1) return 0;
const float ref(atoi(argv[1]));
////
A a;
a.add_obj(25.);
a.add_obj(35.);
a.add_obj(45.);
a.add_obj(55.);
std::cout << "n*** input vector ***********n";
for(unsigned int i=0; i<a.a_vec().size(); ++i){
std::cout << "a.a_vec().at(" << i << ")=" << a.a_vec().at(i) << " (address=" << &a.a_vec().at(i) << ")n";
}
std::cout << "****************************n";
int tryN(0);
////
++tryN;
std::cout << "n--- loop:(const auto&)" << std::endl;
for(const auto& af : a.a_vec()){
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")n";
}
std::cout << "---------------" << std::endl;
////
++tryN;
std::cout << "n--- loop:(index) + 'const auto& ... at()'n";
for(unsigned int i=0; i<a.a_vec().size(); ++i){
const auto& af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")n";
}
std::cout << "---------------" << std::endl;
////
std::cout << "n---------------nn";
const float* fclo0 = closest0(ref, a.a_vec());
std::cout << " *** f-closest0(" << ref << ")=" << *fclo0 << " (address=" << fclo0 << ")n";
return 0;
}
我得到的输出的一个例子是
$ ./test 33
*** input vector ***********
a.a_vec().at(0)=25 (address=0x1a4f030)
a.a_vec().at(1)=35 (address=0x1a4f034)
a.a_vec().at(2)=45 (address=0x1a4f038)
a.a_vec().at(3)=55 (address=0x1a4f03c)
****************************
--- loop:(const auto&)
#1 25 (address=0x1a4f030)
#1 35 (address=0x1a4f034)
#1 45 (address=0x1a4f038)
#1 55 (address=0x1a4f03c)
---------------
--- loop:(index) + 'const auto& ... at()'
#2 6.05888e-38 (address=0x1a4f030)
#2 0 (address=0x1a4f034)
#2 45 (address=0x1a4f038)
#2 55 (address=0x1a4f03c)
---------------
---------------
*** f-closest0(33)=0 (address=0x1a4f034)
@juancopanza正确地指出a_vec()
是按值返回的,但这并不能完全解释输出。问题是
const auto& af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")n";
第一行将引用绑定到从数据的临时副本返回的值,该值随后超出范围,因此当您再次访问af
时,底层内存已被回收,可能已被覆盖。
const引用没有延长临时对象的生存期的原因是,它是对在表达式中使用临时对象的结果的引用,而不是对临时对象本身的引用。如果你改为
const auto &vec = a.a_vec();
std::cout << "#" << tryN << " " << vec.at(i) << " (address=" << &vec.at(i) << ")n";
那么它应该是可以的。(虽然正确的答案是让a_vec
首先返回向量的const-ref。)
A::a_vec()
按值返回。每次调用它时都会得到一个新的矢量。你可以通过返回一个参考来解决这个问题
const std::vector<float>& a_vec() const { return a_vec_; }
或者将CCD_ 14公开并直接访问。
问题出现在的第二个循环中
for(unsigned int i=0; i<a.a_vec().size(); ++i){
const auto& af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")n";
}
这是因为变量const auto&
的声明。如果要删除"与"符号并按值而不是按引用保存它,它是有效的,所以只需将其声明为const auto
我认为这句话是可疑的:
const auto& af = a.a_vec().at(i);
我认为a.a_vec()返回的std::vector在语句结束后超出了作用域。然而,你仍然可以参考它的一个成员。
您可以通过引用整个向量来修复此问题,如下所示:
const auto & av = a.a_vec();
或者复制元素,如下所示:
const auto af = a.a_vec().at(i);
for(unsigned int i=0; i<a.a_vec().size(); ++i){
const &auto af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")n";
}
对于#2,删除&在auto之前,它将同样工作。您在第二个循环中获得的值不是引用!
const auto af = a.a_vec().at(i);
- C++为构建时间获取QDateTime的可靠方法
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 使用指针从C++中的数组中获取最大值
- 如何获取std::result_of函数的返回类型
- 如何在openssl-ecc中获取十六进制格式的私钥
- 使用Unreal C++获取VR耳机的世界位置/方向
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 从C字符串中获取奇怪的字符串长度
- 为什么我的for循环不能正确获取argv
- 从python中调用C++函数并获取返回值
- 如何获取一个数字的前3位
- 获取字符串的长度并将其分配给数组
- 无法获取菜单选择以运行函数.C++
- 数组长度,为什么从命令行获取时不能使用它?
- 从两个 4x64 位整数数组中获取取模
- 如何获取C++中的输入并将其"split"到列表中?换句话说,取 N 个输入并放入 N 长度的数组中
- (C++) 取字符串的每个字符并将其放入数组中并获取所述数组的大小?
- 从参考中获取的指针可以在定义明确的C 中取无效
- 如何获取点向量并仅抓取这些点的"y"