Странная проблема с указателем в C ++ - PullRequest
0 голосов
/ 29 июня 2010

Я столкнулся с ОЧЕНЬ неприятной проблемой с указателем. Я ранее разместил здесь: TOUGH: Работа с глубоко вложенными указателями в C ++

Но этот пост стал слишком длинным и устаревшим, поэтому я решил сделать репост с более подробной информацией.

Вот мой заголовочный файл, который определяет мои типы:

#include <string>
#include <vector>
#include <sstream>
#include <iostream>

#define USE_3D_GEOM
//#define USE_2D GEOM

#define DEBUG

#ifdef USE_3D_GEOM
 #define DIMENSIONS 3
#elif USE_2D_GEOM
 #define DIMENSIONS 2
#else
 #define DIMENSIONS 1
#endif

#ifndef _COMMON_H
#define _COMMON_H

template<class T>
inline T from_string(const std::string& s)
{
     std::istringstream stream (s);
     T t;
     stream >> t;
     return t;
};

template <class T>
inline std::string to_string (const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}

enum e_ensemble_kind 
{
  MICROCANONICAL,
  CANONICAL,
  NVT,
  GRAND_CANONICAL,
  NPT,
  NVE
};

enum e_potential_kind 
{
  HARD_SPHERE,
  SQUARE_WELL,
  LENNARD_JONES
};

enum e_file_types
{
  MC_SIMPLE,
  NAMD,
  GROMACS,
  CHARMM
};

#ifdef USE_3D_GEOM
typedef struct s_coordinates t_coordinates;
#endif

#ifdef USE_2D_GEOM
typedef struct s_coordinates t_coordinates;
#endif

typedef struct s_particle t_particle;

typedef struct s_bond t_bond;

typedef struct s_angle t_angle;


typedef struct s_dihedral t_dihedral;

typedef struct s_molecule t_molecule;

typedef struct s_lj_param t_lj_param;

typedef struct s_bond_param t_bond_param;

typedef struct s_angle_param t_angle_param;

typedef struct s_dih_param t_dih_param;

typedef struct s_lookup_tab t_lookup_tab;

#ifdef USE_3D_GEOM
struct s_coordinates
{
  double x;
  double y;
  double z;

  s_coordinates& operator+(const s_coordinates &to_add)
  {
    x += to_add.x;
    y += to_add.y;
    z += to_add.z;
    return *this;
  }
  s_coordinates& operator-(const s_coordinates &to_subtract)
  {
    x -= to_subtract.x;
    y -= to_subtract.y;
    z -= to_subtract.z;
    return *this;
  }
  s_coordinates& operator=(const s_coordinates &to_assign)
  {
    x = to_assign.x;
    y = to_assign.y;
    z = to_assign.z;
    return *this;
  }
  bool operator==(const s_coordinates &to_assign)
  {

    return x == to_assign.x && y == to_assign.y && z == to_assign.z;
  }
};
#endif

#ifdef USE_2D_GEOM
struct s_coordinates
{
  double x;
  double y;

  s_coordinates& operator+(const s_coordinates &to_add)
  {
    x += to_add.x;
    y += to_add.y;
    return *this;
  }
  s_coordinates& operator-(const s_coordinates &to_subtract)
  {
    x -= to_subtract.x;
    y -= to_subtract.y;
    return *this;
  }
  s_coordinates& operator=(const s_coordinates &to_assign)
  {
    x = to_assign.x;
    y = to_assign.y;
    return *this;
  }
  bool operator==(const s_coordinates &to_assign)
  {

    return x == to_assign.x && y == to_assign.y;
  }
};
#endif

typedef struct s_particle
{
  t_coordinates position;
  double charge;
  double mass;
  std::string name;
  std::vector<t_lj_param>::iterator my_particle_kind_iter;

  s_particle& operator=(const s_particle &to_assign)
  {
    position = to_assign.position;
    charge = to_assign.charge;
    mass = to_assign.mass;
    name = to_assign.name;
    my_particle_kind_iter = to_assign.my_particle_kind_iter;
    return *this;
  }
} t_particle;

struct s_bond
{
  t_particle * particle_1;
  t_particle * particle_2;
  std::vector<t_bond_param>::iterator my_bond_kind_iter;

  s_bond& operator=(const s_bond &to_assign)
  {
    particle_1 = to_assign.particle_1;
    particle_2 = to_assign.particle_2;
    my_bond_kind_iter = to_assign.my_bond_kind_iter;
    return *this;
  }
};

struct s_angle
{
  t_particle * particle_1;
  t_particle * particle_2;
  t_particle * particle_3;
  std::vector<t_angle_param>::iterator my_angle_kind_iter;

  s_angle& operator=(const s_angle &to_assign)
  {
    particle_1 = to_assign.particle_1;
    particle_2 = to_assign.particle_2;
    particle_3 = to_assign.particle_3;
    my_angle_kind_iter = to_assign.my_angle_kind_iter;
    return *this;
  }
};


struct s_dihedral
{
  t_particle * particle_1;
  t_particle * particle_2;
  t_particle * particle_3;
  t_particle * particle_4;
  std::vector<t_dih_param>::iterator my_dih_kind_iter;

  s_dihedral& operator=(const s_dihedral &to_assign)
  {
    particle_1 = to_assign.particle_1;
    particle_2 = to_assign.particle_2;
    particle_3 = to_assign.particle_3;
    particle_4 = to_assign.particle_4;
    my_dih_kind_iter = to_assign.my_dih_kind_iter;
    return *this;
  }
};

struct s_molecule
{
  std::string res_name;
  std::vector<t_particle> my_particles;
  std::vector<t_bond> my_bonds;
  std::vector<t_angle> my_angles;
  std::vector<t_dihedral> my_dihedrals;

  s_molecule& operator=(const s_molecule &to_assign)
  {
    res_name = to_assign.res_name;
    my_particles = to_assign.my_particles;
    my_bonds = to_assign.my_bonds;
    my_angles = to_assign.my_angles;
    my_dihedrals = to_assign.my_dihedrals;
    return *this;
  }
};

struct s_lj_param
{
  double epsilon;
  double sigma;
  std::string atom_kind_name;
};

struct s_bond_param
{
  std::string atom_1;
  std::string atom_2;
  double bond_coeff;
  double default_length;
};

struct s_angle_param
{
  std::string atom_1;
  std::string atom_2; 
  std::string atom_3;
  double angle_coeff;
  double default_angle;
};

struct s_dih_param
{
  std::string atom_1;
  std::string atom_2; 
  std::string atom_3; 
  std::string atom_4;  
  std::vector<double> dih_coeff;
  std::vector<unsigned int> n;
  std::vector<double> delta;
};

struct s_lookup_tab {
  std::string name;
  int code;
};

#endif /*_COMMON_H*/

А вот вызов, который я делаю, чтобы добавить переменную типа t_molecule (см. Заголовок выше для определения t_molecule) к массиву молекул.

    void Molecule_Manager_Main::add_molecule(const t_molecule new_molecule)
{
    std::cout << "TYPE :" << new_molecule.res_name << std::endl; 
    std::cout << "3: BOND PARTICLE 1 : "
      << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].particle_1->name
          << std::endl;  
    std::cout << "3: BOND PARTICLE 2 : "
    << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].particle_2->name
          << std::endl; 
    std::cout << "3: BOND ITER CONST : "
    << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].my_bond_kind_iter->bond_coeff
          << " "
    << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].my_bond_kind_iter->default_length
          << std::endl;
    my_molecules.push_back(new_molecule);
    std::cout << "99: INDEX : " << my_molecules.size()-1 << std::endl;
    std::cout << "TYPE :" << my_molecules[my_molecules.size()-1].res_name << std::endl; 
    std::cout << "4: BOND PARTICLE 1 : "
          << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].particle_1->name
          << std::endl;  
    std::cout << "4: BOND PARTICLE 2 : "
    << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].particle_2->name
          << std::endl; 
    std::cout << "4: BOND ITER CONST : "
    << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].my_bond_kind_iter->bond_coeff
          << " "
    << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].my_bond_kind_iter->default_length
          << std::endl;
  add_performed = true;
}

Это прекрасно работает ... печатается строка с именем resname и печатается информация о последней связи в векторе связей. Затем, как только я добавил все мои молекулы. Я называю это:

t_molecule * Molecule_Manager_Main::get_molecule(unsigned int index)
{
    std::cout << "TYPE :" << my_molecules[index].res_name << std::endl; 
    std::cout << "5: BOND PARTICLE 1 : "
      << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].particle_1->name
          << std::endl;  
    std::cout << "5: BOND PARTICLE 2 : "
    << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].particle_2->name
          << std::endl; 
    std::cout << "5: BOND ITER CONST : "
    << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].my_bond_kind_iter->bond_coeff
          << " "
    << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].my_bond_kind_iter->default_length
          << std::endl;
  return &(my_molecules[index]);
} 

Это сегфолты на линии облигаций.

Я могу сказать по индексам, которые я печатаю на этапе добавления, что я не перезаписываю молекулы, которые нажимаю на вектор (размер растет) ..

Другими словами, кажется, что происходит: Читать подвектор (работает) -> добавить еще элементы в родительский вектор -> перечитать подвектор (seg-faults)

Эти функции являются ЕДИНСТВЕННЫМИ средствами для добавления переменных молекул к вектору, а переменные молекул добавляются только один раз и не изменяются посмертно в моем текущем тесте.

Есть идеи ???? Заранее спасибо !!

Ответы [ 4 ]

1 голос
/ 29 июня 2010

Просто читая, что вы получаете доступ к varaible с именем my_bond_kind_iter.После того, как вы добавите еще несколько элементов в родительский вектор, он изменит свой размер.Это означает (при условии, что у вас нет C ++ 0x контейнеров, поддерживающих rvalue), что дочерний вектор также будет скопирован, что сделает недействительными все существующие указатели и ссылки на него.Поэтому, когда вы пытаетесь получить доступ к этому старому итератору, который теперь полностью недействителен, возникнет ошибка сегментации.Это, конечно, также произойдет, если вы добавите больше в дочерний вектор.

Векторные итераторы небезопасны, вы не можете держать их рядом и обращаться к ним позже, потому что размеры векторов изменяются, что означает перемещение памяти, и это происходит по прихоти реализации.

0 голосов
/ 29 июня 2010

Я буду рекомендовать избегать использования оператора нижнего индекса (p. Ex. My_molecules [index]) при написании кода трассировки (но не ограничиваясь кодом трассировки), предпочитайте функцию-член at ().

0 голосов
/ 29 июня 2010

Я был бы вами, я бы сильно реорганизовал этот код.

Большую часть времени, когда я получаю проблему, подобную вашей (и она становится очень редкой), я рефакторинг кода, пока я не вижу ясно проблемуЗдесь слишком много повторений, которых можно явно избежать.Используйте typedefs, reference, consts, чтобы избежать повторений.

Рефакторинг позволяет вам реорганизовать ваш код и вашу мысль, упростить, сделать проблемы очевидными.Потратьте время, чтобы сделать это, и вы найдете источник проблемы.

Это может быть за пределами этого кода.

(о рефакторинге, я рекомендую прочитать это: http://sourcemaking.com/refactoring)

0 голосов
/ 29 июня 2010

В разных объектах вы храните итераторы для доступа к элементам в некоторых векторах.Эти итераторы становятся недействительными при изменении базового вектора, например, путем добавления новых элементов.Разыменование такого итератора - неопределенное поведение.

Возможно, вы изменяете векторы, для которых вы сохранили итераторы, когда вы добавляете новые молекулы, и использование этих итераторов в дальнейшем приводит к ошибкам сегментации.

...