创建自定义类型对象的向量

Creating a vector of custom type objects

本文关键字:向量 对象 类型 自定义 创建      更新时间:2023-10-16

当我试图编译我所写的这段代码时,我得到了一些非常令人困惑的编译错误。这个想法是创造一个对象"分子",它有一个类型为"原子"的向量。每个原子从包含原子的x、y、z坐标及其z值的文件中读取。下面是我的代码示例。我已经把我的错误追溯到向量,所以我在下面展示了与它相关的所有代码。这个程序有点大,而且它以前没有Atom类(我实现了原子向量来取代嵌套数组,该数组在分子类中保存几何形状,我试图学习如何使用向量)。

我将vector初始化为分子类的私有成员,名称为atoms,类型为Atom,

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

然后在分子类的构造函数中,我从文件中读取原子的数目,并将向量原子的大小调整为那个数目

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

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

然后继续读取同一个文件,在文件的每行向vector添加一个原子。

    while(!file.eof())
    {
        int a;
        double b,c,d;
        file >> a >> b >> c >> d;
        Atom A(a,b,c,d);
        atoms.push_back(A);
    }

我在编译时收到的错误对我来说看起来像胡言乱语,它们引用了我没有编写的代码行。下面是一个例子

 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();

是否有问题与我的向量声明或我如何填充它。如果矢量不是我的编译错误的来源,我可以发布更多的代码,但这是我唯一改变的一方。

As Requested The Atom class:头文件:

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);
};

和原子类的源文件:

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;    }

编译器正在您的Atom类中寻找默认构造函数,但它没有默认构造函数。需要默认构造函数的原因是:

atoms.resize (natom);

resize()如果新容器的大小大于当前容器的大小,则向vector中添加新元素,并且这些新元素是默认构造的。

你真正想要的是reserve():

atoms.reserve (natom);

简单地分配内存用于存储元素,但实际上并不添加任何元素。如果使用resize(),最终添加的原子数量是输入文件中原子数量的两倍—一半是默认构造的,与文件数据无关,另一半是根据文件数据值构造的。

或者,完全删除resize()/reserve(),让push_back()在需要时重新分配向量。但是,由于您提前知道要添加多少原子,因此使用reserve()是一个好方法,因为向量只需要分配一次(除非在加载文件后添加更多原子)。

您显然已经解决了您发现的问题。现在让我来帮你解决一个你还没有意识到的问题。

我看到你们目前的设计有两个相当严重的问题。首先,您的Molecule类对Atom类的内部细节了解得太多了(特别是如何从文件中读取Atom数据)。其次,你的分子类正在从它的函数中的文件中读取一个分子,但是从文件中读取数据实际上应该在流提取器中进行。

我会这样组织:

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;
    }
};

这样,如何从文件中读取Atom的细节就留在Atom类中。Molecule类只处理要读取的若干个原子,然后再读取这些原子。

至于如何使用它,你可以这样读一个分子:

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

关于它在做什么和为什么:分子的提取操作符(operator>>)从文件中提取分子的数据。它通过读取一个计数,然后从文件中读取原子的数量来实现这一点。Atom的提取操作符只是从文件中提取一个Atom的数据,然后返回。

在这两种情况下,它们都遵循返回对istream本身的引用的标准约定,因此调用代码可以在调用后检查流的状态,或者将任意数量的提取串在一起,如file >> a >> b >> c;

根据cplusplus.com,向量包含项的副本。您需要声明一个公共复制构造函数来实现这一点,并声明一个默认构造函数来实现内存分配。它似乎在抱怨后者。

要完善这一点,请确保遵循三个规则

这可以很容易地通过修改现有的构造函数声明来实现:
//For the default constructor
Atom(int zv=0, double xcart=0, double ycart=0, double zcart=0);

编辑:复制构造函数看起来编译器可以找出它。划掉我刚才说的,加上copy assign &dctor .