用istream读取文件

时间:2014-08-02 22:04:47

标签: c++ polymorphism istream

我正在开发一个涉及多态和继承的项目。

假设项目中使用的类的层次结构是:

Media ----> Book ----> MediaRegistry 

并且每个类的声明如下:(类已经缩小到我遇到问题的成员)

class Media
{
public:
    Media();
    Media(int id, string content);  
    virtual void input(istream& in) = 0;
    friend istream& operator>>(istream& in, Media& media);

protected:                          
    string _mediaTitle;
    int _id;
};

使用:

istream& operator>>(istream& in, Media& media) 
{
    media.input(in);
    return in;
}

class Book:public Media     // Inherits from abstract base class Media
{
public:
    Book();
    Book(int id, string title, int nrOfPages); // Constructor 
    void input(istream& in);

private:
    int _nrOfPages;
};

使用:

void Book::input(istream& in)
{
    in >> _id >> _mediaTitle >> _nrOfPages;
}

class MediaRegistry
{
public:
    MediaRegistry();
    int addMedia(Media*);       
    int loadMedia();

private:
    static const int MAX = 10;
    //Media* _pMedias[MAX];     // Vector for MAX numbers of Media pointers
    std::vector<Media*> _pMedias;
    int _nrOfMedias;                // _nrOfMedias == MAX when registry is full
    ifstream inFile;
    ofstream outFile;
    string path = ".\\data.txt";
};

使用:

int MediaRegistry::loadMedia()
{
    inFile.open(path.c_str());
    while (!inFile.eof())
    {
        int mediaType = 0;
        inFile >> mediaType;
        if (inFile.eof())
            break;
        Media* media = nullptr;
        media = new Book();
        inFile >> *media;
        addMedia(media);
     }
    inFile.close();
    return 0;
}

现在在data.txt,作为媒体的标题,如果我在单词之间有空格(即&#34; The Clown&#34;而不是例如&#34; the-Clown&#34 ;),程序遇到此成员函数istream& operator>>(istream& in, Media& media)中的问题。但是,我无法理解,我多次调试它来跟踪问题。事实上,我希望我的程序能够获得一个包含空格的字符串,但它现在没有这样做?

2 个答案:

答案 0 :(得分:1)

字符串上的>>输入运算符将只读取一个单词(如果可能)或根本不读取任何内容。所以当你有代码时:

void Book::input(istream& in)
{
    in >> _id >> _mediaTitle >> _nrOfPages;
}

它只需要一个整数,然后是一个字,然后再一个整数。如果您尝试给它以下输入:

  

1234
  小丑
  216

它将首先读取整数1234,然后是单词The,然后它将尝试将Clown解释为整数(将失败)。注意最后216是如何保持未读的。流上的下一个输入操作将从此处开始,这可能会导致意外结果。

如果您的文件设置为明确的行结构(例如,每个&#34;字段&#34;在单独的行上),std::getline(in, line)之类的内容可能会更好:

#include <iostream>     // std::istream
#include <string>       // std::getline
#include <limits>       // std::numeric_limits
using namespace std;

void Book::input(istream& in)
{
    in >> _id;
    // this ignores the rest of the line after the previously read integer
    //  (otherwise getline will just read the rest of the line)
    in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    getline(in, _mediaTitle);

    in >> _nrOfPages;
}

答案 1 :(得分:0)

除了@Frxstrem's answer之外,您的loadMedia()功能还可以使用补丁。有一些事情它做错了或做得不好:

  

1)当可以在构造函数的成员初始化列表中完成时打开文件流。

loadMedia()应该用于从输入文件读取数据到程序,而不是用于执行打开和关闭操作。这可以使用RAII,或在构造函数中初始化并在析构函数中释放的概念来完成。由于文件名已知,我们可以通过成员初始化列表初始化它。

MediaRegisty::MediaRegistry() : inFile{path} { }

因为您正在使用C ++ 11(使用nullptr证明),我们可以使用std::ifstream的构造函数作为参数。std::string。我还使用了统一初始化语法(T{})。有些编译器不支持该语法,因此如果出现错误,只需用括号替换它们。

  

2)在没有可靠地检查失败的情况下执行提取。

!eof()从未打算用作输入条件。输入应在控制通过循环之前完成,以便最小化使用提取失败的结果的可能性(从而避免未定义的行为)。输入流有一个布尔运算符,用于检查流的有效性。如果你以正确的方式使用它,那么在提取之后将会调用它:

for (auto p = std::make_pair(int{}, make_unique<Book>());
     inFile >> p.first >> *p.second; addMedia(media));

我使用std::pair来包含两个变量和一个for循环来放置我们想要执行的语句。 make_unique<Book>()创建std::unique_ptr<Book>int{}0相同。

  

3)动态分配数据而不释放它,有效地导致内存泄漏。

new / new[]的每个实例都需要相应的delete / delete[]。如果您没有释放资源,则会出现内存泄漏。

如果您遇到需要使用动态分配的情况,请考虑使用std::unique_ptrstd::shared_ptr等内存管理容器。这些类通过包含资源并在其析构函数中释放它来利用RAII(当对象超出范围时)。

  

4)不必要地关闭文件。

文件流在超出范围时通过调用析构函数内的close()来关闭文件句柄。仅在需要使用其他文件重新打开时才调用close()

我希望这会有所帮助。这些是我所做的改变:

template<class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) noexcept
{
    return new T{std::forward<Args>(args)...};
}

class MediaRegistry
{
public:
    MediaRegistry();
    int addMedia(std::unique_ptr<Media>);       
    int loadMedia();

private:
    std::vector<std::unique_ptr<Media>> _pMedias;
    std::ifstream inFile;
    ...
};

MediaRegistry::MediaRegistry() : inFile{path} { }

int MediaRegistry::addMedia(std::unique_ptr<Media> p)
{
    _pMedias.push_back(std::move(p));
    return 0; // not clear what's supposed to be returned here so I put 0
}

int MediaRegistry::loadMedia()
{
    for (auto p = std::make_pair(int{}, make_unique<Book>());
         inFile >> p.first >> *p.second; addMedia(p.second));
    return 0;
}