I have a class hierarchy as shown in the example below, where a State contains a list of ZipCodes and a list of Citys, each of which contain pointers to the ZipCodes.
The goal is to be able to update the ZipCodes without needing to update Citys (or to create new instances of City).
The C++ code below meets this requirement, but it uses pointers, which I prefer to avoid because of this and that. How can I re-design this [naive] implementation so that it doesn't rely on pointers? Thanks for any help!
EDIT: Updated code below to use boost::shared_ptr instead of raw pointers. Note that State, City, and ZipCode are just example names, and they turned out to be poor choice names (I could've picked "A", "B", and "C") because the actual code allows the equivalent of City to share ZipCodes.
#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
using namespace std;
/**
* Zone Improvement Plan (ZIP) code
*/
class ZipCode {
public:
ZipCode() : code_(0), plus4_(0) {}
ZipCode(int code, int plus4 = 0) : code_(code), plus4_(plus4) {}
virtual ~ZipCode() {};
int code() const { return code_; }
int plus4() const { return plus4_; }
void set_code(int code) { code_ = code; }
void set_plus4(int plus4) { plus4_ = plus4; }
private:
int code_;
int plus4_;
};
typedef boost::shared_ptr<ZipCode> ZipPtr;
/**
* City points to one or more zip codes
*/
class City {
public:
const vector<ZipPtr>& zip() const { return zip_; }
void add_zip_ptr(const ZipPtr x) { if (x != NULL) zip_.push_back(x); }
private:
// TODO: this vector should be a hash set
vector<ZipPtr> zip_;
};
/**
* State contains cities, each of which has pointers to
* zip codes within the state.
*/
class State {
public:
const vector<City>& city() const { return city_; }
const vector<ZipPtr>& zip() const { return zip_; }
const ZipPtr zip_of(int code) const {
for (size_t i = 0; i < zip_.size(); i++) {
if (zip_[i]->code() == code) {
return zip_[i];
}
}
return ZipPtr();
}
void add_city(const City& x) { city_.push_back(x); }
void add_zip(int code) { zip_.push_back(ZipPtr(new ZipCode(code))); }
private:
// TODO: these vectors should be hash sets
vector<City> city_;
vector<ZipPtr> zip_;
};
int main() {
State texas;
City dallas, houston;
// create state ZIPs
texas.add_zip(75380);
texas.add_zip(75381);
texas.add_zip(77219);
texas.add_zip(77220);
// point city ZIPs to the ones we just created
dallas.add_zip_ptr(texas.zip_of(75380));
dallas.add_zip_ptr(texas.zip_of(75381));
houston.add_zip_ptr(texas.zip_of(77219));
houston.add_zip_ptr(texas.zip_of(77220));
// print all ZIPs
cout << "ZIPs in Texas: " << endl;
const vector<ZipPtr>& zips = texas.zip();
for (size_t i = 0; i < zips.size(); i++) {
cout << " " << zips[i]->code() << endl;
}
cout << "ZIPs in Dallas, Texas: " << endl;
const vector<ZipPtr> zip_ptrs1 = dallas.zip();
for (size_t i = 0; i < zip_ptrs1.size(); i++) {
cout << " " << zip_ptrs1[i]->code() << endl;
}
cout << "ZIPs in Houston, Texas: " << endl;
const vector<ZipPtr> zip_ptrs2 = houston.zip();
for (size_t i = 0; i < zip_ptrs2.size(); i++) {
cout << " " << zip_ptrs2[i]->code() << endl;
}
// change a state ZIP...
cout << "Changing Houston's ZIP 77220..." << endl;
ZipPtr z = texas.zip_of(77220);
if (z != NULL) z->set_code(88888);
// ...and show the ZIPs of the affected city
cout << "ZIPs in Houston, Texas: " << endl;
const vector<ZipPtr> zip_ptrs3 = houston.zip();
for (size_t i = 0; i < zip_ptrs3.size(); i++) {
cout << " " << zip_ptrs3[i]->code() << endl;
}
return 0;
}
I see the situation as two 1:n relationships
State : City == 1 : n
City : Zipcode
== 1 : n
Based on that, I think that the State containing
vector<ZipCode> zip_;
is not sound.
I might do
class State {
vector< City > cities_in_state_;
};
class City {
vector< Zipcode > zips_in_city_;
};
This does not require pointers.
Unless you want to duplicate your ZipCode objects, you fall into this category of usage (described in your first link):
The Bar instance is actually managed
by some other part of your program,
whereas the Foo class just needs to be
able to access it.
It seems like a legit use.
However, you might want to consider the copy option (to permanently avoid problems if the vector has to reallocate its data) or make State aggregate ZipCodes from its Cities instead of distributing them ZipCodes.
The copy simply implies that you stop using pointers in City. Aggregating the ZipCodes means that instead of giving State a list of ZipCodes, you would give City the list of ZipCode instances, and when calling zip_of, you would iterate through the cities and iterate through their ZipCode collection.
Related
I'm working on some exam-examples for a c++ programming exam and the example where I'm stuck at requires me to code a class for returning the contents of a "Closet" object. One of the methods required in the example adds a vector of Garment objects to a second vector of Garment objects, (so, filling the closet with clothes). Up until this point my code has passed all the references and checks I've been given (a list with the supposed runtime errors and cout/cerr statements), so I've removed method-definitions and calls in the code I'm posting here to only show the part where I'm getting the unexpected returns.
I supposed that one of the constructors or even another method might interfere with the output, so I've ran several versions of the code trough a visualizer (Python tutor for c++), but that didn't shed any new insight either, no other methods were called (as expected) and no other output prompted from the constructors either.
#include <iostream>
#include <stdexcept>
#include <vector>
#include <string>
using namespace std;
enum class Color{Red, Blue, Gray, Yellow};
const std::vector<std::string> color_names{"red", "blue", "gray", "yellow"};
enum class Type{Pants, Blouse, Shirt, Skirt};
const std::vector<std::string> type_names{"pants", "blouse", "shirt", "skirt"};
class Garment {
int preis;
Color farbe;
Type typ;
public:
//Konstruktor
Garment (int p, Color f = Color::Gray, Type t = Type::Pants){
this->preis = p;
this->farbe = f;
this->typ = t;
//negativer Preis = exception
if (p < 0){throw runtime_error("Preis kleiner als 0!");} }
int get_price() const{
return this->preis; }
Type get_type() const{
return this->typ; }
bool has_color(Color f) const{}
void deteriorate(int w){}
int get_index_color() const{}
int get_index_type() const{}
friend ostream& operator<<(ostream& out, const Garment &g){
//[40000 Cent, yellow blouse]
out << "[" << g.preis << " Cent, "<< color_names[g.get_index_color()]
<< " " << type_names[g.get_index_type()];
out << "]";
return out;
}
};
class Closet {
size_t capacity;
vector<Garment> inventory;
public:
//Konstruktor Beginn
Closet (size_t c, vector<Garment> inv){
this->capacity = c;
this->inventory = inv;
if (capacity < 5 || capacity > 300){throw runtime_error ("Komplette Kapazitaet ueber oder unterschritten!");}
if (this->inventory.size() > this->capacity){throw runtime_error ("Relative kapazitaet ueberschritten");}
vector<int>kleiderliste {0,0,0,0};
for (auto x : inv){
if (x.Garment::get_type() == Type::Pants){kleiderliste[0]++;}
if (x.Garment::get_type() == Type::Blouse){kleiderliste[1]++;}
if (x.Garment::get_type() == Type::Skirt){kleiderliste[2]++;}
if (x.Garment::get_type() == Type::Shirt){kleiderliste[3]++;}
}
int zaehler = 0;
for (auto y : kleiderliste){
if (y != 0 ){zaehler++;}
}
if (zaehler <2){throw runtime_error("Nur mehr kleidungsstuecke eines typs im schrank");}
}
bool add(vector<Garment> v){
if ((v.size() + this->inventory.size()) <= this->capacity){
cerr << 1;
this->inventory.insert(this->inventory.begin(),v.begin(),v.end());
return true;
}else{
cerr << 0;
return false;
}
}
double mean_price() const{
}
friend ostream & operator<<(ostream &out,const Closet &c){
out << "[" << c.capacity << ",{";
for (auto x : c.inventory){
out <<x;
}
out << "},";
out << c.mean_price();
out << "]";
return out;
}
};
int main(){
Garment pants{34500, Color::Blue, Type::Pants};
Garment blouse{12700, Color::Red, Type::Blouse};
const Garment shirt{2300, Color::Yellow, Type::Shirt};
Garment shirt2{23500, Color::Red, Type::Shirt};
Garment skirt{26600, Color::Gray, Type::Skirt};
Garment skirt2{4600, Color::Blue, Type::Skirt};
Closet closet {10, {skirt, blouse, shirt, pants, skirt}};
cout << closet.add({shirt2, skirt2}) << closet.add({blouse,skirt,pants}) << closet.add({}) << closet.add({pants}) << '\n';
return 0; }
This code is supposed to yield the following output via cout:
1110. The Closet::add method is supposed to return true three times and false one time in a row.
What I actually get as return values via cout << is: 0111
To test if the code does what it's supposed to I'm outputting 1 for true and 0 for false on the cerr channel too, and there I get the correct 1110 numbers.
What leads to the return output not be 1110? Are the method calls made in a different order in the compiler?
As explained by Raymond-Chen the method is not guaranteed to be called in the left to right order that would produce the expected "1110" output. Different compilers lead to different order of the calls being executed. In this specific case switching to the clang compiler yielded the expected "1110" output.
I'm trying to code an text-based adventure game builder. I have three classes: Room, Object, and my main class. In my Room class, I have a (private) vector of pointers of Objects: vector<Object*> objectsInRoom
This keeps track of all the Objects stored in each room. I have a function called objects() in the Room class that returns objectsInRooms for when I call that vector in my main class.
vector<Object*> Room::objects() { return objectsInRoom; }
In my main class, in my function pickUpObject(), I've created a vector of pointers of Objects called roomObject. I call objects() in the Room class and store the Objects in objectsInRoom (which is only accessed in the Room class) in roomObject (which is accessible in my function in main). I also have a vector of Objects called allObjects that stores all the items that I want to pick up from the room and carry around with me. It has a global scope.
I want to make it so that if I pick up an item in a particular room, I add the item to allObjects, delete the pointer to that element in roomObjects (and thus the pointer to that element in objectsInRooms in the Room class), and the item itself.
My pickUpObject function is: (Room* current just tells me what room I'm in and thus what Objects I have)
void pickUpObject(vector<Object>&allObjects, Room* current)
{
vector<Object*>roomObjects; int length; string name; char flag;
roomObjects = current->objects();
length = roomObjects.size();
bool repeat = true;
while (repeat)
{
if (length == 0)
{
cout << "There are no objects to pick up in " << current->name() << endl;
repeat = false;
}
else
{
cout << "There is a ";
for (int k = 0; k < roomObjects.size() - 1; k++)
{
cout << roomObjects[k]->getName();
if (length > 2)
cout << ", ";
}
if (length > 1)
cout << " and " << roomObjects[length - 1]->getName() << " here." << endl;
else
cout << roomObjects[length-1]->getName() << "." << endl;
cout << "What object do you want to pick up?" << endl;
cin >> name;
//this is where the deletion happens
for (int i = 0; i < length; i++)
if (name.compare(roomObjects[i]->getName()) == 0)
{
allObjects.push_back(*roomObjects[i]);
roomObjects.erase(roomObjects.begin() + i);
deleteVectorContent(roomObjects, i, i + 1);
}
cout << "roomObject size = " << roomObjects.size() << endl;
cout << "--------------------" << endl;
cout << "allObject size = " << allObjects.size() << endl;
for (int i = 0; i < allObjects.size(); i++)
cout << allObjects[i].getName() << endl;
for (int i = 0; i < roomObjects.size(); i++)
{
cout << roomObjects[i]->getName() << endl;
}
cout << "Do you want to pick up another object? (Y/N): ";
cin >> flag;
if (flag == 'N')
repeat = false;
}
}
}
I've looked up various posts on StackOverflow to try and resolve my dilemma. In main, I've created a method called deleteVectorContent to try and delete the pointer.
void deleteVectorContent(vector<Object*> objectVector, int start, int stop)
{
for (int k = start; k < stop; k++)
delete objectVector[k];
objectVector.clear();
}
I've also tried 'roomObjects.remove()' to remove the item itself from that room. Whenever I compile, however, my compiler also throws me an exception. Help would be greatly appreciated.
P.S. The link to this assignment is here. If you scroll down to the "Extra Credit for the Programming Assignment" and go to the first one marked "10 points," that is what I am working on. Thank you so much for the help!
Room::objects() is returning a copy of objectsInRoom, so any modifications that pickUpObject() makes to that returned vector will not be applied back to objectsInRoom. You would need to make Room::objects() return a reference to objectsInRoom instead, eg:
vector<Object*>& Room::objects()
{
return objectsInRoom;
}
void pickUpObject(vector<Object> &allObjects, Room* current)
{
vector<Object*> &roomObjects = current->objects();
...
}
Otherwise, don't provide direct access to objectsInRoom at all. Introduce new methods to Room to access/remove a given Object* from its objectsInRoom, eg:
int Room::numObjects()
{
return objectsInRoom.size();
}
Object* Room::getObject(int index)
{
return objectsInRoom[index];
}
Object* Room::takeObject(int index)
{
Object *obj = objectsInRoom[index];
objectsInRoom.erase(objectsInRoom.begin()+index);
return obj;
}
void pickUpObject(vector<Object> &allObjects, Room* current)
{
int length = current->numObjects();
...
for (int i = 0; i < length; ++i)
{
if (name == current->getObject(i)->getName())
{
Object *obj = current->takeObject(i);
allObjects.push_back(*obj);
delete obj;
break;
}
}
...
}
Note that allObjects is receiving copies of the actual Objects, not Object* pointers. The code you showed is leaking memory when you make a copy of *roomObjects[i] and then erase() the Object* at i without delete'ing the Object it is pointing at. If Object is so easily copyable, you can save yourself a lot of headaches by simply getting rid of all the Object* pointers and just use Object everywhere:
class Room
{
vector<Object> objectsInRoom;
...
};
int Room::numObjects()
{
return objectsInRoom.size();
}
Object& Room::getObject(int index)
{
return objectsInRoom[index];
}
Object Room::takeObject(int index)
{
Object obj = objectsInRoom[index];
objectsInRoom.erase(objectsInRoom.begin()+index);
return obj;
}
void pickUpObject(vector<Object> &allObjects, Room* current)
{
int length = current->numObjects();
...
for (int i = 0; i < length; ++i)
{
if (name == current->getObject(i)->getName())
{
allObjects.push_back(current->takeObject(i));
break;
}
}
....
}
Otherwise, don't mix Object with Object* like you are, use Object* everywhere.
If you have a fixed set of Objects for the game, I would create a global vector<Object> to hold them all, and then just pass around Object* pointers everywhere as needed. Then you don't have to worry about cleaning up memory manually at all:
vector<Object> allGameObjects;
// fill as needed...
void Room::addObject(Object *obj)
{
objectsInRoom.push_back(obj);
}
Object* Room::takeObject(int index)
{
Object *obj = objectsInRoom[index];
objectsInRoom.erase(objectsInRoom.begin()+index);
return obj;
}
void pickUpObject(vector<Object*> &allObjects, Room* current)
{
...
allObjects.push_back(current->takeObject(i));
...
}
If you absolutely need a vector that owns Object* pointers that have to be cleaned up before the vector is destroyed, consider using vector<unique_ptr<Object>> for that, let the compiler and STL handle the hard work for you. If you ever find yourself having to write something like deleteVectorContent(), rethink your design.
Basically, say, I have the following data:
(let me note that the columns change with every piece of data I get, i.e. I need to keep things general and cannot restrict my solution to only Tenor, Date, etc.)
Now I want to be able to represent and conveniently access this data in an object/class in C++.
I have been playing around with map a bit:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class my_table {
private:
map<string, map<string, string>> c;
public:
void set(string key1, string key2, string value){ this->c[key1][key2] = value; }
string get(string key1, string key2){
map<string, map<string, string>>::iterator it = this->c.find(key1);
if (it != this->c.end()){
map<string, string>::iterator it2 = this->c[key1].find(key2);
if (it2 != this->c[key1].end()){
return c[key1][key2];
}
return "n/a";
}
return "n/a";
}
};
void main() {
my_table a;
a.set("1", "Tenor", "1D");
cout << a.get("1", "Tenor") << endl; // returns '1D'
cout << a.get("2", "Tenor") << endl; // returns 'n/a'
cout << a.get("1", "Rate") << endl; // returns 'n/a'
}
But I am not overly satisfied with this implemenation. In particular, I would want to be able to do things like:
a.get("Tenor","3M", "Rate") // should return '1.6%'
a.get("Date","01-Jan-2016", "Responsibility") // should return 'MG'
a.get_all("Type","Forward", "Rate") // should return an array {1.3%,2.4%}
a.get_row(4) // should return an array {4M,...,2.0%,MG}
And:
I am wondering whether there are there any standard packages that could help me simplify this implementation overall?
In particular, my get function seems unnecessarily cumbersome.
And generally, is map is even the right way to go in terms of storing data like this?
And what if I wanted to generalise this implemenation to more than just 2 keys? Maybe 3 keys. My solution is quite rigid
enum struct Type {
Spot
Forward
}
struct Row {
string tenor;
Date date;
int convention;
Type type;
double rate;
ResposibilityType responsibility;
};
std::vector<Row> table = {
[...]
}
access you do with std::find_if. Tables in databases might be stored like this internally. If you want multiple primary keys you can create for each key a map that maps from the primary key to an element in table. If you want a combined key, you need tuple like this std::map<std::pair<Key1,Key2>, Row*>
How about the matrix type from boost.ublas? You can create a simple enum type to easily reference columns.
For querying you can probably build something quick via the filter_iterator.
Hope this helps!
Edit: Sorry didn't notice your comment. A quick hack I can think of to support dynamic column size is using a hash map for storing column name to column index mapping in a separate hash map. Good luck!
Limiting yourself to maps could overcomplicate this somewhat. If I understand this correctly, the data structure is completely undefined at compile time. In that case perhaps a simpler way to implement it is as a vector of hash-key-value triples, like this:
#include "stdafx.h"
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class HashKeyValue
{
private:
string hash;
string key;
string value;
public:
HashKeyValue() {}
HashKeyValue(string h, string k, string v)
{
hash = h;
key = k;
value = v;
}
string getHash() { return hash; }
string getKey() { return key; }
string getValue() { return value; }
};
class my_table
{
private:
vector<HashKeyValue> hkv;
public:
my_table() {}
void set(string h, string k, string v)
{
hkv.push_back(HashKeyValue(h, k, v));
}
string getV(string h, string k)
{
for (unsigned int i = 0; i < hkv.size(); i++)
{
if (hkv[i].getHash() == h && hkv[i].getKey() == k)
return hkv[i].getValue();
}
return "n/a";
}
string getByColValue(string col1, string val, string col2)
{
string hash;
int got = 0;
for (unsigned int i = 0; i < hkv.size() && !got; i++)
{
if (hkv[i].getKey() == col1 && hkv[i].getValue() == val)
{
hash = hkv[i].getHash();
got = 1;
}
}
if (got)
{
for (unsigned int i = 0; i < hkv.size(); i++)
{
if (hkv[i].getHash() == hash && hkv[i].getKey() == col2)
return hkv[i].getValue();
}
return "n/a";
}
else return "n/a";
}
};
int main()
{
my_table m;
m.set("1", "Tenor", "1D");
m.set("3", "Tenor", "3M");
m.set("3", "Rate", "1.6%");
cout << "get-1-Tenor(1D): " << m.getV("1", "Tenor") << endl;
cout << "get-1-Alto(n/a): " << m.getV("1", "Alto") << endl;
cout << "get-3-Rate(1.6%): " << m.getV("3", "Rate") << endl;
cout << "getBCV-Tenor-3M-Rate(1.6%): " << m.getByColValue("Tenor", "3M", "Rate") << endl;
return 0;
}
Hopefully getByColValue() makes sense; it first looks up the hash, then looks up the Rate for that hash. The hash is what relates each key-value pair to others on the same row. It shouldn't be too tricky to change getByColValue() to return a vector<string> instead, for the getByColValue("Type","Forward","Rate") case: just make hash a vector<string> instead, define the return type as another vector<string>, and a few other tweaks.
This also makes the implementation of getRow() fairly trivial; just loop over hkv where hash==rowid and bung the key/value pairs (or just the values) into a vector.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 9 years ago.
Improve this question
Warning: I understand if anyone may want to stop reading now, this post contains ~275 lines of code across 6 files (although nothing very complex)! I realize this is usually a bad thing to do, but it's a last ditch effort as I've put cout's in everything method showing none of them crash or do anything that I wouldn't expect, researched implementation of the standard methods I'm using, and ran this code with a huge variety of inputs but sometimes it runs successfully, other times it fails (after finishing everything). I can't find any pattern or broken code segment.
The program simulates a type of shop with a single server allowing customers to order one of two things and there is a waiting line. The user inputs the simulation length, customer arrival probability per time unit (minute), and the time it takes for each item to be made. After running, the program then prints out a few statistics - total wait time (excluding those remaining in line), total customers served, and average wait time.
Even with long simulations (100,000 minutes) I've seen successful and failed runs (again, only failing after simulation completion). At first I thought it looked like using (>= 1) for arrival probability (customer arrives each minute) always worked, but have since noticed those failing. If anything, it seems fairly high arrival (> ~.8) and very low (<= ~.01) arrival probabilities crash the least often in long simulations, but still can sometimes in short ones. Very odd!
Whenever it does crash, the debugger shows the program counter stopping at the closing brace of queueType's destructor, but this destructor seems extrememly standard to me, and the same syntax has worked with other classes that allocate memory on the heap with their constructors? I feel like the answer must be something fairly basic that is eluding me.
Any help would be greatly appreciated, code follows:
queueType.h:
#ifndef QUEUETYPE_H
#define QUEUETYPE_H
#include <algorithm>
#include <cstdlib>
template<class Type>
class QueueType {
public:
QueueType();
~QueueType();
QueueType(const QueueType& other);
Type& getFront() {return queueArray[front];}
int getNumElements() const {return numElements;}
void reposition();
void addElement(Type);
bool isEmpty() const {return numElements == 0;}
bool isFull() const {return SIZE == numElements;}
void updateWaitTimes(Type*&, int&, int&);
QueueType<Type>& operator=(const QueueType other);
friend void swap(QueueType& first, QueueType& second) {
using std::swap;
swap(first.front, second.front);
swap(first.back, second.back);
swap(first.numElements, second.numElements);
swap(first.queueArray, second.queueArray);
}
private:
static const int SIZE = 25;
int front, back, numElements;
Type *queueArray;
};
template<class Type>
QueueType<Type>::QueueType() {
queueArray = new Type[SIZE];
front = back = numElements = 0;
}
template<class Type>
QueueType<Type>::~QueueType() {
delete [] queueArray;
}
template<class Type>
QueueType<Type>::QueueType(const QueueType& other):
queueArray(new Type[SIZE]),
front(other.front),
back(other.back),
numElements(other.numElements)
{
std::copy(other.queueArray, other.queueArray + SIZE, queueArray);
}
template<class Type>
void QueueType<Type>::reposition() {
front = (front + 1) % SIZE;
back = (back + 1) % SIZE;
numElements--;
}
template<class Type>
void QueueType<Type>::addElement(Type newElement) {
if (isEmpty()) {
queueArray[0] = newElement;
front = back = 0;
numElements = 1;
} else {
back = (back - 1) % SIZE;
queueArray[back] = newElement;
numElements++;
}
}
template<class Type>
void QueueType<Type>::updateWaitTimes(Type*& element, int& position, int& counter) {
if (isEmpty()) {
element = NULL;
} else {
if (position == 0) {
position = front;
}
element = &queueArray[position];
position = (position + 1) % SIZE;
}
if (counter == numElements) {
element = NULL;
}
counter++;
}
template<class Type>
QueueType<Type>& QueueType<Type>::operator=(const QueueType other) {
swap(*this, other);
return *this;
}
#endif /* QUEUETYPE_H */
customerType.h:
#ifndef CUSTOMERTYPE_H
#define CUSTOMERTYPE_H
class CustomerType {
public:
CustomerType();
CustomerType(int, int);
~CustomerType();
CustomerType(const CustomerType& other);
void incrementWaitTime() {waitTime++;}
int getArrivalTime() const {return arrivalTime;}
int getWaitTime() const {return waitTime;}
CustomerType& operator=(const CustomerType& other);
private:
int ID, arrivalTime, waitTime;
};
#endif /* CUSTOMERTYPE_H */
customerType.cpp:
#include "customerType.h"
CustomerType::CustomerType() {
waitTime = arrivalTime = ID = 0;
}
CustomerType::CustomerType(int arrivalTime, int ID) {
this->arrivalTime = arrivalTime;
this->ID = ID;
waitTime = 0;
}
CustomerType::~CustomerType() {
}
CustomerType::CustomerType(const CustomerType& other) {
waitTime = other.waitTime;
arrivalTime = other.arrivalTime;
ID = other.ID;
}
CustomerType& CustomerType::operator=(const CustomerType& other) {
waitTime = other.waitTime;
arrivalTime = other.arrivalTime;
ID = other.ID;
return *this;
}
serverType.h:
#ifndef SERVERTYPE_H
#define SERVERTYPE_H
#include "customerType.h"
#include <cstdlib>
#include <string>
class serverType {
public:
serverType();
~serverType();
serverType(const serverType& other);
bool isFree() const {return (status == "free");}
void setCustomer(CustomerType& newCustomer, int& transactionTime);
void decrementTransactionTime();
serverType& operator=(const serverType& other);
private:
std::string status;
int transactionTime;
CustomerType currentCustomer;
};
#endif /* SERVERTYPE_H */
serverType.cpp:
#include "serverType.h"
serverType::serverType() {
status = "free";
transactionTime = 0;
}
serverType::~serverType() {
}
serverType::serverType(const serverType& other) {
status = other.status;
transactionTime = other.transactionTime;
currentCustomer = other.currentCustomer;
}
void serverType::setCustomer(CustomerType& newCustomer, int& transactionTime) {
currentCustomer = newCustomer;
this->transactionTime = transactionTime;
status = "busy";
}
void serverType::decrementTransactionTime() {
transactionTime--;
if (transactionTime == 0)
status = "free";
}
serverType& serverType::operator=(const serverType& other) {
status = other.status;
transactionTime = other.transactionTime;
currentCustomer = other.currentCustomer;
return *this;
}
main.cpp:
#include "queueType.h"
#include "serverType.h"
#include <ctime>
#include <climits>
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
int simulationTime, coneTime, shakeTime, currentTime = 0;
int customerID = 1, totalWaitTime = 0, customersServiced = 0;
double arrivalProb;
cout << "Time-driven ice cream shop simulation" << endl
<< "Enter the following information to begin:" << endl << endl;
cout << "Length of simulation (in minutes): ";
cin >> simulationTime;
cout << endl << "Probability of customer arrival each minute (example: 0.25): ";
cin >> arrivalProb;
cout << endl << "Minutes to make an ice cream cone: ";
cin >> coneTime;
cout << endl << "Minutes to make a shake: ";
cin >> shakeTime;
cout << endl << endl;
QueueType<CustomerType> Line;
serverType server;
float chance;
srand(time(0) % INT_MAX);
while (currentTime < simulationTime) {
chance = float (rand())/RAND_MAX;
if (chance < arrivalProb) {
if (!Line.isFull()) {
Line.addElement(CustomerType(currentTime, customerID));
customerID++;
} else {
cout << "Customer #" << customerID
<< " came during a full line and left!" << endl;
customerID++;
}
}
if (server.isFree() && (!Line.isEmpty())) { //going with 40% shake, 60% cone
customersServiced++;
if (chance < 0.4) {
server.setCustomer(Line.getFront(), shakeTime);
} else {
server.setCustomer(Line.getFront(), coneTime);
}
totalWaitTime += Line.getFront().getWaitTime();
Line.reposition();
} else if (!server.isFree()) {
server.decrementTransactionTime();
CustomerType *customerPointer = new CustomerType();
int position = 0, counter = 0;
Line.updateWaitTimes(customerPointer, position, counter);
while (customerPointer != NULL) {
(*customerPointer).incrementWaitTime();
Line.updateWaitTimes(customerPointer, position, counter);
}
delete customerPointer;
}
currentTime++;
}
cout << endl << endl << "Simulation complete." << endl << endl;
cout << "Total wait time: " << totalWaitTime << endl
<< "Customers serviced: " << customersServiced << endl
<< "Average wait time: " << float (totalWaitTime) / customersServiced;
return 0;
}
Note that the queueType copy constructor/overloaded =/destructor never getting called until the destructor does once in the very end. I also know I don't need to have a customerType (currentCustomer) as one of serverType's private members, but just for the sake of realism.
You are mismanaging memory here:
CustomerType *customerPointer = new CustomerType();
int position = 0, counter = 0;
Line.updateWaitTimes(customerPointer, position, counter);
You are allocating memory for customerPointer. Then you change the value of what customerPointer points to in the Line.updateWaitTimes function. Then you do this:
delete customerPointer;
So what you allocated and what you deleted have different values. You're corrupting the heap by attempting to delete an address that doesn't start at the beginning of the dynamically allocated block.
If what you are deleting is a pointer to dynamically allocated memory, i.e. you designed it this way, but is a "different" pointer than the original you started out with, then you need to rewrite your code so you're not doing this "pointer dance" between customerPointer and the Line.updateWaitTimes function.
This is just one of probably many issues with your code you need to fix. One fix is to quit with the manual memory mamagement within your main() function. Learn to write code that minimizes or eliminates the usage of raw naked pointers. Yes, your QueueType class must do memory management, sure, but that doesn't mean your main() has to do this also.
Also, your QueueType class maintains its own memory that should not be fooled around with by an outside entity. Look at your QueueType::updateWaitTimes function -- why is it giving a pointer to the passed in "element" pointer? You then use this pointer to your internal queue and finagle with it in main(), which gives disastrous results. Writing code like this means that you haven't totally grasped the meaning of "encapsulation".
This line likely as problem, as it can leave back as negative
back = (back - 1) % SIZE;
you probably meant something like
back = (SIZE + back - 1) % SIZE;
WOW. Just finally realized the reason it was crashing was how I changed back around in queueType::reposition and queueType::addElement, in reposition I didn't need to move back at all since it's just called after someone leaves the front, and in my add I meant to move back BACK one but used - not + and moved it forward...program fixed. Thank you for answers/comments
So searching to see if someone has already asked this I see lots of questions about iterating over arrays. But what I've got is an array of iterators. Essentially here's what I'm up to:
I have a sorted std::list of a custom object. The object just contains an int and a double, and has methods for the sorts of things you would expect (constructors, setters, getters, operator< to make it sortable by the double, a toSting() method). That all works, including sorting.
Now I want a bunch of iterators that point into the list at different points. There will be one to the head of the list, one to the tail and several pointing into various points in the middle. I'm doing this using an old-style array (this may be the problem - I'll try it with a std::array, but I still want to understand why this hasn't worked). So I've got a subroutine that initializes this array. It almost works. I can build the array and output from within the subroutine and everything looks good. Outputting from outside the subroutine the last element of the array has changed and no longer appears to point into the list. Here's the relevant code:
using namespace std;
#include <iostream>
#include <list>
#include <cmath>
#include <algorithm>
#include "random.h"
#include "Double_list_struct.h"
/**********************************Subroutine declarations***************************/
template <typename Tplt>
void output_list(list<Tplt> to_out);
template <typename Tplt>
void initialize_list(list<Tplt> &alist, int size);
template <typename Tplt>
void initialize_iter_array(typename list<Tplt>::iterator* itar, int size, list<Tplt> alist);
/***************************************Main routine*******************************/
int main(void)
{
int list_size = 16;
// Make the list that will be tested.
list<Double_list_struct> list_to_play_with;
initialize_list(list_to_play_with, list_size);
list_to_play_with.sort();
cout << "Sorted list is: " << endl;
output_list(list_to_play_with);
// Make an array of list<Double_list_struct>::iterator of size floor(sqrt(N))
int iter_array_size = floor(sqrt(list_size));
list<Double_list_struct>::iterator* iter_array;
iter_array = new list<Double_list_struct>::iterator[iter_array_size];
// Initialize the iterators in iter_array to point to roughly evenly spaced locations in the list
initialize_iter_array(iter_array, iter_array_size, list_to_play_with);
for (int i = 0; i < iter_array_size; i++)
{
cout << "In main routine, iter_array[" << i << "]:" << (*(iter_array[i])).toString() << endl;
}
cout << "Reset it, and redo the output loop??" << endl;
iter_array[iter_array_size-1] = list_to_play_with.end();
iter_array[iter_array_size-1]--;
for (int i = 0; i < iter_array_size; i++)
{
cout << "In main routine, iter_array[" << i << "]:" << (*(iter_array[i])).toString() << endl;
}
}
/************************************************Subroutine code**************************************/
// Output all elements of a list to cout.
template <typename Tplt>
void output_list(list<Tplt> to_out)
{
...not important here
}
template <typename Tplt>
void initialize_list(list<Tplt> &alist, int size)
{
...not important here
}
template <typename Tplt>
void initialize_iter_array(typename list<Tplt>::iterator* itar, int size, list<Tplt> alist)
{
itar[0] = alist.begin();
itar[size-1] = alist.end();
itar[size-1]--; // Recall that .end() makes an iterator point *past* the end...
// Find out how big the list is
int listsize = 0;
for (typename list<Tplt>::iterator it = itar[0]; it != itar[size-1]; it++)
{
listsize = listsize + 1;
}
int spacing = floor(listsize/(size-1));
cout << "In initialize_iter_array(): created itar[0]: " << (*itar[0]).toString() << endl;
for (int i = 1; i < size-1 ; i++)
{
itar[i] = itar[i-1];
for (int j = 0; j < spacing; j++)
{
itar[i]++;
}
cout << "In initialize_iter_array(): created itar[" << i << "]: " << (*itar[i]).toString() << endl;
}
cout << "In initialize_iter_array(): created itar[" << size-1 << "]: " << (*itar[size-1]).toString() << endl;
}
This generates output
Sorted list is:
struct[15] = 0.135837
struct[1] = 0.200995
struct[12] = 0.217693
...SNIP...
struct[8] = 0.863816
struct[14] = 0.887851
struct[2] = 0.893622
struct[10] = 0.925875
In initialize_iter_array(): created itar[0]: struct[15] = 0.135837
In initialize_iter_array(): created itar[1]: struct[5] = 0.314127
In initialize_iter_array(): created itar[2]: struct[11] = 0.704419
In initialize_iter_array(): created itar[3]: struct[10] = 0.925875
In main routine, iter_array[0]:struct[15] = 0.135837
In main routine, iter_array[1]:struct[5] = 0.314127
In main routine, iter_array[2]:struct[11] = 0.704419
In main routine, iter_array[3]:struct[-1] = 6.21551e-71
Reset it, and redo the output loop??
In main routine, iter_array[0]:struct[15] = 0.135837
In main routine, iter_array[1]:struct[5] = 0.314127
In main routine, iter_array[2]:struct[11] = 0.704419
In main routine, iter_array[3]:struct[10] = 0.925875
So, you see, iter_array[3] is correct inside the initialization subroutine, but has "moved" after the subroutine exits. I then reset it from outside the subroutine, but obviously I'd like to not have to do that...
My best guess is that there is something subtle going on here with how the assignment operator works for iterators. But I'm very puzzled.
initialize_iter_array takes the list by value, which means it's putting iterators that point into the parameter copy of the list, not the original list. You probably meant to pass the list by const& instead.