C++ getline 分段错误,std::vector<custom class>

C++ getline segmentation fault, std::vector<custom class>

我在谷歌搜索时在调试器中查看了一段时间,但我认为我偶然发现了一些我不熟悉的 C++ 行为。我将简要概述一下我在做什么,what/where 问题是什么。我将代码块放在下面。

正在发生的事情的大致轮廓是:

  1. 创建了自定义 class (LogReader) 来处理单个日志文件。
  2. LogReader 包含一个指向 ifstream 的指针 (ifstream *log_file)
  3. ifstream 在构造函数中与 getline() 一起使用,效果很好。
  4. LogReader 被放置在一个向量中。
    • 下面 main.cpp 的代码直接使用了 LogReader(没有向量)。段错误在两种情况下都会发生。
  5. LogReader.advance() 被调用。此函数中使用了 getline()。段错误发生在这里(在 LogReader.cpp 中注释)。

感谢您就我遗漏的可能导致此问题的 C++ 行为提供任何帮助!

编辑:不将 LogReader 放入向量中会消除段错误(现在在其他地方失败,但不是问题)。更改是在 main.cpp

中注释以下行
readers.push_back(&label_reader);

我想现在的问题是为什么使用 std::vector 会导致这个问题。


LogReader.h

#ifndef LOGREADER
#define LOGREADER

using namespace std;

class LogReader {
     private:
         LogReader(){} // private default constructor

     public:
         ifstream *log_file; // file the log is read from
         vector<int> val_locations; // offsets in line for values
         string next_line; // next line from the file

         int current_time; // time for most recent reading
         string current_line;
         int next_time; // what is the next time in the file
         vector<string> current_vals; // what the current vals are

         LogReader(string log_loc, vector<int> offsets); // given a file to start on

         bool advance(int new_time); // advance the log reader, return true if advanced
         bool has_more(); // is there more in the log
 };

 #endif

LogReader.cpp

// c++ imports
#include <boost/algorithm/string.hpp>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

// my imports
#include "LogReader.h"
#include "functions.h"

using namespace std;

LogReader::LogReader(string log_loc, vector<int> offsets){
    // make the file reader
    ifstream lf(log_loc);
    log_file = &lf;
    // pull out the first line
    getline(*log_file, current_line);
    cout << current_line << endl;

    // get the set of current values
    val_locations = offsets;
    for(int i = 0; i < val_locations.size(); i++) {
        current_vals.push_back(get_line_part(current_line, 
                    val_locations.at(i)));
    }
    // get the current time
    current_time = stoi(get_line_part(current_line, 0));

    // pull down the next line
    getline(*log_file, next_line);
    cout << next_line << endl;
    // get the next time
    next_time = stoi(get_line_part(next_line, 0));
}

bool LogReader::advance(int new_time){
    if(new_time < next_time)
        return false; // nothing to do, current still good

    cout << "can check time" << endl;

    // update the time and values
    current_time = next_time;
    current_line = next_line;
    current_vals.clear();

    cout << "can do housekeeping" << endl;

    for(int i = 0; i < val_locations.size(); i++) {
        current_vals.push_back(get_line_part(next_line, 
                    val_locations.at(i)));
    }

    cout << "can push in new values" << endl;

    // move the line
    next_line.clear();
    if(!getline(*log_file, next_line)) {  // **SEGFAULT**
        // no more lines
        cout << "no more lines" << endl;
        next_line.clear();
        next_time = -1;
        return true;
    }
    cout << "got the line" << endl;
    // update the time as well
    next_time = stoi(get_line_part(next_line, 0));
    return true;
}

bool LogReader::has_more(){
    return next_time != -1;
}

main.cpp

// c imports
#include <time.h>

// c++ imports
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <boost/date_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>

// my imports
#include "LogReader.h"
#include "functions.h"

// custom shorter namespaces
namespace bfs = boost::filesystem;

// used namespaces
using namespace std;

void update_line(int *current_time, string *current_line, 
        ifstream *current_file){
    if(!getline(*current_file, *current_line)){
        *current_time = -1;
        current_line->clear();
        return;
    }

    try {
        *current_time = stoi(get_line_part(*current_line, 0));
    } catch (int e) {
        cout << "update line, bad stoi on time" << endl;
        cout << *current_line << endl;
        throw e;
    }
}

void update_vals(vector<float*> vals, string line) {
    for(int i = 0; i < vals.size(); i++) {
        // offset for fact that first two are time and sensor
        try {
            *(vals.at(i)) = stof(get_line_part(line, 2 + i));
        } catch (int e) {
            cout << "update_vals, bad stof for " << i << endl;
            cout << line << endl;
            throw e;
        }
    }
}

string get_correct_file(string name, vector<string> options) {
    for(int i =0; i < options.size(); i++) {
        string option = options.at(i);
        if(boost::algorithm::contains(option, name)){
            return option;
        }
    }

    return string("");
}

int main(int argc, char* argv[]) {
    // open the base dir
    bfs::path base_dir("log/");
    if(!bfs::exists(base_dir) && !bfs::is_directory(base_dir)){
        cout << "Bad base directory" << endl;
        return 1;
    }

    // create a vector of the possible traces
    vector<string> traces;
    for(bfs::directory_iterator iter(base_dir);
            iter != bfs::directory_iterator(); iter++) {
        stringstream trace_path;
        trace_path << iter->path().string();
        traces.push_back(trace_path.str());
    }

    int trace_index = user_choose_option(traces);

    // load that directory
    bfs::path trace_dir(traces.at(trace_index));
    if(!bfs::exists(base_dir) && !bfs::is_directory(base_dir)){
        cout << "Selected a bad trace directory" << endl;
        return 1;
    }

    // get the image directory
    cout << "loading image directory" << endl;
    string img_path_string = trace_dir.string();
    stringstream img_path_stream;
    img_path_stream << img_path_string << "/img/";
    bfs::path img_dir(img_path_stream.str());
    if(!bfs::exists(img_dir) && !bfs::is_directory(img_dir)){
        cout << "no image directory" << endl;
        return 1;
    }

    // get image list, ends up in sorted order from naming conventions
    cout << "getting image paths" << endl;
    vector<string> image_paths;
    for(bfs::directory_iterator iter(img_dir);
            iter != bfs::directory_iterator(); iter++) {
        stringstream image_path;
        image_path << iter->path().string();
        image_paths.push_back(image_path.str());
    }

    // get the data traces
    cout << "loading data traces" << endl;
    vector<string> log_paths;
    vector<string> label_paths;
    string trace_path_string = trace_dir.string();
    for(bfs::directory_iterator iter(trace_path_string);
            iter != bfs::directory_iterator(); iter++) {
        string cur_file = iter->path().string();
        cout << cur_file << endl;
        if(boost::algorithm::contains(cur_file, "label-")) {
            label_paths.push_back(cur_file);
        } else if(boost::algorithm::contains(cur_file, "log-")) {
            log_paths.push_back(cur_file);
        }
    }
    cout << endl;

    // temp for reading in line parts
    // istringstream temp;

    cout << "getting log readers" << endl;
    // choose the label file to use, get first line
    int label_index = user_choose_option(label_paths);
    vector<int> label_offsets;
    label_offsets.push_back(1);
    LogReader label_reader(label_paths.at(label_index), label_offsets);
    /*
    ifstream label_file(label_paths.at(label_index));
    string label_line;
    getline(label_file, label_line);
    int label_time;
    temp.clear();
    temp.str(get_line_part(label_line, 0));
    temp >> label_time;
    string label_current = get_line_part(label_line, 1);
    */

    /*
    // get the accel
    string accel_path = get_correct_file("accel", log_paths);
    vector<int> accel_offsets;
    accel_offsets.push_back(2);
    accel_offsets.push_back(3);
    accel_offsets.push_back(4);
    LogReader accel_reader(accel_path, accel_offsets);
    */

    vector<LogReader*> readers;
    vector<bool> updated;
    readers.push_back(&label_reader);
    updated.push_back(true);
//    readers.push_back(&accel_reader);
//    updated.push_back(true);

    int l_time = current_time_min(readers);
    while(label_reader.has_more() ){ // || accel_reader.has_more()) {
        // figure out what time to advance to
        int n_time;
        cout << label_reader.has_more() << endl;
        if(same_current_time(readers)) {
            n_time = next_time_min(readers);
        } else {
            n_time = current_time_nextmin(readers);
        }
        cout << n_time << endl;

        label_reader.advance(n_time);
        cout << label_reader.current_line << endl;
        /*
        // advance all the readers
        for(int i = 0; i < readers.size(); i++) {
            cout << "loop " << i << endl;
            // keep track of which values updated
            readers.at(i);
            cout << "can get from vector" << endl;
            bool advanced = readers.at(i)->advance(n_time);
            cout << advanced << endl;
            if(advanced) {
                updated.at(i) = true;
            } else {
                updated.at(i) = false;
            }
        }

        // sanity check printing
        for(int i = 0; i < readers.size(); i++) {
            cout << readers.at(i)->current_line << endl;
        }
        */

        // deal with statistics here

    }

    /*
    ifstream accel_file(accel_path);
    string accel_line;
    getline(accel_file, accel_line);
    int accel_time;
    temp.clear();
    temp.str(get_line_part(accel_line, 0));
    temp >> accel_time;
    float accel_current_x = stof(get_line_part(accel_line, 2));
    float accel_current_y = stof(get_line_part(accel_line, 3));
    float accel_current_z = stof(get_line_part(accel_line, 4));
    vector<float*> accel_vals;
    accel_vals.push_back(&accel_current_x);
    accel_vals.push_back(&accel_current_y);
    accel_vals.push_back(&accel_current_z);

    // get the sprox
    string sprox_path = get_correct_file("sprox", log_paths);
    ifstream sprox_file(sprox_path);
    string sprox_line;
    getline(sprox_file, sprox_line);
    int sprox_time;
    temp.clear();
    temp.str(get_line_part(sprox_line, 0));
    temp >> sprox_time;
    float sprox_current = stof(get_line_part(sprox_line, 2));
    vector<float*> sprox_vals;
    sprox_vals.push_back(&sprox_current);

    // get the lprox
    string lprox_path = get_correct_file("lprox", log_paths);
    ifstream lprox_file(lprox_path);
    string lprox_line;
    getline(lprox_file, lprox_line);
    int lprox_time;
    temp.clear();
    temp.str(get_line_part(lprox_line, 0));
    temp >> lprox_time;
    float lprox_current = stof(get_line_part(lprox_line, 2));
    vector<float*> lprox_vals;
    lprox_vals.push_back(&lprox_current);

    // get the light
    string light_path = get_correct_file("light", log_paths);
    ifstream light_file(light_path);
    string light_line;
    getline(light_file, light_line);
    int light_time;
    temp.clear();
    temp.str(get_line_part(light_line, 0));
    temp >> light_time;
    float light_current = stof(get_line_part(light_line, 2));
    vector<float*> light_vals;
    light_vals.push_back(&light_current);
    */

//    int time_current = min(label_time, min(sprox_time, 
//                min(lprox_time, min(accel_time, 
//                light_time))));

    /*
    // variables for processing here
    int total_time = 0;
    map<string, int> label_counts;

    while(label_time != -1 || accel_time != -1 || sprox_time != -1
            || lprox_time != -1 || light_time != -1) {
        time_current++;
        if(label_time != -1 && time_current > label_time) {
            update_line(&label_time, &label_line, &label_file);
            if(label_line.size() > 0) // if last label, don't overwrite
                label_current = get_line_part(label_line, 1);
        }
        if(accel_time != -1 && time_current > accel_time) {
            update_line(&accel_time, &accel_line, &accel_file);
            if(accel_line.size() > 0) // if last line, don't overwrite
                update_vals(accel_vals, accel_line);
        }
        if(sprox_time != -1 && time_current > sprox_time) {
            update_line(&sprox_time, &sprox_line, &sprox_file);
            if(sprox_line.size() > 0) // if last line, don't overwrite
                update_vals(sprox_vals, sprox_line);
        }
        if(lprox_time != -1 && time_current > lprox_time) {
            update_line(&lprox_time, &lprox_line, &lprox_file);
            if(lprox_line.size() > 0) // if last line, don't overwrite
                update_vals(lprox_vals, lprox_line);
        }
        if(light_time != -1 && time_current > light_time) {
            update_line(&light_time, &light_line, &light_file);
            if(light_line.size() > 0) // if last line, don't overwrite
                update_vals(light_vals, light_line);
        }

        // Processing happens here
        total_time++;
        if(label_counts.count(label_current) == 0)
            // not in map
            label_counts[label_current] = 0;
        label_counts[label_current]++;
    }

    // post processing happens here
    cout << "Labels Counts:" << endl;
    for(map<string, int>::iterator it = label_counts.begin(); 
            it != label_counts.end(); it++) {
        cout << it->first << " -> " << it->second << " -> ";
        cout << 1.0 * it->second / total_time << endl;
    }
    */
}

您的程序表现出未定义的行为,因为您使用的是指向已删除对象的指针。

ifstream lf(log_loc);
log_file = &lf;

if 在构造函数 returns 时被删除,而您仍然持有指向对象的指针。

log_file 更改为对象而不是指针。