构建一个动态分配的类对象数组

时间:2016-01-27 13:21:34

标签: c++ arrays

首先,如果这个问题对您来说非常容易,我想提前道歉,但我只是初学者。

我已经被困在这个问题大约一个星期了,它变得荒谬,因为它不应该那么难,即使对于像我这样的完全初学者也是如此。

我正在编写一个程序,该程序从文本文件中读取大量有关收据的信息,如姓名,金额,日期等,然后将其打印到屏幕上。很简单吧?好吧,我开始在我的两个类Transaction和TransactionsList中使用静态数组,它工作正常,我正在将文件的内容打印到屏幕上,一行接一行。 现在我需要使用动态数组来做到这一点。

文本文件中的每一行都包含一个日期,类型,名称,总和,朋友的数量以及那些应该被读取的朋友的名称,这些朋友应该被存储为动态数组trans中的Transaction类对象。无论我在这个问题上做了多少理论和谷歌搜索,这都是我无法理解的。我应该在哪里使用重载的分配运算符,复制构造函数以及如何正确调用它们?我已经阅读了这些概念,但我还是不能在我的程序中使用它们。这些问题现在只是在脑子里飞来飞去。

我已经更改了数组的朋友和trans被声明为指针,我理解是正确的。然后我想用“new”为数组分配内存,但是在这里我开始不确定我用new分配的位置,在类的构造函数内部还是在需要它们的函数内部? 我意识到向量是很多这些问题的答案但是我应该告诉你我还没有进入向量,所以我试图在没有向量的情况下解决这个问题。我意识到这可能有点倒退,但我应该能够构建我动态分配的对象数组,并在没有我认为的向量的情况下将其打印出来。我听说它们更实用,但是现在我必须理解这个没有向量概念的任务。 我已经阅读了浅拷贝和深拷贝之间的区别,我得到了理论,但我无法以某种方式实现它。 (我知道,我可能很迟钝)。 这是我到目前为止所得到的:

#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
#include <iomanip>

using namespace std;

class Transaction
 {
  private:
   string date;
   string type;
   string name;
   double sum;
   int nr_friends;
   string *friends;

  public:
   Transaction();
   ~Transaction();
   Transaction &operator = ( const Transaction &t );
   string get_name();
   int get_no_friends();
   double get_sum();
   bool readOneTrans( istream &is );
   void writeOneTrans( ostream &os );
 };


class TransactionsList
 {
 private:
   Transaction *trans;
   int no_Trans;

 public:
   TransactionsList();
   ~TransactionsList();
   void read( istream & is );
   void print( ostream & os );
   void add( Transaction & t );

 };


int main()
{
    ifstream inFile("test.txt");
    Transaction t;
    TransactionsList tl;

   // t.readOneTrans(inFile);  // reading just one line works fine (when uncommented)
   // t.writeOneTrans(cout);  // printing works too just fine


    //tl.read(inFile); // here I want to read all contents of file

    //tl.print(cout); // and here print out them to the screen


return 0;

}


Transaction::Transaction()
    {
        date = "000000";
        type = "transp";
        name = "default";
        sum = 0.0;
        nr_friends = 0;
        friends = NULL;
    }

Transaction::~Transaction()
{
    delete [] friends;
}

Transaction &Transaction::operator = ( const Transaction &t )
{

        if ( this != &t )
        {
            delete[] friends;
            date = t.date;
            type = t.type;
            name = t.name;
            sum = t.sum;
            nr_friends = t.nr_friends;
            friends = new string[nr_friends];

            for ( int i = 0; i < nr_friends; i++ )
            {
                friends[i] = t.friends[i];
        }
    }


    return *this;
}

string Transaction::get_name()
    {
    return name;
}

double Transaction::get_sum()
    {
    return sum;
}

int Transaction::get_no_friends()
    {
        return nr_friends;
    }

bool Transaction::readOneTrans( istream &is )
    {
        is >> date >> type >> name >> sum >> nr_friends;

        friends = new string[nr_friends];

        for (int i = 0; i < nr_friends; i++)
            {
                is >> friends[i];
            }

        return is;
        return !is.eof();
    }

void Transaction::writeOneTrans( ostream &os )
    {
        os << left << setw(10) << date <<
        setw(10) << type << setw(10) << name
        << setw(10) << sum << setw(10)
        << nr_friends;

        for (int i = 0; i < nr_friends; i++)
            {
                os << left << setw(8) << friends[i];
            }

        os << endl;
    }



TransactionsList::TransactionsList()
{
        no_Trans = 1;
        trans = new Transaction[no_Trans];

}

TransactionsList::~TransactionsList()
{
    delete [] trans;
}

void TransactionsList::read( istream & is )
{
            Transaction t;

            while ( t.readOneTrans( is ))
                {

                    add( t );
                }

}

void TransactionsList::print( ostream & os )
{
    Transaction t;

    for (int i = 0; i < no_Trans; i++)
        {
            t = trans[i];
            t.writeOneTrans( os );
        }

    if (os == cout)
        {
            os << "\nNumber of transactions: " << no_Trans << endl;
        }

}

void TransactionsList::add( Transaction & t )
{
   // each time I read a line from the file it is passed in as object t here

   // here I want to add this object t to the dynamic array trans somehow
   // and keep building the array with a new class object every time

    // Probably by overloading assignment operator somehow but how?

        trans[no_Trans] = t;
        no_Trans++;

 // i have no idea what to put here to make it work...

}

正如您所看到的,我想要做的是不断使用类Transaction的不同对象构建动态数组trans,每个实例代表我正在读取的文本文件中的不同行,以便我可以打印出来文件中的所有行到最后都是屏幕。 输出行应如下所示:

  011216    food      John       300       2        Nathan   Julia

现在动态地执行此操作,我意识到我必须复制在方法“add”中传入的对象t的内容,并将其添加到数组trans,并以某种方式不丢失先前t:s的数据代表前面的文本行。这对于我来说很容易做,而数组中的静态数组,因为我刚刚将数组trans中的下一个元素指定为等于当前对象t(在add函数内)。这是我的添加函数在静态数组中的外观:

 void TransactionsList::add( Transaction & t )
{
        trans[no_Trans] = t;
        no_Trans++;
}

显然,当您使用动态分配的内存时,这不起作用。我读了一些关于这个的理论,我理解在运行时不能改变数组的大小,因此实际上必须删除数组,然后将其分配为更大的数组,并使用深层副本复制旧内容,只需复制动态数组的内存地址,但使用olds内容创建一个新数组。

正如你所看到的,我已经阅读了很多理论,但并没有真正理解它...... 有人可以帮忙吗?我会非常感激,因为我在一周内没有学到任何东西,现在这真的让我很伤心。我现在需要取得进步!

3 个答案:

答案 0 :(得分:1)

有关容器的一些提示:

  1. 请勿使用using namespace std;why?

  2. c ++中的无符号整数大小通常表示为来自std::size_t的{​​{1}}。

  3. 熟悉rule of three / rule of three/four/five

  4. 通常应用于此类的非常有用的习语是:&#39; Resource Acquisition Is Initialization (RAII)&#39;。

  5. 底线:

    1. 管理资源时我们通常需要

      • 一个析构函数
      • 复制构造函数
      • 移动构造函数
      • 副本分配运算符
      • 移动分配操作员
    2. 资源获取应该只在构造函数中发生。

      <cstddef>等函数不应执行单独的资源获取,而是创建适当大小的临时文件并交换/移动内容。

答案 1 :(得分:0)

构造动态分配数组的问题与构造对象本身的问题完全分开。

class TransactionList {

    Transaction *trans;
    size_t trans_size;
    size_t no_Trans;
public:
    TransactionList(size_t initial_size)
         : trans(new Transaction[initial_size]),
           trans_size(initial_size),
           no_Trans(0)
    {
    }

    ~TransactionList()
    {
          delete[] trans;
    }

    // ...
};

那就是它。您现有的add()方法没有什么不同。它仍然以完全相同的方式工作,因为数组实际上只是指向数组中第一个元素的指针,这仍然就是这种情况。

但是,当no_Trans达到实际分配的trans_size时,您确实需要弄清楚要做什么。那将是你的家庭作业。

您可能想要做的是将其更改为Transaction *个对象的数组,并在将每个Transaction添加到数组时动态分配它们。这将需要额外的工作。

答案 2 :(得分:0)

(这个答案不需要额外的知识,只需要对代码进行一点改动)

构造函数中的事情变得奇怪:

no_Trans = 1;
trans = new Transaction[no_Trans];

人们通常会为未来的元素留出一些空间来添加:

max_Trans = 100;
no_Trans = 0;
trans = new Transaction[max_Trans];

并在add()

if (no_Trans >= max_Trans) { // no more space?
    // make a new array that is as twice big as the old one
    max_Trans = 2 * max_Trans;
    Transaction new_trans = new Transaction[max_Trans];

    // copy elements to the new array
    for (int i = 0; i < no_Trans; i++)
        new_trans[i] = trans[i]; 

    // delete the old one and start to use the new one
    delete[] trans;
    trans = new_trans;
}

trans[no_Trans] = t;
no_Trans++;

当然max_Trans也可以是1,并使其增长为1,2,3,4 ...但是每次添加操作都需要new,这是低效的。