I'm writing a function that handles an order input file (csv) using a while loops to iterate though it.
762212,1,2020-03-15,10951-3,64612-2,57544-1,80145-1,27515-2,16736-1,79758-2,29286-2,51822-3,39096-1,32641-3,63725-3,64007-2,23022-1,16974-3,26860-2,75536-2,26461-1
1,373975319551257,12-2023
258572,2,2020-03-15,96497-1,70616-1,80237-2,22248-2,56107-1,59695-1,37948-3,21316-3,63498-1,18329-1,56833-1,66295-1,47680-3,30346-1
1,201741963232463,02-2022
857003,3,2020-03-15,16655-1,88019-3,75069-3,96017-2,46883-2,15138-1,77316-1,70063-3,54452-3,86429-2,15134-2,60176-1,12946-3
2,cfeeham3s
747893,4,2020-03-17,48520-1,93268-2,63636-1,23750-2,99771-3,83203-1,21316-3,89921-2,15134-3,82831-1,30346-2,54044-3,28561-1,14792-2,23523-3,56826-2
1,3571379825697064,04-2025
Every two lines represents an input. I have the following function that handles this input:
list<Order> orders;
void read_orders(string file_name) {
fstream read_file;
read_file.open(file_name, ios::in);
if (read_file.is_open()) {
string s;
int line_num = 1; // keeps track of line number in input file
int o_id;
string o_date;
int c_id;
vector<LineItem> itms;
while (getline(read_file, s)) {
cout << orders.size(); // shows that only two objects are added before failure
if (line_num % 2 == 1) { // handle odd numbered lines of input
auto data = split(s, ',');
int o_id = stoi(data[0]);
string o_date = data[1];
int c_id = stoi(data[2]);
vector<LineItem> itms;
// get line items
int n_line_items = data.size() - 3;
vector<string> end_data(data.end() - n_line_items, data.end());
for (string x: end_data) {
auto parts = split(x, '-');
LineItem* it = new LineItem(stoi(parts[0]), stoi(parts[1]));
itms.push_back(*it);
delete it;
}
} else { // handle even numbered lines of input
auto data = split(s, ',');
Credit* pay_credit = new Credit(0.0, data[1], data[2]); // initialize each type of payment
PayPal* pay_paypal = new PayPal(0.0, data[1]);
WireTransfer* pay_wire = new WireTransfer(0.0, data[1], data[2]);
if (data[0] == "1") {
Order* ordr = new Order(o_id, o_date, c_id, itms, *pay_credit);
orders.push_back(*ordr);
delete ordr;
} else if (data[0] == "2") {
Order* orr = new Order(o_id, o_date, c_id, itms, *pay_paypal);
orders.push_back(*orr);
delete orr;
} else if (data[0] == "3") {
Order* odr = new Order(o_id, o_date, c_id, itms, *pay_wire);
orders.push_back(*odr);
delete odr;
}
delete pay_credit; // trying to clean up memory
delete pay_paypal;
delete pay_wire;
}
line_num += 1;
}
read_file.close();
}
}
Because of my cout statement, I can tell that it only adds two items to the list before running into the std::bad_alloc error. It seems to happen when it switches from adding a Credit
object to adding a PayPal
object into the Order(...)
when it's initialized. I did a lot of research into why this might happen, so I tried to clean up as much as I knew how to (I'm new to C++) but the same error kept popping up. Does the error happen when I'm adding things to the list or is it when I'm creating these new objects?/How could I fix something like that?
Here are my class definitions in case that's important:
class Payment {
public:
double amount;
string print_detail() {
return "hey";
};
};
class Credit: public Payment {
private:
string card_number;
string expiration;
public:
Credit(double amt, string cn, string exp) {
this->amount = amt;
this->card_number = cn;
this->expiration = exp;
}
string print_detail() {
return "Credit card " + this->card_number + ", exp. " + this->expiration;
}
};
class PayPal: public Payment {
private:
string paypal_id;
public:
PayPal(double amt, string pp_id) {
this->amount = amt;
this->paypal_id = pp_id;
}
virtual string print_detail() {
return "Paypal ID: " + this->paypal_id;
}
};
class WireTransfer: public Payment {
private:
string bank_id;
string account_id;
public:
WireTransfer(double amt, string b_id, string a_id) {
this->amount = amt;
this->bank_id = b_id;
this->account_id = a_id;
}
string print_detail() {
return "Wire transfer from Bank ID " + this->bank_id + ", Account# " + this->account_id;
}
};
class LineItem {
private:
int item_id;
int qty;
public:
LineItem(int i_id, int qt) {
this->item_id = i_id;
this->qty = qt;
}
double subtotal() {
double subtot = 0.0;
for (auto x: items) {
if (x.item_id == this->item_id) {
subtot += x.price * this->qty;
}
}
return subtot;
};
};
class Order {
private:
int order_id;
string order_date;
int cust_id;
vector<LineItem> line_items;
Payment payment;
public:
Order(int o_id, string o_date, int c_id, vector<LineItem> li, Payment pay) {
this->order_id = o_id;
this->order_date = o_date;
this->cust_id = c_id;
this->line_items = li;
this->payment = pay;
}
string pay_type = "";
double total() {
double result = 0.0;
for (auto li: line_items) {
result += li.subtotal();
}
return result;
}
string print_order() {
string text = "===========================\nOrder #";
text += to_string(this->order_id) + ", Date: " + this->order_date + "\nAmount: $";
text += to_string(this->total()) + ", Paid by ";
text += payment.print_detail();
return text;
}
};
And this was the error message showing that it did insert two items:
001122terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Process returned 3 (0x3)
std::bad_alloc
is often thrown when there is not enough memory to be allocated. I can't say if this will solve the problem, but your repeated allocations and deallocations of objects are both unnecessary and harmful (causing memory fragmentation).Instead of
you should do
or
The same applies to every occurence of
new
inread_orders
. You don't need any of them.Another helpful thing you can do is to preallocate memory for
std::vector
. If you don't know how many items it will have, do an educated guess (100, 1000, 10000, etc.).Also, make sure to
std::move
your vectors if you want to transfer the whole content of it and not make a copy.