简体   繁体   中英

Creating a vector of custom type objects

I am getting some very confusing compilation errors when I try to compile this piece of code I have written. The Idea is to create a object "molecule" which has a vector of type "atoms". Each atom is read from a file containing the x,y,z coordinates of the atom and its Zvalue. Here is an example of my code. I have traced my errors back to the vector, so I am showing all the code associated with it below. The program is kind of large and it was working before without an Atom class (I implemented the vector of atoms to replace a nested array that held the geometry in the molecule class, Im trying to learn how to use vectors).

I initialize the vector as a private member of the molecule class with the name atoms and type Atom,

class Molecule {
private:
     std::vector<Atom> atoms;
     // other declarations to follow
 } 

Then in the constructor for the molecule class I read from a file the number of atoms, and resize my vector atoms to that number

 file.open("geom.dat", ios::in);


 if(file.is_open())
{
    file >> natom;
    atoms.resize (natom);

Then continue reading the same file, adding an atom to the vector at each line of the file.

    while(!file.eof())
    {
        int a;
        double b,c,d;

        file >> a >> b >> c >> d;
        Atom A(a,b,c,d);
        atoms.push_back(A);
    }

The errors that I receive when I compile look like gibberish to me, they are referencing lines of code that I have not written. Here is an example

 In file included from molecule.cpp:3:
 In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/iostream:38:
 In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/ios:216:
 In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__locale:15:
 In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/string:439:
 In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/algorithm:627:
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/memory:1456:36: error: no matching
 constructor for initialization of 'Atom'
            ::new ((void*)__p) _Tp();

Is there a problem with my declaration of the vector or how I filled it. If the vector is not the source of my compilation errors, I can post more code but this is the only party I have changed.

As Requested The Atom class: The header file:

class Atom 
{
private:
    int Zval;
    double x;
    double y;
    double z; 

public:
    Atom(int zv, double xcart, double ycart, double zcart);
    ~Atom();
    int get_Zval();
    double get_x();
    void ch_x(double val);
    double get_y();
    void ch_y(double val);
    double get_z();
    void ch_z(double val);

};

and the Source File for the atom class:

Atom::Atom(int zv, double xcart, double ycart, double zcart)
: Zval(zv), x(xcart),y(ycart), z(zcart)  { }
int Atom::get_Zval(){   return Zval;    }

double Atom::get_x() {  return x;   }

void Atom::ch_x(double val) {   x+= val;    }

double Atom::get_y(){   return y;   }

void Atom::ch_y(double val) {   y+= val;    }

double Atom::get_z() {  return z;   }

void Atom::ch_z(double val) {   z+= val;    }

The compiler is looking for a default constructor in your Atom class, but it has no default constructor. The reason a default constructor is needed is because of this line:

atoms.resize (natom);

resize() adds new elements to the vector if the new size is larger than the current size, and those new elements are default constructed.

What you really want is reserve() instead:

atoms.reserve (natom);

Which simply allocates memory for storing elements but does not actually add any elements. If you use resize() , you end up adding twice as many atoms as are present in your input file - half are default constructed and not related to the file data, the other half are value-constructed from the file data.

Or, just remove the resize() / reserve() altogether and let push_back() reallocate the vector when needed. But since you know ahead of time how many atoms are being added, reserve() is a good thing to use, as the vector will only have to be allocated once (unless you add more atoms after loading the file).

You've apparently resolved the issue you found. Now let me help you resolve an issue you don't realize you have (yet).

I see two fairly serious problems with your current design. First, your Molecule class knows far too much about the internal details of the Atom class (and, particularly, how to read Atom data from a file). Second, your molecule class is reading a molecule in from a file in its ctor, but reading data from a file should really happen in a stream extractor.

I'd structure things more like this:

class Atom { 
    int Zval;
    double x, y, z;
public:
    Atom() : Zval(0), x(0), y(0), z(0) {}

    friend std::istream &operator>>(std::istream &is, Atom &a) { 
        return is >> Zval >> x >> y >> z;
    }
};

class Molecule { 
    std::vector<Atom> atoms;
public:

    friend std::istream &operator>>(std::istream &is, Molecule &m) { 
       int size;
       is >> size;
       atoms.reserve(size);
       std::copy_n(std::istream_iterator<Atom>(is), 
                   size, 
                   std::back_inserter(atoms));
       return is;
    }
};

This way, the details of how to read an Atom from a file stay inside the Atom class. The Molecule class only deals with Atoms to the degree of reading a number of Atoms to read, and then reading that many atoms.

As to how you'd use this, you'd read a molecule something like:

std::ifstream in("Molecule.txt");

Molecule m;

in >> m;

As to what it's doing and why: the extraction operator ( operator>> ) for Molecule extracts the data for a molecule from a file. It does that by reading a count, then reading that number of Atoms from the file. Atom's extraction operator just extracts the data for one Atom from the file, then returns.

In both cases, they follow the standard convention of returning a reference to the istream itself, so calling code can check the stream's state after a call, or string together an arbitrary number of extractions, like file >> a >> b >> c;

According to cplusplus.com , vectors contain copies of items. You need to declare a public copy constructor to allow this, and a default constructor to allow for memory allocation. It looks like it's complaining about the latter.

To round this out, make sure to follow the Rule Of Three

This can be easily done by changing your existing constructor declaration to the following:

//For the default constructor
Atom(int zv=0, double xcart=0, double ycart=0, double zcart=0);

EDIT: The copy constructor looks like the compiler can figure it out. Scratch what I said about that and adding in copy assign & dctor .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM