I'm working my way through Accelerated C++ and have decided to mess around with the one of structs that were defined in there. While doing so, I've come across a problem: creating a vector of these structs and modifying the elements in each one seems to modify the elements in all of them.
I realize that this probably means I've initialized all the structs in the vector to a struct at a single memory address, but I used the .push_back() method to insert "dummy" structs in to the vector. I was under the impression that .push_back() pushes a copy of its argument, effectively creating a new struct.
Here is the header for the struct:
#ifndef _STUDENT_INFO__CHAPTER_9_H
#define _STUDENT_INFO__CHAPTER_9_H
#include <string>
#include <iostream>
#include <vector>
class Student_info9{
public:
Student_info9(){homework = new std::vector<double>;};
Student_info9(std::istream& is);
std::string getName() const {return name;};
double getMidterm() const {return midterm;};
double getFinal() const {return final;};
char getPassFail() const {return passFail;};
std::vector<double> *getHw(){return homework;};
void setName(std::string n) {name = n;};
void setMidterm(double m) {midterm = m;};
void setFinal(double f) {final = f;};
private:
std::string name;
double midterm;
double final;
char passFail;
std::vector<double> *homework;
};
#endif /* _STUDENT_INFO__CHAPTER_9_H */
And here is the code that i'm fooling around with (excuse the excessive print statements... the result of some time trying to debug :) ):
vector<Student_info9> did9, didnt9;
bool did_all_hw9(Student_info9& s)
{
vector<double>::const_iterator beginCpy = s.getHw()->begin();
vector<double>::const_iterator endCpy = s.getHw()->end();
return(find(beginCpy, endCpy, 0) == s.getHw()->end());
}
void fill_did_and_didnt9(vector<Student_info9> allRecords)
{
vector<Student_info9>::iterator firstDidnt = partition(allRecords.begin(), allRecords.end(), did_all_hw9);
vector<Student_info9> didcpy(allRecords.begin(), firstDidnt);
did9 = didcpy;
vector<Student_info9> didntcpy(firstDidnt, allRecords.end());
didnt9 = didntcpy;
}
int main(int argc, char** argv) {
vector<Student_info9> students;
Student_info9 record;
for(int i = 0; i < 5; i++)
{
students.push_back(record);
}
for(int i = 0; i < students.size(); i++)
{
students[i].setMidterm(85);
students[i].setFinal(90);
students[i].getHw()->push_back(90);
std::cout << "student[" << i << "]'s homework vector size is " << students[i].getHw()->size() << std::endl;
students[i].getHw()->push_back(80);
std::cout << "student[" << i << "]'s homework vector size is " << students[i].getHw()->size() << std::endl;
students[i].getHw()->push_back(70);
std::cout << "student[" << i << "]'s homework vector size is " << students[i].getHw()->size() << std::endl;
std::cout << "Just pushed back students[" << i << "]'s homework grades" << std::endl;
if(i == 3)
students[i].getHw()->push_back(0);
}
std::cout << "student[3]'s homework vector size is " << students[3].getHw()->size() << std::endl;
for(vector<double>::const_iterator it = students[3].getHw()->begin(); it != students[3].getHw()->end(); it++)
std::cout << *it << " ";
std::cout << std::endl;
std::cout << "students[3] has " << ( ( find(students[3].getHw()->begin(),students[3].getHw()->end(), 0) != students[3].getHw()->end()) ? "atleast one " : "no " )
<< "homework with a grade of 0" << std::endl;
fill_did_and_didnt9(students);
std::cout << "did9's size is: " << did9.size() << std::endl;
std::cout << "didnt9's size is: " << didnt9.size() << std::endl;
}
As you can see by the print statements, it seems that the homework grades are being added only to one Student_info9 object, copies of which seem to be populating the entire vector. I was under the impression that if you were to use consecutive copies of .push_back() on a single object, it would create copies of that object, each with different memory addresses.
I'm not sure if that's the source of the problem, but hopefully someone could point me in the right direction.
Thanks.
When you push a StudentInfo onto the vector, it is indeed copied, so that's not the problem. The problem is the vector containing the homework grades. Since you only store a pointer to that vector in StudentInfo, only the pointer, not the vector, is copied when you copy a StudentInfo. In other words you have many different StudentInfos that all have a pointer to the same homework vector.
To fix this you should define a copy constructor which takes care of copying the homework vector.
Have you learned about the copy constructor yet? If so, think about what is happening with vector<Student_info9> students on push_back().
Specifically, what happens with this pointer.
std::vector<double> *homework;
The line Student_info9 record; constructs a Student_info9 using the first constructor. This first constructor creates a vector and stores a pointer to it as a member variable. You then proceed to add a copy of this Student_info9 to a vector 5 times. Each copy has a pointer to the same vector.
Your StudentInfo9 class contanis a pointer to a std::vector<double>, which means in the default copy constructor (which will be called when you add a StudentInfo9 object to your vector), the pointer itself is copied. That means all of your StudentInfo9 objects have the same homework vector.
Does that make sense? Please refer to http://pages.cs.wisc.edu/~hasti/cs368/CppTutorial/NOTES/CLASSES-PTRS.html for a more in depth look at pointers and copy constructors.
Related
I wrote the following function and a simple class, while trying to understand how expensive a work with vector can be.
void gen_random(string & str, const int len)
{
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
srand((unsigned int)time(NULL));
str.reserve(len);
for (int i = 0; i < len; ++i)
{
str += alphanum[rand() % (sizeof(alphanum) - 1)];
}
}
class Person
{
public:
//CTOR with parameter
Person(u_int32_t Id)
{
std::cout << "\033[1;32mPerson CTOR: " << Id << "\033[0m" << std::endl;
m_Id = Id;
m_RandSid = new string;
gen_random(*m_RandSid, 10);
}
//CCTOR
Person(const Person & p)
{
std::cout << "\033[1;31mPerson CCTOR: " << p.m_Id << "\033[0m" << std::endl;
m_Id = p.m_Id;
m_RandSid = p.m_RandSid; //trigger string operator=()
}
//MCTOR
Person(Person&& p)
{
std::cout << "\033[1;34mPerson MCTOR: " << p.m_Id << "\033[0m" << std::endl;
m_Id = p.m_Id;
m_RandSid = p.m_RandSid;
p.m_RandSid = nullptr;
}
//DTOR
~Person()
{
std::cout << "\033[1;33mPerson DTOR: "<<m_Id <<"\033[0m"<< std::endl;
if (nullptr != m_RandSid)
{
delete m_RandSid;
}
}
u_int32_t m_Id;
string * m_RandSid;
};
and driver :
int main()
{
int a;
vector<Person> v;
for (int i = 0; i < 2; ++i)
{
std::cout <<std::endl<< "inserting person #" << i << std::endl;
std::cout << "Vector size = " << v.size()<< " Vector capacity = " << v.capacity() << std ::endl;
v.emplace_back(i);
std::cout << *v[i].m_RandSid << std::endl;
std::cout << "Vector size = " << v.size()<< " Vector capacity = " << v.capacity() << std ::endl;
}
std::cout<<std::endl<<"--------------------------------------------------------"<<std::endl;
return 0;
}
when I run this program, I the following output:
inserting person #0
Vector size = 0 Vector capacity = 0
Person CTOR: 0
07QoUmgEe6
Vector size = 1 Vector capacity = 1
inserting person #1
Vector size = 1 Vector capacity = 1
Person CTOR: 1
Person CCTOR: 0
Person DTOR: 0
07QoUmgEe6
Vector size = 2 Vector capacity = 2
--------------------------------------------------------
Person DTOR: 0
free(): double free detected in tcache 2
I don't understand where else I perform another free :\
The other problem is that the string is randomized per execution and not per object,
If srand is performed on each execution, why all the string look the same ?
In your copy and move constructor you simply copy raw pointers, which makes 2 pointers to point to the same memory, and when both objects destroyed you get double deallocation:
Person(const Person & p)
{
std::cout << "\033[1;31mPerson CCTOR: " << p.m_Id << "\033[0m" << std::endl;
m_Id = p.m_Id;
m_RandSid = p.m_RandSid; // now both pointers point to the same memory
}
It is not clear why you need dynamically allocated string objects, you should just store objects by value, but if you do need that you should use smart pointers (either std::shared_ptr or std::unique_ptr depends of what ownership you need). That will not only make your problem disappear but you would not have to provide copy and move constructor manually, compiler generated ones would be sufficient.
Note, your class is also missing proper copy and move assignment operator, though it is not exposed in code shown it still violates the rule of three/five/zero and you may have problems with your code later.
Your copy constructor copies the pointer value, and should be doing a deep copy (allocate a new string). In a simple design like this I would avoid using new/free for the string.
You initialize (actually reset) rand with time with second precision - time(). Your app probably finishes in less than a second, hence the similar strings. Initialize rand only once, eg when starting the app.
This is most probably trivial and I'm confusing struct allocation and pointers somehow, I apologize for this. I have read answers to similar questions but it didn't help. The code is, as always, way more complicted, this is a reduction from 3000+ lines of code to the gist.
The output I expected was
prep 1
main 1
Instead, I get
prep 1
main 0
This is the code:
#include <iostream>
#include <vector>
using namespace std;
struct Entry
{
vector<int> list;
};
struct Registry
{
vector<Entry> entries;
void prep()
{
Entry* entry = new Entry();
entries.push_back(*entry);
entry->list.push_back(0);
cout << "prep " << entry->list.size() << "\n";
}
};
int main()
{
Registry registry;
registry.prep();
cout << "main " << registry.entries[0].list.size() << "\n";
return 1;
}
You don't store pointers in your vector<Entry> so you should not use new. Instead add a default constructed Entry using emplace_back.
A C++17 approach:
void prep()
{
Entry& entry = entries.emplace_back(); // returns a ref the added element
entry.list.push_back(0);
cout << "prep " << entry.list.size() << "\n";
}
Prior to C++17:
void prep()
{
entries.emplace_back(); // does NOT return a ref the added element
Entry& entry = entries.back(); // obtain a ref to the added element
entry.list.push_back(0);
cout << "prep " << entry.list.size() << "\n";
}
If you do want to create and maniplate your Entry before adding it to entries, you can do that too and then std::move it into entries.
void prep()
{
Entry entry;
entry.list.push_back(0);
cout << "prep " << entry.list.size() << "\n";
entries.push_back(std::move(entry)); // moving is a lot cheaper than copying
}
The problem is the order of the prep() function. If you change to push an element into the Element object, and then push it tho the entries vector, the behavior will be the expected.
void prep()
{
Entry* entry = new Entry();
entry->list.push_back(0);
entries.push_back(*entry);
cout << "prep " << entry->list.size() << "\n";
}
This is happening, because you uses a copy in the entries list.
It is also possible to store the pointer of the object therefore you can edit the actual instance after you pushed to the entries vector.
Edit:
As Ted mentioned, there is a memory leak, because the entry created with the new operator never deleted. Another approach could be to use smart pointers (however, in this small example it seems overkill, just use reference)
void prep()
{
std::unique_ptr<Entry> entry = std::make_unique<Entry>();
entry->list.push_back(0);
entries.push_back(*entry.get()); // get the pointer behind unique_ptr, then dereference it
cout << "prep " << entry->list.size() << "\n";
} // unique_ptr freed when gets out of scope
You need to change the implementation of prep():
void prep()
{
Entry entry;
entry.list.push_back(0);
entries.emplace_back(entry);
cout << "prep " << entries.back().list.size() << "\n";
}
There is no need to allocate a Entry on the heap just to make a copy of it.
I'm trying to build a relational database for the class I'm in.
what's happening is that when I process my "Facts" and "Queries" input, I create a new relation object. And then I print them out. If I run one at a time they process just fine, but if I run them back to back, the second one modifies the contents of the vector of tokens within the other relation object.
Database.h
class Database
{
private:
datalogProgram program;
Relation theSchemes;
Relation theFacts;
std::vector<Token> FactsOrder;
public:
Database(datalogProgram input);
Database();
~Database();
Relation processSchemes(datalogProgram processme);
Relation processFacts(datalogProgram processme);
};
Database.cpp
And I apologize for all of the cout's I've been trying to debug this things for hours!
#include "Database.h"
#include <sstream>
Database :: Database(datalogProgram input)
{
// So first I will make a map with relations representing the Schemes Facts and Queries
// Thus I will have a database of schemes facts and queries, rules excluded and ignored for now.
program = input;
theSchemes = processSchemes(program);
theFacts = processFacts(program);
// just checking on progress.
std::cout << "SCHEMES" << std::endl;
theSchemes.printRelation();
std::cout << "FACTS" << std::endl;
theFacts.printRelation();
}
Database :: Database() {}
Database :: ~Database() {}
Relation Database :: processSchemes(datalogProgram input)
{
Relation temp;
// LETS START WITH SCHEMES
std::cout << "processing schemes" << std::endl;
std::vector<Scheme>* schemes = input.returnSchemeList();
// Process First Scheme
// Populate this first vector with ID's from schemes.
// std::vector<Token*> firstTuple;
std::vector<Token*> firstTuple;
std::vector<Token> idListONE;
firstTuple.push_back(input.returnFirstScheme()->returnFirstID());
// std::vector<Token> idListONE;
idListONE = input.returnFirstScheme()->returnCLEANidLIST();
for(int i = 0; i < input.returnFirstScheme()->returnCLEANidLIST().size(); i++)
firstTuple.push_back(&idListONE[i]);
temp = *new Relation(input.returnFirstScheme()->returnName(), firstTuple);
// NOW I NEED TO PROCESS THE REST OF THE SCHEMES
//Take a scheme off of the list, and work on it just like I did above.
for(int j = 0; j < schemes->size(); j++) {
// Populate this first vector with ID's from schemes.
std::vector<Token*> first;
first.clear();
first.push_back(schemes->at(j).returnFirstID());
std::vector<Token> idLista;
idLista.clear();
idLista = schemes->at(j).returnCLEANidLIST();
for(int i = 0; i < schemes->at(j).returnCLEANidLIST().size(); i++)
first.push_back(&idLista[i]);
temp.relationInsert(schemes->at(j).returnName(), first);
}
return temp;
//
// At this point I shoudl have a map with "Schemes" pointing to Relation Objects.
// I want to verify that this is working, so print out all data collected so far.
}
Relation Database :: processFacts(datalogProgram input)
{
Relation temporary;
// NOW WE PROCESS FACTS
// Order does matter, so I will create a vector to use as a key.
std::cout << "procesing facts" << std::endl;
std::vector<Fact>* facts = input.returnFactList();
std::string OUT2;
std::ostringstream convert2;
convert2 << facts->size();
OUT2 = convert2.str();
std::cout << "THE NUMBER OF FACTS IS " << OUT2 << std::endl;
// NOW I NEED TO PROCESS THE REST OF THE
//Take a scheme off of the list, and work on it just like I did above.
std::vector<Token*> firstTuple;
std::vector<Token> idListONE;
for(int j = 0; j < facts->size(); j++) {
std::cout << "NEW ITERATION:" << std::endl;
if(j==0) {
std::cout << "processing first fact" << std::endl;
// is the first Fact!
firstTuple.clear();
std::cout << "processing first fact --> tuple" << std::endl;
firstTuple.push_back(facts->at(j).returnFirstString());
idListONE.clear();
std::cout << "FIRST STRINGLIST" << std::endl;
idListONE = *facts->at(j).returnCleanStringList();
for(int i = 0; i < idListONE.size(); i++) {
std::cout << "FIRST STRING ITER" << std::endl;
firstTuple.push_back(&idListONE[i]);
}
FactsOrder.push_back(*facts->at(j).returnName());
std::cout << "creating first fact" << std::endl;
temporary = Relation(facts->at(j).returnName(), firstTuple);
} else {
std::cout << "processing A fact (ITER)" << std::endl;
// Populate this first vector with ID's from schemes.
std::vector<Token*> first;
first.clear();
std::cout << "processing fact, firststring (ITER)" << facts->at(j).returnFirstString()->getTokensValue() << std::endl;
first.push_back(facts->at(j).returnFirstString());
std::vector<Token> idLista;
idLista.clear();
std::cout << "getting stringlist (ITER)" << std::endl;
idLista = *facts->at(j).returnCleanStringList();
for(int i = 0; i < idLista.size(); i++) {
std::cout << "processing stringlist (ITER) ITER" << std::endl;
first.push_back(&idLista[i]);
}
FactsOrder.push_back(*facts->at(j).returnName());
std::cout << "adding fact" << std::endl;
temporary.relationInsert(facts->at(j).returnName(), first);
}
}
return temporary;
}
relation.cpp
Just so you can see it
Relation :: Relation(Token* key,std::vector<Token*> tuple)
{
std::pair<Token*,std::vector<Token*> > mypair (key,tuple);
contents.insert(mypair);
}
Relation :: Relation() {}
Relation :: ~Relation() {}
void Relation :: relationInsert(Token* key,std::vector<Token*> tuple)
{
std::pair<Token*,std::vector<Token*> > mypair (key,tuple);
contents.insert(mypair);
}
void Relation :: printRelation()
{
std::cout << "PRINT RELATION CALLED" << std::endl;
std::multimap<Token*,std::vector<Token*> >::iterator mapIT;
for(mapIT = contents.begin() ; mapIT != contents.end() ; mapIT ++) {
std::cout << "Key: " << mapIT->first->getTokensValue() "\nValues:" << std::endl;
for(int x = 0; x< mapIT->second.size() ; x++)
std::cout << " " << mapIT->second.at(x)->getTokensValue() << std::endl;
}
}
To solve your problem you must figure out object / pointer ownership in your code. Relation holds a relation between a pointer to Token and a list of other pointer to Tokens. It is ok to keep Token* rather then a copy of Token. (Especially if tokens can be large words you don't want to copy). But who "owns" and manages the tokens?
Lets look at an example
std::vector<Token*> firstTuple;
std::vector<Token> idListONE;
idListONE is a vector to actual Tokens. It is a function local variable so it will be discarded when we exit the function.
firstTuple is a vector to pointers of Tokens.
You push into it in the following manner:
firstTuple.push_back(&idListONE[i]);
So firstTuple tokens are pointers to the internal tokens inside idListONE. That might be valid but you must remember that as soon as idListONE is released or its memory is changed (its size increased for instance) firstTuple becomes invalid, because it will now point at memory that was released and using it may have undefined results and will likely crash the program.
Actually a few lines later you make that mistake:
temporary = Relation(facts->at(j).returnName(), firstTuple);
temporary is a Relation that holds a list to pointer of Tokens. It copies the list that means that it copies the token pointers. However the pointers it copies are to Tokens that belong to idListONE as soon as you exit the function idListONE is released and the pointers inside the Relation are no longer valid and using them is likely one source of the problems you are seeing. There might be additional problems like this in the code
In general there seems to be a lot of confusion about working with pointers vs working with objects.
Look at the following statement:
temp = *new Relation(input.returnFirstScheme()->returnName(), firstTuple);
new Relation(...) will allocate memory on the heap and initialize a Relation.
temp = *<ptr> will use operator= to copy the content on the right into temp. The Relation on the heap is forgotten and its memory is leaked.
Another example:
idListONE.clear();
std::cout << "FIRST STRINGLIST" << std::endl;
idListONE = *facts->at(j).returnCleanStringList();
first you clear idListONE then you use the operator= to overwrite it with a new list.
Why did you clear a list you are writing over?
Why do you return a pointer to a list from returnCleanStringList()? instead of a copy list or a const ref to an internal list? If you decided returnCleanStringList() should return a list by pointer rather then by value then why is the first thing you do is copying it?
Finally you really should choose one style and conform to it. In the long run it makes code clearer.
If you Camelize variable names then always do: idListONE -> idListOne
Also avoid members like 'idListONE', do you really need a different variables for the first index?
I have a C++ vector. I want the vector to hold a variable number of objects.
Visual Studio 2012 is giving me an error:
Error: type name is not allowed
From this C++ code:
#include <iostream>
#include <vector>
using namespace std;
class testObject{
private:
int someInt;
public:
testObject(int a){ someInt=a; }
void show() { cout<<someInt<<endl; }
};
int main()
{
vector<testObject> testVector;
cout << "Initial size: " << testVector.size() <<endl;
for ( int i = 0; i < 3; i++ )
testVector.push_back(testObject(3));
cout << "New size: " << testVector.size() << endl;
for ( int j = 0; j < 3; j++ )
testVector[ j ].show();
system("pause");
}
But here's another sample of code that looks the same but it's not working.
void Dealer::setNumberOfPlayers( const int tNumber )
{
for ( int i = 0; i < tNumber; i++ )
vectorOfGamers.push_back(Player); // Player is a class that I created
}
Can I create vector to hold objects of Dealer, Bot and Player at the same time? How do I do that? As I know, all objects in vector should be of one type.
To answer the first part of your question, you must create an object of type Player before you can use it. When you say push_back(Player), it means "add the Player class to the vector", not "add an object of type Player to the vector" (which is what you meant).
You can create the object on the stack like this:
Player player;
vectorOfGamers.push_back(player); // <-- name of variable, not type
Or you can even create a temporary object inline and push that (it gets copied when it's put in the vector):
vectorOfGamers.push_back(Player()); // <-- parentheses create a "temporary"
To answer the second part, you can create a vector of the base type, which will allow you to push back objects of any subtype; however, this won't work as expected:
vector<Gamer> gamers;
gamers.push_back(Dealer()); // Doesn't work properly!
since when the dealer object is put into the vector, it gets copied as a Gamer object -- this means only the Gamer part is copied effectively "slicing" the object. You can use pointers, however, since then only the pointer would get copied, and the object is never sliced:
vector<Gamer*> gamers;
gamers.push_back(new Dealer()); // <-- Allocate on heap with `new`, since we
// want the object to persist while it's
// pointed to
Question 1:
vectorOfGamers.push_back(Player)
This is problematic because you cannot directly push a class name into a vector.
You can either push an object of class into the vector or push reference or pointer to class type into the vector. For example:
vectorOfGamers.push_back(Player(name, id))
//^^assuming name and id are parameters to the vector, call Player constructor
//^^In other words, push `instance` of Player class into vector
Question 2:
These 3 classes derives from Gamer. Can I create vector to hold objects of Dealer, Bot and Player at the same time? How do I do that?
Yes you can. You can create a vector of pointers that points to the base class Gamer.
A good choice is to use a vector of smart_pointer, therefore, you do not need to manage pointer memory by yourself. Since the other three classes are derived from Gamer, based on polymorphism, you can assign derived class objects to base class pointers. You may find more information from this post: std::vector of objects / pointers / smart pointers to pass objects (buss error: 10)?
You cannot insert a class into a vector, you can insert an object (provided that it is of the proper type or convertible) of a class though.
If the type Player has a default constructor, you can create a temporary object by doing Player(), and that should work for your case:
vectorOfGamers.push_back(Player());
I know the thread is already all, but as I was checking through I've come up with a solution (code listed below). Hope it can help.
#include <iostream>
#include <vector>
class Box
{
public:
static int BoxesTotal;
static int BoxesEver;
int Id;
Box()
{
++BoxesTotal;
++BoxesEver;
Id = BoxesEver;
std::cout << "Box (" << Id << "/" << BoxesTotal << "/" << BoxesEver << ") initialized." << std::endl;
}
~Box()
{
std::cout << "Box (" << Id << "/" << BoxesTotal << "/" << BoxesEver << ") ended." << std::endl;
--BoxesTotal;
}
};
int Box::BoxesTotal = 0;
int Box::BoxesEver = 0;
int main(int argc, char* argv[])
{
std::cout << "Objects (Boxes) example." << std::endl;
std::cout << "------------------------" << std::endl;
std::vector <Box*> BoxesTab;
Box* Indicator;
for (int i = 1; i<4; ++i)
{
std::cout << "i = " << i << ":" << std::endl;
Box* Indicator = new(Box);
BoxesTab.push_back(Indicator);
std::cout << "Adres Blowera: " << BoxesTab[i-1] << std::endl;
}
std::cout << "Summary" << std::endl;
std::cout << "-------" << std::endl;
for (int i=0; i<3; ++i)
{
std::cout << "Adres Blowera: " << BoxesTab[i] << std::endl;
}
std::cout << "Deleting" << std::endl;
std::cout << "--------" << std::endl;
for (int i=0; i<3; ++i)
{
std::cout << "Deleting Box: " << i+1 << " (" << BoxesTab[i] << ") " << std::endl;
Indicator = (BoxesTab[i]);
delete(Indicator);
}
return 0;
}
And the result it produces is:
Objects (Boxes) example.
------------------------
i = 1:
Box (1/1/1) initialized.
Adres Blowera: 0xdf8ca0
i = 2:
Box (2/2/2) initialized.
Adres Blowera: 0xdf8ce0
i = 3:
Box (3/3/3) initialized.
Adres Blowera: 0xdf8cc0
Summary
-------
Adres Blowera: 0xdf8ca0
Adres Blowera: 0xdf8ce0
Adres Blowera: 0xdf8cc0
Deleting
--------
Deleting Box: 1 (0xdf8ca0)
Box (1/3/3) ended.
Deleting Box: 2 (0xdf8ce0)
Box (2/2/3) ended.
Deleting Box: 3 (0xdf8cc0)
Box (3/1/3) ended.
// create a vector of unknown players.
std::vector<player> players;
// resize said vector to only contain 6 players.
players.resize(6);
Values are always initialized, so a vector of 6 players is a vector of 6 valid player objects.
As for the second part, you need to use pointers.
Instantiating c++ interface as a child class
I have the following class (I've trimmed irrelevant stuff):
class Example
{
private:
char* name;
int value[4];
int numVals;
public:
Example();
Example(char name[], int numVals, int, int, int, int);
~Example();
};
And here is the initialization constructor:
Example::Example(char na[], int vals, int v1, int v2, int v3, int v4)
{
name = new char[strlen(na)+1];
strcpy(name, na);
numVals = vals;
value[0] = v1;
value[1] = v2;
value[2] = v3;
value[3] = v4;
// cout << name; // this DOES print out the correct text
}
In my main(), I have an array of Example class, Example myArray[numRecs]. I then have a loop that uses the initialization constructor to fill the array:
myArray[i] = Example(name, numVals, v[0], v[1], v[2], v[3]);
Everything works as expected, however the name does not retain the characters I put into it. I checked using cout what the value is when it is passed into the constructor, and it was correct! However when I use my Example::Print(), it spits out a random character (the same character for each instance of Example).
Here is the Example::Print()
void Example::Print()
{
int total, avg;
total = avg = 0;
cout << left << setw(20) << name << '\t';
for(int i=0; i<4; i++){
if(i<numVals){
cout << left << setw(8) << value[i];
total += value[i];
} else
cout << left << setw(8) << " ";
}
avg = total/numVals;
cout << left << setw(8) << total <<
left << setw(8) << avg << endl;
}
Any ideas? Thanks!
Oh and also, this is for an assignment. We have been told to keep the name field as a char pointer, not a string. I am confused as to what I should be using for the init constructor (char* name or char name[] or.. is there a difference?)
EDIT: Here is the destructor and default constructor. I do not have a copy constructor yet as the assignment does not call for it and it is not used. I'll go ahead and make one for completeness anyway.
Example::~Example()
{
delete [] name;
}
Example::Example()
{
numVals = 0;
for(int i=0; i<4; i++)
value[i] = -1;
}
You should run your program through a memory debugger to witness the nightmare you've created!
You are using manual memory management in your class, yet you forgot to obey the Rule of Three: You didn't implement the copy constructor, assignment operator and destructor! Thus the temporary does allocate memory, copies the pointer (shallowly), and then probably invalidates the memory when it goes out of scope.
The immediate "fix my code" answer is that you have to implement a proper assignment operator and copy constructor to make a deep copy of the char array.
The "this is C++" answer is not to use pointers, new and arrays, and instead std::string.
An easy fix would be to store pointers to the Examples in the array instead of the actual objects. This way, you don't have to deal with copying (which you haven't implemented).