C ++函数导致应用程序崩溃并且无法正常工作

时间:2014-12-05 21:53:56

标签: c++

我的应用程序出现了一个问题,我的PrintAll函数无法正常工作,最终导致我的应用程序崩溃。我的应用程序应该从文件中读取字符串并将它们插入到数组中。问题是它读取不正确并最终导致我的应用程序崩溃。这就是我认为问题所在:

int main()
{
    LoadMovies();

    MovieList *movies = LoadMovies();
    //movies->MovieList::PrintAll();

    //    // test methods for the Movie and MovieList classes
        //PrintAllMoviesMadeInYear(movies, 1984);
        //PrintAllMoviesWithStartLetter(movies, 'B');
        //PrintAllTopNMovies(movies, 5);

    //delete movies;
    return 0;
}

MovieList* LoadMovies()
{
    vector<string> movies;
    ReadMovieFile(movies);
    MovieList ml = MovieList(movies.size());

    string name;
    int year;
    double rating;
    int votes;

    for (int i = 0; i < movies.size(); i++)
    {
        istringstream input_string(movies[i]);
        getline(input_string, name, '\t');
        input_string >> year >> rating >> votes;
        Movie movie (name, year, votes, rating);
        ml.Add(movie);
    }
    ml.PrintAll();
}

完整示例:

/*
 * File: MovieStatsProgram.cpp
 * Author:
 * Date:
 * ===============================================================
 * This is a console app to test the Movie and MovieList classes.
 *
 * TODO:
 *
 * You need to finish the implementation of the loadMovies method
 * to create and initialize the MovieList object.
 *
 * You also need to create three static methods:
 *
 * PrintAllMoviesMadeInYear - it will print all the movies made in a
 * given year once sort in alphabetical order and once sorted by the number
 * of votes with the movie with the most number of votes printed first.
 *
 * PrintAllMoviesWithStartLetter - it will print all the movies started with
 * a given letter sorted in alphabetical order
 *
 * PrintAllTopNMovies - it will display the top N movies based on the number of
 * votes
 */

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <iomanip>
#include <fstream>

using namespace std;

class Movie {
public:
    Movie();
    Movie(string n, int y, int v, double r);
    string get_name();
    void set_name(string n);
    int get_year();
    void set_year(int y);
    int get_votes();
    void set_votes(int v);
    double get_rating();
    void set_rating(double r);
    string PrintMovie();

private:
    string name;
    int year_made;
    int votes;
    double rating;

};

Movie::Movie() {
    name = "null";
    year_made = 0;
    votes = 0;
    rating = 0.0;
}

Movie::Movie(string n, int y, int v, double r) {
    name = n;
    year_made = y;
    votes = v;
    rating = r;
}

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

void Movie::set_name(string n) {
    name = n;
}

int Movie::get_year() {
    return year_made;
}

void Movie::set_year(int y) {
    year_made = y;
}

int Movie::get_votes() {
    return votes;
}

void Movie::set_votes(int v) {
    votes = v;
}

double Movie::get_rating() {
    return rating;
}

void Movie::set_rating(double r) {
    rating = r;
}

string Movie::PrintMovie() {
    cout << fixed << setprecision(1) << rating << "\t\t" << votes << "\t\t" << "(" <<
            year_made << ")" << "\t" << name << endl;
}

class MovieList {
public:
    MovieList(int size);
    ~MovieList();
    int Length();
    bool IsFull();
    void Add(Movie const& m);
    string PrintAll();

private:
    Movie* movies;
    int last_movie_index;
    int movies_size;
    int movie_count = 0;

};

MovieList::MovieList(int size) {
    movies_size = size;
    movies = new Movie[movies_size];
    last_movie_index = -1;
}

MovieList::~MovieList() {
    delete [] movies;
}

int MovieList::Length() {
    return last_movie_index;
}

bool MovieList::IsFull() {
    return last_movie_index == movies_size;
}

void MovieList::Add(Movie const& m)
{
    if (IsFull()) {
        cout << "Cannot add movie, list is full" << endl;
        return;
    }

    ++last_movie_index;
    movies[last_movie_index] = m;
}

string MovieList::PrintAll() {
    for (int i = 0; i < last_movie_index; i++) {
        movies[last_movie_index].Movie::PrintMovie();
        //cout << movies[last_movie_index] << endl;
    }
}

void ReadMovieFile(vector<string> &movies);
MovieList* LoadMovies();

enum MovieSortOrder
{
    BY_YEAR = 0,
    BY_NAME = 1,
    BY_VOTES = 2
};

int main()
{
    LoadMovies();

    MovieList *movies = LoadMovies();
    //movies->MovieList::PrintAll();

    //    // test methods for the Movie and MovieList classes
        //PrintAllMoviesMadeInYear(movies, 1984);
        //PrintAllMoviesWithStartLetter(movies, 'B');
        //PrintAllTopNMovies(movies, 5);

    //delete movies;
    return 0;
}

MovieList* LoadMovies()
{
    vector<string> movies;
    ReadMovieFile(movies);
    MovieList ml = MovieList(movies.size());

    string name;
    int year;
    double rating;
    int votes;

    for (int i = 0; i < movies.size(); i++)
    {
        istringstream input_string(movies[i]);
        getline(input_string, name, '\t');
        input_string >> year >> rating >> votes;
        Movie movie (name, year, votes, rating);
        ml.Add(movie);
    }
    ml.PrintAll();
}

void ReadMovieFile(vector<string> &movies)
{
    ifstream instream;
    instream.open("imdbtop250.txt");
    if (instream.fail())
    {
        cout << "Error opening imdbtop250.txt" << endl;
        exit(1);
    }


    while (!instream.eof())
    {
        string movie;
        getline(instream, movie);
        movies.push_back(movie);
    }

    instream.close();
}

当我在main函数中使用MovieList :: PrintAll时,我的函数崩溃了,当我把它放在LoadMovies函数中时,它会在崩溃之前错误地读取和添加数据。列表的大小为251,应用程序将只读取相同的数据251次。

2 个答案:

答案 0 :(得分:1)

你有两个问题:

1:正如Brad S所说,你的函数什么都不返回。这是禁忌。

MovieList* LoadMovies()
{
    MovieList ml = MovieList(movies.size());
    // Your function returns a pointer to a MovieList, so...
    return &ml;
}

所以,问题#2是你要在你的函数中返回指向你在堆栈上创建的东西的指针。当您尝试在函数之外访问它时,您将遇到未定义的行为。

选项1:

 MovieList* ml = new MovieList( movies.size() );
 return ml;

您现在需要在完成后删除ml。

选项2: 更改你的函数以返回一个非指针...然后你就没有管理内存的麻烦。

编辑:试试这个

int main()
{
    // Don't need this
    // LoadMovies();

    MovieList *movies = LoadMovies();

    // Uncommented this
    delete movies;
return 0;
}

MovieList* LoadMovies()
{
    vector<string> movies;
    ReadMovieFile(movies);
    // CHANGE
    MovieList* ml = new MovieList(movies.size());
    // CHANGE

    string name;
    int year;
    double rating;
    int votes;

    for (int i = 0; i < movies.size(); i++)    
    {
        istringstream input_string(movies[i]);
        getline(input_string, name, '\t');    
        input_string >> year >> rating >> votes;
        Movie movie (name, year, votes, rating);
        ml.Add(movie);
    }
    ml.PrintAll();
    // CHANGE
    return ml;
}

答案 1 :(得分:0)

您的MovieList课程存在根本问题。这就出现了这一点:

MovieList ml = MovieList(movies.size());

您的MovieList类有一个成员,它是指向动态分配的内存的指针。完成此操作后,您必须通过创建用户定义的复制构造函数和赋值运算符来管理复制和赋值。

最简单的解决方法是使用std::vector<Movie>代替Movie *作为MovieList的成员变量。然后复制分配免费提供,您不需要实现更多功能。

但是,如果由于某种原因无法使用std::vector,可以添加以下功能:

class MovieList {
public:
    //...
    MovieList(const MovieList& m);
    MovieList& operator=(MovieList m);
    //...
};

#include <algorithm>
//...    
// copy constructor
MovieList::MovieList(const MoveList& m) {
    movies_size = m.size;
    movie_count = m.movie.count;
    last_movie_index = m.last_movie_index;
    movies = new Movie[movies_size];
    for (int i = 0; i < movies_size; ++i)
       movies[i] = m.movies[i];
}
//...
// assignment operator
MovieList& MovieList::operator=(MoveList m) {
    std::swap(m.movie_size, movie_size);
    std::swap(m.last_movie_index, last_movie_index);
    std::swap(m.movies, movies);
    std::swap(m.movie_count, moviE_count);
    return *this;
}

向您描述此内容的最简单方法不是描述这些内容。最好的方法是使用调试器并在任何这些函数中放置断点并逐步执行代码。当你点击我上面提到的那一行时,你很可能会看到复制构造函数被调用 - 然后你就可以看到它在做什么了。

当您将现有MovieList分配给另一个MovieList时,分配运算符就是调用的函数。它是通过copy/swap成语实现的。这依赖于工作副本构造函数(上面提供)和析构函数(您已在代码中提供)。它的工作原理是创建一个临时的MovieList,并使用临时的MovieList交换当前MovieList的内部。关于如何运作,SO上有很多线索。

至于你需要这些功能的原因是没有上述功能,行:

MovieList ml = MovieList(movies.size());

将创建两个MovieList个对象,一个临时对象,一个非临时对象,但两者的movies指针将指向同一个内存。销毁临时文件时,会调用析构函数,从而删除movies指向的内存。现在你有m1指向已经冒烟的内存。当您尝试使用m1时的坏消息。

上面的用户定义的复制和赋值函数正确地复制了对象,以便为movies获得两个不同的内存分配,这样当调用析构函数时,删除的内存对于该对象是唯一的。

同样,如果您使用std::vector并且放弃必须编写复制ctor /赋值运算符,所有这些都将得到缓解。