Input bugando without reason

2

I've done a program that computes (by a DB itself using fstream ) patients from a hospital. It has output functions (one for ostream using iomanip and one for ofstream and fstream ) and input ( get() ). As a requirement, I had to use selection sort and binary search that spend a damn memory (at least the way I had to do the binary search) data in a raw way. I implemented the following:

#include <cstring>
#include <iomanip>
class patientType
{
    private:
    char* first_name_t;
    char* second_name_t;
    unsigned id_t;
    public:
    patientType()
    {
    }
    void get()
    {
        char* first_name = new char[128];
        char* second_name = new char[128];
        unsigned id;
        std::cout << "\nType patient's first name: ";
        std::cin >> first_name;
        std::cout << "\nType patient's second name: ";
        std::cin >> second_name;
        std::cout << "\nType patient's ID: ";
        std::cin >> id;
        std::cout << std::endl;
        this->first_name_t = new char[std::strlen(first_name)];
        std::strcpy(this->first_name_t, first_name);
        delete first_name;
        this->second_name_t = new char[std::strlen(second_name)];
        std::strcpy(this->second_name_t, second_name);
        delete second_name;
        this->id_t = id;
    }
    patientType(const char* first_name, const char* second_name, const unsigned id)
    {
        this->first_name_t = new char[std::strlen(first_name)];
        std::strcpy(this->first_name_t, first_name);
        this->second_name_t = new char[std::strlen(second_name)];
        std::strcpy(this->second_name_t, second_name);
        this->id_t = (unsigned)id;
    }
    ~patientType() //De
    {
        this->id_t = 0;
        delete[] this->first_name_t;
        delete[] this->second_name_t;
    }
    friend std::ostream& operator<<(std::ostream& os, patientType& patient)
    {
        os << patient.first_name() << std::left << std::setw(3) << ' ' << patient.second_name() << std::left << std::setw(3) << ' ' << patient.id() << std::endl;
        return os;
    }
    void save(std::ofstream& ofs)
    {
         ofs << this->first_name() << "\t" << this->second_name() << "\t" << this->id() << "\n";
    }
    void save(std::fstream& ofs)
    {
        ofs << this->first_name() << "\t" << this->second_name() << "\t" << this->id() << "\n";
    }
    void operator=(const patientType& patient)
    {
        this->first_name_t = new char[std::strlen(patient.first_name_t)];
        std::strcpy(this->first_name_t, patient.first_name_t);
        this->second_name_t = new char[std::strlen(patient.second_name_t)];
        std::strcpy(this->second_name_t, patient.second_name_t);
        this->id_t = (unsigned)patient.id_t;
    }
    const char* first_name() {return const_cast<const char*>(this->first_name_t);}
    const char* second_name() {return const_cast<const char*>(this->second_name_t);}
    const unsigned id() {return (const unsigned)id_t;}
};
#include <vector>
template<class T> std::vector<unsigned> binary_search(T* var, T term, unsigned size)
{
    std::vector<unsigned> ret;
    unsigned mid = size / 2;
    for(unsigned i = 0; i < mid; i++) {if(var[i] == term) ret.push_back(i);}
    for(unsigned i = mid; i < size; i++) {if(var[i] == term) ret.push_back(i);}
    return ret;
}

template<class T, class BinaryCompare> std::vector<unsigned> binary_search(T* var, T term, unsigned size, BinaryCompare compare_operation)
{
    std::vector<unsigned> ret;
    unsigned mid = size / 2;
    for(unsigned i = 0; i < mid; i++) {if(compare_operation(var[i],term)) ret.push_back(i);}
    for(unsigned i = mid; i < size; i++) {if(compare_operation(var[i],term)) ret.push_back(i);}
    return ret;
}
template <typename T> void swap(T& var, T& var1)
{
    T temp = var;
    var = var1;
    var1 = temp;
}
template <typename T> void selection_sort(T* var, unsigned size, unsigned pos = 0) //1 3 5 2 4; tamanho 5
{
    if(!(pos >= size))
    {
        for(int i = pos+1; i < size; i++)
        {
            if(var[pos] > var[i]) swap(var[pos], var[i]);
        }
        selection_sort(var, size, pos+1);
    }
    else return;
}
template <typename T, typename BinaryCompare>void selection_sort(T* var, unsigned size, BinaryCompare compare_operation, unsigned pos = 0)
{
    if(!(pos >= size))
    {
        for(int i = pos+1; i < size; i++)
        {
            if(compare_operation(var[pos], var[i])) swap(var[pos], var[i]);
        }
        selection_sort(var, size, compare_operation, pos+1);
    }
    else return;
}

So I did the main ():

#include <iostream>
#include <fstream>
#include "patientType.hpp"
#include "search.hpp"
#include "sort.hpp"
#include <string>
#include <sstream>

bool file_exists(const char* file)
{
    std::ifstream file_(file);
    if(!file_.good() || file_.fail() || file_.bad()) return false; else return true;
}
bool empty_file(const char* file)
{
    std::ifstream file_(file);
    return file_.peek() == std::ifstream::traits_type::eof();
}

int main()
{
    while(true)
    {
        unsigned option = 0;
        if(!file_exists("patient.db") || empty_file("patient.db"))
        {
            std::ofstream file("patient.db");
            std::cout << "1. Add patient\n";
            std::cout << "2. Exit\n> ";
            std::cin >> option;
            switch(option)
            {
                case 1:{patientType patient; patient.get(); patient.save(file); break;}
                case 2: {exit(0);break;}
                default: {std::cout << "Input a valid option.\n";break;}
            }
        }
        else
        {
            std::string file_content;
            std::fstream file("patient.db");
            unsigned file_size = 0;
            while(std::getline(file, file_content)) {if(!file_content.empty()) file_size++;}
            file.close();
            file.open("patient.db");
            patientType* patients = new patientType[file_size];
            int instance = 0;
            while(std::getline(file, file_content))
            {
                std::string params[3];
                std::stringstream A(file_content);
                for(int i = 0; std::getline(A, file_content, '\t');) {if(!file_content.empty()){params[i] = file_content;i++;}}
                patientType A_(params[0].c_str(), params[1].c_str(), std::stoi(params[2]));
                patients[instance] = A_;
                instance++;
            }
            std::cout << "1. Print patient\n2. Add a patient\n3. Sort patients by last name\n4. Sort patients by ID\n5. Search patient by last name\n6. Search patient by ID\n7. Exit the program\n> ";
            std::cin >> option;
            switch(option)
            {
                case 1:
                {
                    unsigned index;
                    std::cout << "\nSelect index (in range 1 to " << file_size << ").\n> ";
                    std::cin>> index;
                    if(index < 1 || index > file_size)
                    {
                        exit(-1);
                    }
                    else std::cout <<"\n" << std::left << "1st name" << std::left << std::setw(3) << " 2nd name" << std::left << std::setw(3) << " ID\n" << patients[index - 1] << "\n";
                    break;
                }
                case 2:
                {
                    file.close();
                    patientType patient;
                    patient.get();
                    std::ofstream file_("patient.db", std::ios::app);
                    patient.save(file_);
                    break;
                }
                case 3:
                {
                    selection_sort(patients, file_size, [](patientType& first, patientType& second){return first.second_name() > second.second_name();});
                    break;
                }
                case 4:
                {
                    selection_sort(patients, file_size, [](patientType& first, patientType& second){return first.id() > second.id();});
                    break;
                }
                case 5:
                {
                    unsigned term;
                    std::cout << "Input the ID term: ";
                    std::cin >> term;
                    std::vector<unsigned> occurrences = binary_search(patients, patientType("","",term), file_size, [](patientType& first, patientType& second){return first.id() == second.id();});
                    std::cout << "Found " << occurrences.size() << " occurrences.\n";
                    for(auto a : occurrences)
                    {
                        std::cout << patients[a];
                        std::cout << "\nAt " << a << "\n";
                    }
                    std::cout << std::endl;
                    break;
                }
                case 6:
                {
                    char* term = new char[128];
                    std::cout << "Input the last name term: ";
                    std::cin >> term;
                    std::vector<unsigned> occurrences = binary_search(patients, patientType("", term,0), file_size, [](patientType& first, patientType& second){return first.second_name() == second.second_name();});
                    std::cout << "Found " << occurrences.size() << " occurrences.\n";
                    for(auto a : occurrences)
                    {
                        std::cout << patients[a];
                        std::cout << "\nAt " << a << "\n";
                    }
                    std::cout << std::endl;
                    delete[] term;
                    break;
                }
                case 7:
                {
                    exit(0);
                }
            }
        }
    }
}

If you test yourself, it goes into multiple functions, debug gives SIGTRAP several times and out of nothing, when you ask for an input several times it crash - to . The program (with the requirements) would be slow, but probably would not bug. Where is the error? How to arrange?

    
asked by anonymous 03.05.2014 / 03:10

1 answer

1

The problem is in the snippets that you allocate a new string like this:

new char[std::strlen(first_name)];

Should be:

new char[std::strlen(first_name) + 1];

Without + 1 you are not considering the null character of string patientType , which causes no problem in your program until the patientType::get() destructor is called. And that runs when you add new patients and when you list them.

Other details:

  • In the first_name function, the variables second_name and delete [] must be deleted using delete (same as you did in the destructor) and not only patientType::operator=(const patientType& patient) .
  • In return first.second_name() == second.second_name(); you should check and delete the previous (or clean) values of the strings before allocating new strings.
  • When you do strcmp() you are comparing pointers, to compare the value of strings use binary_search(T* var, T term, unsigned size, BinaryCompare compare_operation) .
  • And finally, when you call the T term function you are copying the construtor de cópia object. This means that every time you call this function, the compiler will call the term (which you do not have defined), so that the strings are not properly allocated. A simple solution is to pass T& term by reference ( std::string ).
  • Another simple solution to these problems is not allocating dynamically when there is no need. For example, in the class you do:

    // Na classe:
    char* first_name_t; 
    char* second_name_t;
    
    // Em get
    char* first_name = new char[128];
    char* second_name = new char[128];
    

    You can simply do:

    // Na classe:
    char first_name_t[128]; 
    char second_name_t[128];
    
    // Em get
    char first_name[128];
    char second_name[128];
    

    And limit the user not to enter names greater than 127 characters. With this you no longer have to worry about deallocating. The downside of doing this is that each name will always occupy 128 char, rather than occupy just what it needs.

    I imagine you can not use %code% , but this is an example of why you should use it (it would save you all that work). But anyway, when you're manually managing memory, you should check to see if your class meets the Rule of the Three (or Five in C ++ 11).

        
    03.05.2014 / 15:27