在std::向量上循环的不同方法以及获取指向其元素的指针的问题

issue with different methods to loop over a std::vector and getting pointer to its elements

本文关键字:取指 获取 元素 问题 指针 方法 向量 std 循环      更新时间:2023-10-16

让我在前言中说,我不是这里的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);