0

In the program I'm writing, I have a vector whose elements are instances of a struct called "person", and I'm allowing the user to add elements to the vector. I've tried two methods of actually adding elements to the vector so far, and neither of them has worked. I'm storing the user's input in a variable called inputtedPerson that is an instance of "person", and then I'm trying to add that variable to the vector. First I tried Sample.at(0) = inputtedPerson, but that ended up giving me this error when I tried to run the program in GDB.

Error

Then, I tried creating an iterator called iter, setting it to Sample.begin(), and having the program add the element to the beginning of the vector by setting *iter as equal to inputtedPerson. (For every subsequent person that is added, the iterator is incremented by one, such that the people are added in sequential order.) Unfortunately, that produced a segmentation fault error:

Error

What am I doing wrong?

Here's my code:

// DelibDem.cpp : Defines the entry point for the application.
//

#include "DelibDem.h"
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>

//initializing variables
using namespace std;
bool continue_ = true;
string name = "";
string partyID = "";
int numD = 0;
int numR = 0;
int difference = 0;
int vectorSize = 0;
int newVectorSize = 0;
int demPos = 0;
int repPos = 0;
struct person{
    string Name;
    string PartyID;
    string equivalentName;
    string equivalenceClass;
};
vector<person> Sample;
std::vector<person>::iterator iter = Sample.begin();

int main()
{
    //user adds people to the vector
    while (continue_ == true) {
        string personName;
        string personPartyID;
        string answer;
        person inputtedPerson;
        cout << "Enter a person's name: ";
        std::getline(cin, personName);
        //cout << personName;
        cout << "Enter the person's party ID (D or R): ";
        std::getline(cin, personPartyID);
        //cout << personPartyID;
        if (personPartyID == "D") person inputtedPerson = { personName, personPartyID, "", "Republicans" };
        else person inputtedPerson = { personName, personPartyID, "", "Democrats" };
        *iter = inputtedPerson;
        //cout << "{{" << Sample.at(0).Name << ", " << Sample.at(0).PartyID << ", " << Sample.at(0).equivalentName << ", " << Sample.at(0).equivalenceClass << "}\n";
        //for (int i = 1; i < Sample.size(); i++) {
        //  cout << ", {" << Sample.at(i).Name << ", " << Sample.at(i).PartyID << ", " << Sample.at(i).equivalentName << ", " << Sample.at(i).equivalenceClass << "}\n";
        //}
        //cout << "}";
        iter++;
        cout << "Do you wish to add more people? (Y/N) ";
        cin >> answer;
        if (answer == "N") continue_ = false;
        cin.ignore();
    }
    //The number of Democrats in the sample is stored in numD. The number of Republicans is stored in numR.
    for (auto& element : Sample)
    {
        if (element.PartyID == "D") numD++;
        else numR++;
    }
    //print the number of Democrats and Republicans
    cout << numD;
    cout << numR;
    //determine if an equivalence relation exists
    if (numD == numR) cout << "An equivalence relation is possible" << endl;
    else {
        cout << "An equivalence relation is not possible, because ";
        if (numD > numR) {
            cout << "There are " << numD - numR << " more Democrats than Republicans" << endl;
            int difference = numD - numR;
            while (difference != 0) {
                string specifiedName;
                vectorSize = Sample.size();
                cout << "Which Democrat do you want to remove from the sample?" << endl;
                cin >> specifiedName;
                for (std::vector<person>::iterator it = Sample.begin(); it != Sample.end(); ++it) //this is the part I'm asking about
                {
                    if (( * it).Name == specifiedName && ( * it).PartyID == "D") {
                        Sample.erase(it);
                        break;
                    }
                }
                newVectorSize = Sample.size();
                if (vectorSize == newVectorSize) cout << "The requested person is not one of the Democrats in the sample. Please try again." << endl;
                else difference--;
            }
        }
        else {
            cout << "There are " << numR - numD << " more Republicans than Democrats" << endl;
            int difference = numR - numD;
            while (difference != 0) {
                string specifiedName;
                vectorSize = Sample.size();
                cout << "Which Republican do you want to remove from the sample?" << endl;
                cin >> specifiedName;
                for (std::vector<person>::iterator it = Sample.begin(); it != Sample.end(); ++it)
                {
                    if (( * it).Name == specifiedName && ( * it).PartyID == "R") {
                        Sample.erase(it);
                        break;
                    }
                }
                newVectorSize = Sample.size();
                if (vectorSize == newVectorSize) cout << "The requested person is not one of the Republicans in the sample. Please try again." << endl;
                else difference--;
            }

        }
        cout << "The number of Democrats and Republicans is now equal and an equivalence relation is possible." << endl;
    }
    //print the properties of each element in the sample
    for (auto& element : Sample)
    {
        cout << element.Name << endl;
        cout << element.PartyID << endl;
        cout << element.equivalentName << endl;
        cout << element.equivalenceClass << endl;
        cout << "\n";
    }
    //creating two vectors containing only the Democrats and Republicans, respectively
    vector<person> Democrats;
    vector<person> Republicans;
    for (auto& element : Sample)
    {
        if (element.PartyID == "D") {
            Democrats.at(demPos) = element;
            demPos++;
        }
        else {
            Republicans.at(repPos) = element;
            repPos++;
        }
    }
    //assigning each Democrat to a Republican and vice versa
    std::vector<person>::iterator iterD = Democrats.begin();
    std::vector<person>::iterator iterR = Republicans.begin();
    for (int i = 0; i < Democrats.size(); i++)
    {
        (*iterD).equivalentName = (*iterR).Name;
        iterD++;
        iterR++;
    }
    iterD = Democrats.begin();
    iterR = Republicans.begin();
    for (int i = 0; i < Republicans.size(); i++)
    {
        (*iterR).equivalentName = (*iterD).Name;
        iterR++;
        iterD++;
    }
    return 0;
    //printing the properties of each element in the sample again
    for (auto& element : Sample)
    {
        cout << element.Name << endl;
        cout << element.PartyID << endl;
        cout << element.equivalentName << endl;
        cout << element.equivalenceClass << endl;
        cout << "\n";
    }
}
  • `push_back` adds a new item to the end of a vector. – Kyle Dec 03 '22 at 22:11
  • The vector is initially empty. Thats why `at(0)` fails - there is no element 0 yet. Same for begin() - there is no first element until you have added one. – BoP Dec 03 '22 at 22:13
  • @BoP So should I just add a dummy element to the vector at the beginning of my program and then remove it as soon as I've added the first real element? – StanAtkinson Dec 03 '22 at 22:15
  • No, you should use [push_back](https://en.cppreference.com/w/cpp/container/vector/push_back) to add new elements, as Kyle suggested. – BoP Dec 03 '22 at 22:18
  • OK, push_back worked. Thanks Kyle and BoP! – StanAtkinson Dec 03 '22 at 22:29
  • I'm sure you could have presented a lot less code to demonstrate your problem. That's what a [mre] is for. – Paul Sanders Dec 03 '22 at 22:38

1 Answers1

0

I point some issues with the posted code, addressing the main question as well, and include an alternative solution at the end:

  • Try not to using namespace std;, as it pollutes your program's namespace (see more here).

  • Try to declare variables next to their first use.
    If possible, prefer local variables to globals. For this example, no global variables are needed.
    Always initialize variables, especially locals.

  • Try to use block braces even for one-line blocks.

  • Try not to mix std::getline and std::cin >>. For this example, better stick to std::getline (see more here).

  • Use functions to structure the code, especially if you start repeating a few lines now and then (e.g. print the Sample vector).
    If that function is printing an object, it can be implemented as its operator<<.

  • This is not the correct way to add values to a vector. Notice iter is initialized to Sample.begin(), which happens to be the same as Sample.end() since Sample is empty; so *iter, i.e. dereferencing iter, will be an error.

vector<person> Sample;
std::vector<person>::iterator iter = Sample.begin();
*iter = inputtedPerson;
iter++;

Once you have read the person information, you can use Sample.push_back to add a new person to Sample.

sample.push_back(std::move(p));
  • The code below is an erase if. It may be more efficient, because it can break earlier, but it is more complex and it doesn't express intent as well.
vectorSize = Sample.size();
for (std::vector<person>::iterator it = Sample.begin(); it != Sample.end(); ++it)
{
    if (( * it).Name == specifiedName && ( * it).PartyID == "D") {
        Sample.erase(it);
        break;
    }
}
newVectorSize = Sample.size();
if (vectorSize != newVectorSize)
    difference--;

std::erase_if gives you the number of erased elements, so you don't need to use neither vectorSize nor newVectorSize.

if (auto no_of_erased_people{
        std::erase_if(sample,
            [&specifiedName, &party](auto& person) {
                return person.Name == specifiedName and person.PartyID == party;
            }
        )
    };
    no_of_erased_people != 0) {
    difference--;
}
  • Likewise, the code below is a count if.
for (auto& element : Sample)
{
    if (element.PartyID == "D") numD++;
    else numR++;
}

You won't get to write many fewer lines, but they, again, should be more readable.

auto numD{ std::ranges::count_if(sample, [](const auto& person) {
    return person.is_democrat(); })
};
auto numR{ std::ssize(sample) - numD };
  • Finally, in order to set the equivalentName for each person, there is no need to create two new vectors, one for democrats and one for republicans. The original sample vector can be partitioned, sorting people from one party before the other. Since you know there are the same number of people from both parties, you can then walk the sample vector with one iterator pointing to the current person of each party, and match them.
auto rep_it{ std::partition(sample.begin(), sample.end(),
    [](auto& person) { return person.is_democrat(); }) };
for (auto dem_it{ sample.begin() }; rep_it != sample.end(); ++dem_it, ++rep_it) {
    dem_it->equivalentName = rep_it->Name;
    rep_it->equivalentName = dem_it->Name;
}

[Demo]

rturrado
  • 7,699
  • 6
  • 42
  • 62