目前有一个自定义的链表类可以很好地工作。
我实现了一个函数,该函数可以在程序退出时将每个Node的所有内容导出为.txt文件。现在我想导入相同的文件,以便在程序开始时重新填充列表,这就是我遇到一些困难的地方。(>>
和<<
操作符是重载的)并且这个整个链表类是特定于这个程序的,并不是真正意义上的可重用性。
my export function:
void List::exportData(){
Node *currentPtr = Head;
cout<<"Saving Data..."<<endl;
ofstream fileOut("stock_transaction_history.txt");
while (currentPtr != 0){
fileOut<< currentPtr->symbol << " " <<currentPtr->shares<<endl;
currentPtr = currentPtr->next; //iterates down the list
}
}
现在我完全被导入数据特性困住了。如果需要的话,我还有一个函数addToBack((Node *newPtr)
供我使用。
void List::importData(){
Node *currentPtr
ifstream stockIn("stock_transaction_history.txt");
}
stockIn >> currentPtr->symbol >>currentPtr ->shares;//(Node(tempSymbol, num));
currentPtr = currentPtr->next;
stockIn.close();
}
我想我可能不得不通过Node *tempPtr=new Node();
的一些东西来调用addToBack
,或者只是调用我节点的通用data
部分?
这是我的node.h
class Node
{
private:
friend class List;
string symbol;
int shares;
Node *next;
public:
Node()
: next(0)
{
}
建议:
保持指向最后一个节点的指针
这将加快追加,使工作更容易。
创建附加方法。这将在列表的末尾添加一个节点。对于一般插入也很有用。
导入方法使用附加方法。
import方法创建一个新节点,用文件中的数据初始化它,然后调用append方法。
为Node
类实现operator >>(istream&)
将I/O封装到一个中心位置。把关于类内部关系的知识留给类,而不是留给List
。
从Node
中删除friend
声明
替换为get/set方法和link_to()方法。这将放松Node
和List
之间的耦合
非常简单,您可以按照我下面描述的方式读取该文件。
void List::importData(const char *filePath)
{
// open the file.
std::ifstream file(filePath);
// storage for the values read from the stream.
std::string symbol;
int shares;
// here we read the symbol from the file stream, with "file >> symbol". the
// return value of that expression is the stream 'file', so we can chain
// that to reading the share count with "file >> symbol >> shares". notice
// that we use this in a conditional - this is because the stream will
// evaluate to 'true' if there is more data to read, or 'false' otherwise.
while ((file >> symbol >> shares))
{
// create the new node
addToBack(symbol, shares);
}
}
List
/Node
安排的一些潜在改进。我下面给出的忽略了复制赋值和复制构造的问题。事实上,如果你做了这两件事中的任何一件,它都会爆炸——这是一个需要解决的问题。但最终,我建议使用std::list<StockItem>
,其中StockItem
只包含symbol
和share_count
。
#include <fstream>
#include <memory>
// the node class used in the list. note that I have not declared the list
// to be a friend of the node. its needed as the data members are public. if
// you dont want the data to be public (e.g. you want to enforce certain
// operations) make them private, and provide accessor functions. in this case
// StockNode is a struct, making the data members public by default.
struct StockNode
{
std::string mSymbol;
int mShares;
StockNode *mNext;
// custom constructor, which populates the symbol and shares.
StockNode(const std::string& symbol, int shares)
: mSymbol(symbol), mShares(shares), mNext(0)
{
}
};
class StockList
{
// we store the head AND the tail of the list. storing the tail allows for
// fast appends.
StockNode *mHead;
StockNode *mTail;
public:
// we override the default constructor to initialize the head/tail pointers
// to 0 (null).
StockList() : mHead(0), mTail(0)
{
}
// destructor - since we are using raw pointers, we need to manage the
// freeing of the StockNodes ourselfs (again, if we used a
// std::list<StockNode> we could have avoided this.
~StockList()
{
clear();
}
void clear()
{
StockNode *node = mHead;
// while we havent reached the end of the list.
while (node)
{
// find the next element
StockNode *temp = node->mNext;
// free the memory for the current element.
delete node;
// set node to the next element in the list.
node = temp;
}
// reset the pointers
mHead = 0;
mTail = 0;
}
// appends a node to the list. i have called it push_back in line with the
// standard library implementation std::list (which you would normally use
// here, but it looks like this is homework). notice that the parameter
// is not a pointer, but a std::auto_ptr. look up the documentation for it
// to see exactly how it works. its not *required* here, but i use it so
// the interface documents that we are taking ownership of the node.
void push_back(std::auto_ptr<StockNode> stockNode)
{
// notice below the calls to "release", this stops the std::auto_ptr
// managing the memory - so it doesn't free the memory when we still
// need it.
if (mTail)
{
// the tail is set, write the new value.
mTail->mNext = stockNode.release();
mTail = mTail->mNext;
}
else
{
// no tail set means this is the first element, set the head and
// the tail.
mHead = stockNode.release();
mTail = mHead;
}
}
// ... implement other methods for looking up StockNodes, etc...
void exportData(const std::string& filePath) const
{
std::ofstream file(filePath.c_str());
for (StockNode *node = mHead; node; node = node->mNext)
{
// note that i have used 'n' instead of std::endl. this is
// because std::endl prints the 'n' and flushes the stream
// as we are writing to file, i figure it'll be a little quicker
// if it doesnt flush to disk after every line.
file << node->mSymbol << " " << node->mNext << 'n';
}
}
void importData(const std::string& filePath)
{
std::ifstream file(filePath.c_str());
std::string symbol;
int shares;
while ((file >> symbol >> shares))
{
push_back(std::auto_ptr<StockNode>(new StockNode(symbol, shares)));
}
}
};