C ++ push_back进入数组会改变对象的变量

时间:2014-01-30 22:19:56

标签: c++ vector physics

我是C ++的新手,正在用它做一个物理项目。 我遇到了一个问题: 我创建了一个 Photon 类,其成员var名为 energy (float)。 在循环(解析值的txt文件)中,当我创建新的 Photon 时,我会为其指定一个 energy (作为一个字符串 - 我后来转换为浮点数)通过构造函数。 然后我将 photon 推入光子矢量。当 cout 创建 Photon 之后的能量时,它们就像预期的那样,并且正确对应于txt文件。

但是,在循环之后,当我再次循环光子矢量并打印photons[i].energy时,值会奇怪地改变 - 其中一些甚至 {{1 }}

NaN

有什么想法吗?

更新 - 光子课程:

void Event::addPhoton(string data) {
    Photon p(data); // the string data is parsed in constructor
    cout << "the energy: " << p.energy << " \n"; // energies output here are correct
    photons.push_back(p);

    // BUG somewhere here
    cout << "Update: Just added a photon. Current photon energies: ***" << endl;
    for (int i = 0; i < photons.size(); i++) {
        cout << photons[i].energy << ", "; // here, values have changed !!
    }
    cout << endl;
}

4 个答案:

答案 0 :(得分:4)

push_back使用Photon的复制构造函数,此时它似乎什么都不做,因此会导致Photon随机初始化值。

如果您使用的是支持C ++ 11的编译器,则只需将其定义为

即可跳过实现
Photon(const Photon&) = default;
在标题中

,这告诉编译器您需要整个对象的完整副本。如果您没有定义一个,编译器将为您生成一个。但是有可读性的原因会鼓励使用默认值。

在这种特殊情况下,如果您的编译器支持C ++ 11,您可以随时使用emplace_back并在向量中构建对象。

photons.emplace_back(data);

答案 1 :(得分:4)

您只想删除班级Photon::Photon(const Photon& orig) {}的复制构造函数,它应该可以正常工作。

注意:您也可以删除析构函数,因为您可能不需要它。

注意2:默认的复制构造函数(如果删除你的,将自动生成)要求Photon的成员要么可以轻易复制,要么自己拥有复制构​​造函数。

问题

实际上,您的Photon副本构造函数为空。因此,每个基本类型值或POD都是默认初始化的(使用随机值)。

标准说:

C ++ 11,[class.base.init],12.6.2 / 8

  

在非委托构造函数中,如果给定的非静态数据成员未由a指定   mem-initializer-id(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer )并且该实体不是抽象类的虚基类(10.4) ,然后:

     

[...]

     
      
  • 否则,该实体默认初始化
  •   

默认情况下默认值是什么意思?

C ++ 11,8.5 / 11

  

如果没有为对象指定初始化程序,则默认初始化该对象;如果没有执行初始化,具有自动或动态存储持续时间的对象具有不确定的值。

这意味着如果你没有为你的(复制)构造函数提供 member-initializer-list ,如果你没有在(copy)构造函数中指定有意义的值,你最终会有完成(复制)构造函数后的值中的垃圾。

因此,在行photons.push_back(p);中,您将p复制到向量中,您将最终获得垃圾初始化条目。

解决方案

所以我的建议如下:剥离以下函数并设计一个默认构造函数,为成员提供有意义的值。

删除:

Photon::Photon(const Photon& orig) {}
Photon::~Photon() {}

添加:

Photo::Photon (void) : 
  momentum(/*any value required by Momentum constructor*/), 
  energy(0.0) 
{
}

您不需要析构函数,因为您不管理资源。因此,您也不需要提供复制构造函数,因为编译器只会为您复制一个将复制Photon类的每个成员的文件。

使用push_back的示例应该可以使用,但是如果使用C ++ 11编译器,也可以使用emplace_back,因为它只是将其参数“移交”给合适的构造函数。 / p>

而不是

Photon p(data); // the string data is parsed in constructor
photons.push_back(p);

你也可以

photons.emplace_back(data); 

你不会在这里制作任何副本,因为矢量中的新对象将使用数据字符串构建。

关于在C ++ 11中隐藏复制构造函数的注意事项

Photon(const Photon&) = default;

我认为复制构造函数的显式默认值不是更具可读性,但如果您不希望它是公共的但受保护(甚至是私有),您可能希望明确默认它。

默认情况下,隐式构造函数,析构函数和赋值运算符是其类的inline public成员。

答案 2 :(得分:3)

将对象插入向量时,(通常)使用对象的复制构造函数创建对象的副本。您的复制构造函数不执行任何操作,因此复制的光子的energy值保持未初始化状态。要解决此问题,请将Photon的复制构造函数复制到原始光子的energy值上。

Photon::Photon(const Photon& orig): energy(orig.energy) {
    // etc.
}

答案 3 :(得分:2)

您需要向我们提供产生错误的代码。

顺便说一句,能量成员应该漂浮。 如果您的输入源是文本文件,则在传递给Photon构造函数之前,应将能量值解析为浮点数(float或double)。

如果您只想实现成员复制,您甚至可以省略复制构造函数。 C ++编译器将为您生成一个。