I'm having a problem which I think is related to creating a copy of an object. I have a map class that contains a std::map with a bunch of classes defining the different tile types indexed by their ID, and a bunch of tiles in a vector each with a pointer to one of those classes. Additionally I have another class, Scenario, that contains the map.
The problem comes when I create a Scenario object and try to do anything with its map. There must be some problem when copying the pointers but I haven't been able to figure it out.
I have tried to reduce the code to what's necessary to reproduce the problem:
#include <string>
#include <map>
#include <vector>
#include <iostream>
using namespace std;
class TileInfo {
string name;
public:
int id;
TileInfo() {};
TileInfo(int id, string name);
string getName() const;
};
TileInfo::TileInfo(int id, string name)
{
this->id = id;
this->name = name;
}
string TileInfo::getName() const
{
return name;
}
class Tile
{
public:
TileInfo* info;
Tile() {};
Tile(const Tile & other, map<int,TileInfo> & info);
Tile (TileInfo* info);
string getName() const;
};
Tile::Tile (TileInfo* tileInfo)
{
this->info = tileInfo;
}
Tile::Tile( const Tile & other, map<int,TileInfo> & tileInfo )
{
map<int,TileInfo>::iterator it = tileInfo.find(other.info->id);
if(it == tileInfo.end())
this->info = NULL;
else
this->info = &(it->second);
}
string Tile::getName() const
{
return info->getName();
}
class Map
{
vector <vector <Tile>> tiles;
map <int, TileInfo> tileInfo;
public:
void print() const;
Map() {};
Map(map<int,TileInfo> tileInfo);
Map(const Map & other);
string getName() const;
void loadTiles();
};
Map::Map(map<int,TileInfo> tileInfo)
{
this->tileInfo = tileInfo;
}
Map::Map(const Map & other)
{
this->tileInfo = other.tileInfo;
for(unsigned int i = 0; i < other.tiles.size(); i++)
{
vector<Tile> line;
for(unsigned int j = 0; j < other.tiles[i].size(); j++)
{
Tile tmpTile = Tile(other.tiles[i][j],tileInfo);
line.push_back(tmpTile);
}
this->tiles.push_back(line);
}
}
void Map::print() const
{
for(unsigned int i = 0; i < tiles.size(); i++)
{
for(unsigned int j = 0; j < tiles[i].size(); j++)
{
cout << "Tile at " << i << ", " << j << " is a "; cout << tiles[i][j].getName() << endl;
}
}
}
void Map::loadTiles()
{
for(unsigned int i = 0; i < 3; i++)
{
vector <Tile> line;
for(unsigned int j = 0; j < 3; j++)
{
map<int,TileInfo>::iterator ite = tileInfo.find(i);
if( ite!=tileInfo.end())
line.push_back(Tile(&(ite->second)));
else
{
line.push_back(Tile(NULL));
}
}
tiles.push_back(line);
}
}
class Scenario
{
public:
Map map;
Scenario() {};
Scenario(Map map);
};
Scenario::Scenario(Map map)
{
this->map = map;
this->map.print();
}
Map createMap()
{
map<int,TileInfo> tmpInfo;
for(unsigned int i = 0; i < 3; i++)
{
tmpInfo.insert(pair<int,TileInfo>(i,TileInfo(i,"testname")));
}
Map rtrnVal = Map(tmpInfo);
rtrnVal.loadTiles();
return rtrnVal;
}
int main()
{
Map newMap = createMap();
Scenario newScenario = Scenario(newMap);
newScenario.map.print();
int input;
cin >> input;
}
The print() at the end of the Scenario constructor prints the map's contents correctly, but the one after the scenario is assigned causes a crash. Any help would be appreciated.
The original std::map where your original TileInfo objects are stored is here:
map<int,TileInfo> tmpInfo;
for(unsigned int i = 0; i < 3; i++)
{
tmpInfo.insert(pair<int,TileInfo>(i,TileInfo(i,"testname")));
}
That is the map the houses the pairs. you then make a copy of said map. It creates copies of all your TileInfo objects, which is ok.
Map rtrnVal = Map(tmpInfo);
But this is where the wheels begin to come off. You fire loadTiles here, which sets up your map of vectors that addres the relative TileInfo objects in rtrnVal. This code:
rtrnVal.loadTiles();
leads to this code. Note that you're pushing the addresses of your mapped TileInfo objects into your vectors. The map where these reside is this objects map.
void Map::loadTiles()
{
for(unsigned int i = 0; i < 3; i++)
{
vector <Tile> line;
for(unsigned int j = 0; j < 3; j++)
{
map<int,TileInfo>::iterator ite = tileInfo.find(i);
if( ite!=tileInfo.end())
line.push_back(Tile(&(ite->second)));
else
{
line.push_back(Tile(NULL));
}
}
tiles.push_back(line);
}
}
Finally, back where we started you do this:
return rtrnVal;
This makes a copy of the Map, which makes a copy of the map of vectors, which contain pointers to the TileInfo in the map within rtrnVal that is about to be destroyed on function exit. The result back on the caller side.
Map newMap = createMap();
newMap now holds dangling pointers to TileInfo objects that were in rtrnVal in createMap.
Possible Solution
Rather than having a map of vectors containing pointers to TileInfos, consider a map of vectors of indexes, where each index keys to a 0-based slot in a TileInfo std::vector that is contained along side the map. I can see what you were trying to do (singular TileInfos shared across multiple cells) so you still reap that benefit. The vector will come along for the ride wherever the map does (they're owned by the same Map object) and copying won't harm anything because both the map (of indexes) and the vector (of TileInfo) will copy safely. In the end you still get what you want (singular TileInfos) but now-indexed by number rather than pointer. As a bonus, you don't have to deal with rebasing pointers to TileInfo from one map to another in a custom copy-ctor. Its all relative (0.. n-1)
Best of luck.
You're storing pointers to TileInfo:
class Tile
{
public:
TileInfo* info;
...
But once the object is copied, the pointers are copied across, but the TileInfo that they pointed to have been destructed, so they are referencing invalid memory = crash.
Consider implementing the copy constructor / copy operator to handle it.
Related
I am having trouble passing an array of object pointers from main() to a function from different class.
I created an array of object pointers listPin main() and I want to modify the array with a function editProduct in class Manager such as adding new or edit object.
Furthermore, I want to pass the whole listP array instead of listP[index]. How to achieve this or is there any better way? Sorry, I am very new to c++.
#include <iostream>
using namespace std;
class Product
{
protected:
string id, name;
float price;
public:
Product()
{
id = "";
name = "";
price = 0;
}
Product(string _id, string _name, float _price)
{
id = _id;
name = _name;
price = _price;
}
};
class Manager
{
protected:
string id, pass;
public:
Manager(string _id, string _pass)
{
id = _id;
pass = _pass;
}
string getId() const { return id; }
string getPass() const { return pass; }
void editProduct(/*array of listP*/ )
{
//i can edit array of listP here without copying
}
};
int main()
{
int numProduct = 5;
int numManager = 2;
Product* listP[numProduct];
Manager* listM[numManager] = { new Manager("1","alex"), new Manager("2", "Felix") };
bool exist = false;
int index = 0;
for (int i = 0; i < numProduct; i++) { //initialize to default value
listP[i] = new Product();
}
string ID, PASS;
cin >> ID;
cin >> PASS;
for (int i = 0; i < numManager; i++)
{
if (listM[i]->getId() == ID && listM[i]->getPass() == PASS) {
exist = true;
index = i;
}
}
if (exist == true)
listM[index]->editProduct(/*array of listP */);
return 0;
}
Since the listP is a pointer to an array of Product, you have the following two option to pass it to the function.
The editProduct can be changed to accept the pointer to an array of size N, where N is the size of the passed pointer to the array, which is known at compile time:
template<std::size_t N>
void editProduct(Product* (&listP)[N])
{
// Now the listP can be edited, here without copying
}
or it must accept a pointer to an object, so that it can refer the array
void editProduct(Product** listP)
{
// find the array size for iterating through the elements
}
In above both cases, you will call the function as
listM[index]->editProduct(listP);
That been said, your code has a few issues.
First, the array sizes numProduct and numManager must be compiled time constants, so that you don't end up creating a non-standard variable length array.
Memory leak at the end of main as you have not deleted what you have newed.
Also be aware Why is "using namespace std;" considered bad practice?
You could have simply used std::array, or std::vector depending on where the object should be allocated in memory. By which, you would have avoided all these issues of memory leak as well as pointer syntaxes.
For example, using std::vector, you could do simply
#include <vector>
// in Manager class
void editProduct(std::vector<Product>& listP)
{
// listP.size() for size of the array.
// pass by reference and edit the listP!
}
in main()
// 5 Product objects, and initialize to default value
std::vector<Product> listP(5);
std::vector<Manager> listM{ {"1","alex"}, {"2", "Felix"} };
// ... other codes
for (const Manager& mgr : listM)
{
if (mgr.getId() == ID && mgr.getPass() == PASS)
{
// ... code
}
}
if (exist == true) {
listM[index]->editProduct(listP);
}
You cannot have arrays as parameters in C++, you can only have pointers. Since your array is an array of pointers you can use a double pointer to access the array.
void editProduct(Product** listP){
and
listM[index]->editProduct(listP);
Of course none of these arrays of pointers are necessary. You could simplify your code a lot if you just used regular arrays.
Product listP[numProduct];
Manager listM[numManager] = { Manager("1","alex"), Manager("2", "Felix")};
...
for(int i = 0; i < numManager; i++ ){
if(listM[i].getId() == ID && listM[i].getPass() == PASS) {
exist = true;
index = i;
}
}
if(exist == true){
listM[index].editProduct(listP);
}
I am trying to write C++ code suitable for object oriented programming.
I have two classes, namely, Student and Course. In the Student class, I have quiz_scores which is a 1-D array of 4 integers. I need both set and get methods, both are used in natural common way.
In the following, I implement setQuizScores method:
void Student :: setQuizScores(int* quizscores){
for(int i = 0; i<4; i++){
quiz_scores[i] = quizscores[i];
}
Where quizscores are my private members.
Now, next thing is that I want to return this quiz_scores array in the getQuizScores for each students of Student class.
However, the problem is that C++ does not allow us to return arrays directly. Instead, I want the structure of my code as following:
int Student :: getQuizScores(){
Do something;
return the elements of quiz_scores;
}
How can I do that efficiently?
I prefer not to use the Standard Template Library (STL), so I need to create my own arrays and access them according to the explanation above.
There are a few ways how you could return an array:
Pass in an array to copy to
void Student::getQuizScores(int* out) {
for(int i = 0; i < 4; i++)
out[i] = quiz_scores[i];
}
Student student;
int scores[4];
student.getQuizScores(scores);
// use scores[0], etc...
return a struct containing the array
struct Scores {
int values[4];
};
Scores Student::getQuizScores() {
Scores s;
for(int i = 0; i < 4; i++)
s.values[i] = quiz_scores[i];
return s;
}
Student student;
Scores s = student.getQuizScores();
// use s.values[0], etc...
return a reference to the quiz_scores array inside the class
using Scores = int[4];
Scores const& Student::getQuizScores() const {
return quiz_scores;
}
Student student;
Scores const& scores = student.getQuizScores();
// use scores[0], etc...
Just as setQuizScores() is able to take a pointer to an array, so too can getQuizScores() return a pointer to the quiz_scores member array, eg:
const int* Student::getQuizScores() const {
// do something...
return quiz_scores;
}
The caller can then access the array elements as needed, eg:
Student s;
...
const int *scores = s.getQuizScores();
for(int i = 0; i < 4; ++i){
cout << scores[i] << ' ';
}
Alternatively, since the array is fixed size, you can return a reference to the array instead, eg:
typedef int scoresArr[4];
scoresArr quiz_scores;
...
const scoresArr& Student::getQuizScores() const {
// do something...
return quiz_scores;
}
Student s;
...
const scoresArr &scores = s.getQuizScores();
for(int i = 0; i < 4; ++i){
cout << scores[i] << ' ';
}
You can return a pointer to the quiz_scores array through getQuizScores method as shown below:
Version 1: Using trailing return type
auto getQuizScores() -> int(*)[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
Now you can use this returned pointer to initialize other arrays. One possible example would be:
#include <iostream>
struct Student
{
int quiz_scores[4]= {1,2,3,4};
//getQuizScores returns a pointer to an array of size 4 with element of type int
auto getQuizScores() -> int(*)[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
void setQuizScores(int* quizscores)
{
for(int i = 0; i<4; i++)
{
quiz_scores[i] = quizscores[i];
}
}
};
int main()
{
Student s;
int arr[4];
for(int i = 0; i< 4; ++i)
{
arr[i] = (*s.getQuizScores())[i];
std::cout<<arr[i]<<std::endl;
}
return 0;
}
Version 2: Without using trailing return type
int (*getQuizScores())[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
Version 2 is the same as version 1 except that this time the getQuizScores method does not uses trialing return type.
There are other possibilities also like returning a reference to the quiz_scores array.
In the code below, the Struct1 pointer in Struct2 should point consistently to a certain object of Struct1. Each of these structs is contained in a vector.
However, when I output the index variable of the Struct1 objects pointed to in the Struct2 objects, in some cases the wrong one is returned.
Why does a pointer to an object contained in a vector sometimes point to a different object in the vector?
struct Struct1
{
size_t index;
std::vector<size_t> data;
}
struct Struct2
{
Struct1 *s1;
}
class MyClass
{
std::vector<Struct1> s1s;
std::vector<Struct2> s2s;
size_t propIndex = 0;
void input(QByteArray &line)
{
if (line == "1") {
for (size_t i = s1s.size(); i <= propIndex; i++) {
s1s.push_back({ .index = i, .data= {} });
}
QByteArrayList list = getList();
for (auto id : list) s1s.at(propIndex).data.push_back(id.toULongLong());
}
else {
if (propIndex == s2s.size()) {
s2s.push_back({ .s1 = nullptr });
}
if (line == "2") {
size_t index = getIndex();
for (size_t i = s1s.size(); i <= index; i++) {
s1s.push_back({ .index = i, .data= {} });
}
s2s.at(propIndex).s1 = &s1s.at(index);
}
}
propIndex++;
}
QByteArrayList output()
{
QByteArrayList output;
for (auto s2 : s2s) {
output += QByteArray::number(s2.s1->index) + "\n";
}
return output;
}
}
The problem is that you take a pointer to an item in a vector:
s2s.at(propIndex).s1 = &s1s.at(index);
The vector is a dynamic structure and its data may be reallocated when it grows. So any push_back() could invalidate all the pointers:
s1s.push_back({ .index = i, .data= {} });
Note that the vector allocation algorithm is designed to reserve space for several elements when it needs to grow. This explains that the issue appears only from time to time.
One solution could be to keep not a pointer but the index of the elements together with the a pointer to the vector.
As discussed in the comments on the accepted answer, a possible solution is using unique_ptrs.
In the OPs example code, the Struct1 vector would be replaced by a unique_ptr vector:
std::vector<std::unique_ptr<Struct1>> s1s;
Then the objects need to be allocated independently and a unique pointer needs to be created to store in the vector, and we need to call .get() to get a raw pointer to the object:
if (line == "1") {
for (size_t i = s1s.size(); i <= propIndex; i++) {
s1s.push_back(std::unique_ptr<Struct1>(new Struct1({ .index = i, .data = {} })));
}
QByteArrayList list = getList();
for (auto id : list) s1s.at(propIndex)->data.push_back(id.toULongLong());
}
...
if (line == "2") {
size_t index = getIndex();
for (size_t i = s1s.size(); i <= index; i++) {
s1s.push_back(std::unique_ptr<Struct1>(new Struct1({ .index = i, .data = {} })));
}
s2s.at(propIndex).s1 = s1s.at(index).get();
I am having problems with my constructor in class World.
I created a 2D array with pointers where each entry in the array is of type Organism, hence the line of code:
Organism* grid[20][20];
When I run my program, I only see
hello
and after that, I get a message saying that my program has stopped working. I'm pretty sure it's the line of code
grid[i][j]->symbol = ' ';
that's causing the problem. Just to see what would happen, I changed that line to
grid[i][j];
and didn't get any errors. But, the moment I put ->, I seem to get errors.
Is there a reason why my program stops working after I put ->? Any help would be appreciated.
This is my code:
#include <iostream>
using namespace std;
class Organism
{
public:
char symbol;
};
class World
{
public:
World();
private:
Organism* grid[20][20];
};
int main()
{
World world;
return 0;
}
World::World()
{
for(int i = 0; i < 20; i++)
for(int j = 0; j < 20; j++)
{
cout << "hello" << endl;
grid[i][j]->symbol = ' ';
cout << "here" << endl;
}
}
You have an array of pointers to Organism.
Pointers can not hold any data other than an address. They can only point to memory (that holds data).
Your array's pointers does not point to anything, thats why you get undefined behaviour when you try to assign data to where the pointers point at.
You need to allocate memory for your array.
Organism grid[20][20]; // Create an array of objects (not pointers).
/* ... */
grid[i][j].symbol = ' ';
Same using dynamic memory:
class World {
public:
World();
~World(); // Rule of Three.
World(const World&) = delete; // Rule of Three.
World& operator=(const World&) = delete; // Rule of Three.
private:
Organism** grid;
};
World::World() {
grid = new Organism*[20]; // Allocate memory to point to.
for(std::size_t i = 0; i != 20; ++i) {
grid[i] = new Organism[20]; // Allocate memory to point to.
for(std::size_t j = 0; j != 20; ++j) {
cout << "hello" << endl;
grid[i][j].symbol = ' ';
cout << "here" << endl;
}
}
}
// Destructor needed to deallocate memory (otherwise it will leak).
World::~World()
{
for (std::size_t i = 0; i != 20; ++i) {
delete[] grid[i];
}
delete[] grid;
}
Now you can see how complicated it gets when using dynamic memory and why it's recommended to prefer to use automatic storage duration (i.e. create objects, not pointers and new/delete).
Even better is to use a container from the standard library for storing your elements as e.g. std::vector.
Related:
What is The Rule of Three?
Change
Organism* grid[20][20];
To
Organism grid[20][20];
and use
grid[i][j].symbol = '';
instead of
grid[i][j]->symbol = '';
and add a default constructor to Organism
class Organism
{
Organism();
...
}
or make Organism a struct
struct Oranism
{
...
}
I've been working on a very in depth project for one of my classes. It supposed to read in Person objects and put them into a hash table. I'm still trying to get my head around the concept of a hash table so any help would be appreciated.
It will be hashing based on last name and since some people may have the same last name, I was going to make each bucket a vector of Person objects. I'm trying to test the class by adding a person to the hash function and then returning it. My code compiles successfully but I get a thread error in the put function on this line: table[index].push_back(p);
Could anyone please help me figure out what is going wrong? Thank you!
int main()
{
HashTable ht(10);
ht.put(p1, p1->lName);
ht.getName("Booras");
}
HashTable:
#include "Person.h"
#include <vector>
class HashTable: public DataStructures
{
private:
vector<vector<Person>> table;
public:
HashTable(int tableSize);
~HashTable();
int tableSize;
void getName(string str); //prints out friends with matching name
void put(Person p, string str);
void remove(Person *p, string str);
int hash(string str);
};
HashTable::HashTable(int tableSize)
{
vector< vector<Person> > table(tableSize, vector<Person>(tableSize));
for (int i = 0; i < tableSize; i++) {
table.push_back(vector<Person>()); // Add an empty row
}
}
HashTable::~HashTable()
{
}
//Find a person with the given last name
void HashTable::getName(string key)
{
int index = hash(key);
for(int i=0; i<table[index].size(); i++)
{
if(table[index][i].lName.compare(key) == 0)
std::cout << "Bucket: " << index << "Bin: " << i;
table[index][i].print();
}
//create exception for person not found
}
void HashTable::put(Person p, string str)
{
int index = hash(str);
table[index].push_back(p);
}
void HashTable::remove(Person *p, string str)
{
int index = hash(str);
int i=0;
while(&table[index][i] != p && i<table[index].size())
i++;
for(int j=i; j<table[index].size()-1; j++)
table[index][j] = table[index][j+1];
table[index].pop_back();
}
int HashTable::hash(string str)
{
int hashValue = 0;
for(int i=0; i<str.length(); i++)
{
hashValue = hashValue + int(str[i]);
}
hashValue %= tableSize;
if(hashValue<0) hashValue += tableSize;
return hashValue;
}
Main:
int main() {
Person *p1 = new Person("Kristy", "Booras", "Reston", "03/15");
HashTable ht(10);
ht.put(*p1, p1->lName);
ht.get("Booras");
return 0;
}
You don't show us the HashTable::hash(string) member function, but I'd assume that your problems originate in the HashTableconstructor: You don't initialize the tableSize member variable, which you'll need to calculate a valid hashed index.
While looking at the constructor:
HashTable::HashTable(int tableSize)
{
vector< vector<Person> > table(tableSize, vector<Person>(tableSize));
This has initialized table to have tableSize non-empty elements, for a total of tableSize * tableSizedefault-constructed Person objects.
for (int i = 0; i < tableSize; i++) {
table.push_back(vector<Person>()); // Add an empty row
}
}
Now you have added more rows, so that table.size() == 2*tableSize, with the first half of entries non-empty (as explained above) and the second half holding empty vectors.
That is probably not what you intended.
And in all of that you haven't initialized the member tableSize. It easily gets confusing, if you use local variables or argument names that hide member names.