C++ Vectors insert new objects - c++

I'm a self-taught c++ programmer (still at novice level).
I think I got an idea of how c++ works, but I can't wrap my head around this:
I want to create and populate an std::vector with different elements of a defined-by-me class:
// other code
while (getline(cfgDataStream, cfgData)) //parsing cycle of the config file
{
std::stringstream ss(cfgData); //creating a stream in order to fill fields
ss >> string1 >> IP1 >> IP2 >> PORT2 >> INDEX;
//they are all strings save the last one, which is a int
if (ss.fail())
{
//bad things happen
}
//FIRST IDEA: Using insert()
CModbusServer MBtemp* = new CModbusServer(this, IP2.c_str(), PORT2, INDEX)
std::vector<CModbusServer*>::iterator iterator = this->m_pServerCollection.begin(); //I get the vector initial position
m_pServerCollection.insert(iterator + (INDEX), MBTemp); // I put the new object in the right index (I don't trust the order in the config file)
//SECOND IDEA: Using push_back()
m_pServerCollection.push_back( new CModbusServer(this, IP2.c_str(), PORT2, INDEX)); //I attach each new object to the end of vector (i trust the order in the config file)
}
basically I want to create an object of CModbusServer and insert its pointer in a vector, so that I have n different CModbusServer objects in each vector position.
And this is where I get lost, i tried two ways of insertion (as shown in the code ) without success.
CModbusServer has, among others, a const char* ipAddress field. If I try to access to that field (i.e. to use it in a .Format(_T("%S)) function) I get random data. Trying to see why I noticed that in the vector I don't have n different objects,but n copies of the last object created with new CModbusServer(this, IP2.c_str(), PORT2, INDEX). Probably this happens due to the fact that I have a vector of pointers, but those should be pointers to different objects...
I'm using Visual Studio 2015 with MFC in order to realize a dialog-based application. I have an AppEngine class that calls method from other classes and has a vector of CModbusServer elements.
CModbusServer.h is as follows:
class CModbusServer
{
public:
CModbusServer(void *parentEngine, const char* , unsigned short , int );
~CModbusServer();
const char* ipAddress;
unsigned short port;
int indNode;
modbus_t *MBserver;
bool isConnected;
}
So, my question are:
1) Why can't I access to the ipAddress field (instead of reading "192.0.2.1" I read random characters) while I theoretically should be able to read it usingtheApp.CModbusServerVector[properIndex]->ipAddress?
2) I'm making a mistake in populating the vector, but I can't see where and, most importantly, why it's wrong.
Thank you for your assistance, and excuse my english and any omission.
EDIT:
The constructor code of CModbusServer is this:
CModbusServer::CModbusServer(void *pE, const char* ip, unsigned short nport, int ind)
: parentEngine(pE), //used in order to keep track of the parent dialog
ipAddress(ip),
port(nport),
indNode(ind)
{
this->isConnected = false;
this->m_socket = INVALID_SOCKET;
memset(&m_socketstructhint, 0, sizeof m_socketstructhint);
m_socketstructhint.ai_family = AF_UNSPEC;
m_socketstructhint.ai_socktype = SOCK_STREAM;
m_socketstructhint.ai_protocol = IPPROTO_TCP;
MBserver = modbus_new_tcp(ipAddress, (int)nport);
}
Please tell me if I omitted any other useful information.
Initially I used CString for managing the strings, but then I incurred in more and more issues and finally got a compiling and patially working code with const char*. I managed to enstablish a connection and to read the desired modbus registers, but then I got stuck on the isAddress printing problem.
modbus_new_tc(ip,port) is a method found in libmodbus library, a freeware library written for C that I had to use.
EDIT 2: related to angew answer:
So, If I'm right, what's happening is that I create a temporary set of pointers, that are used by the constructor (I've now added the relevant code). But shouldn't be the constructed object unrelated with what I've passed as argument? Aren't those argument copied? Sorry if the question is stupid, but I'm still learning.
the indices are sequential, though in the config file thay could as well be 0-1-2-3 (1 per line) or 0-3-1-2, that's what I meant by "don't trust them".
Since the push_back method has the same issues, probably the problem is back in the constructor. What puzzles me is that by doing a step by step execution I can see that with each iteration of the while loop I get new and correct data, but instad of being put in the i-th position, is put in the first i positions (i.e.: origin data: a b c, 1st run vector = a; 2nd run vector = b b,3rd run vector = c c c)
I didn't know std::unique_ptr<>, i'll look it up.
I've tried to use std:string or even CString, but the problem lies beneath libmodbus libraries.

Calling c_str on a std::string returns a "live" pointer to the internal data stored in that std::string instance. The pointer returned points to a buffer which is only valid as long as the std::string on which it was called remains alive and unmodified.
The constructor of CExtracalModbusServer just stores the pointer passed in. That pointer becomes dangling as soon as IP2 is re-assigned in the next iteration of the input loop. (The address pointed to is still the same, but the buffer which was previously located at that address has either been overwritten, or freed, or something else. In other words, the pointer is just dangling).
As for inserting into the vector: the first way (with insert) could only work if the indices in the files are sequential and starting at 0. You need a valid iterator into the vector to insert into it, where valid here means either pointing to one of the elements already in the vector, or the past-the-end iterator (the one returned by end()). If INDEX is equal to or larger than the size of the vector, then m_pServerCollection.insert(iterator + (INDEX), MBTemp); will attempt to insert outside the vector (essentially a buffer overfow). Ubnefined behaviour ensues.
The push_back way of inserting data should work, and if you see it misbehaving, it's either an artifact of the earlier bug (the one with dangling pointers), or there is a separate problem in code which you haven't shown.
Unrelated to the question at hand, but the code contains pretty bad practice in the form of manually managed dynamic memory. Instead of storing a CModbusServer* in the vector and managing the memory manually with delete in all the right places, you should be using std::unique_ptr<CModbusServer> which will take care of proper deallocation for you, even in the presence of exceptions.
If CModbusServer is under your control, you should likewise change it to store a std::string instead of a const char*. Never use C-style strings in C++ unless you have to interact with C-style APIs, and even in such case, limit their use only to the interaction itself. It's the same principle all over again: don't manage memory manually.

Related

How can I allocate memory for a data structure that contains a vector?

If I have a struct instanceData:
struct InstanceData
{
unsigned usedInstances;
unsigned allocatedInstances;
void* buffer;
Entity* entity;
std::vector<float> *vertices;
};
And I allocate enough memory for an Entity and std::vector:
newData.buffer = size * (sizeof(Entity) + sizeof(std::vector<float>)); // Pseudo code
newData.entity = (Entity *)(newData.buffer);
newData.vertices = (std::vector<float> *)(newData.entity + size);
And then attempt to copy a vector of any size to it:
SetVertices(unsigned i, std::vector<float> vertices)
{
instanceData.vertices[i] = vertices;
}
I get an Access Violation Reading location error.
I've chopped up my code to make it concise, but it's based on Bitsquid's ECS. so just assume it works if I'm not dealing with vectors (it does). With this in mind, I'm assuming it's having issues because it doesn't know what size the vector is going to scale to. However, I thought the vectors might increase along another dimension, like this?:
Am I wrong? Either way, how can I allocate memory for a vector in a buffer like this?
And yes, I know vectors manage their own memory. That's besides the point. I'm trying to do something different.
It looks like you want InstanceData.buffer to have the actual memory space which is allocated/deallocated/accessed by other things. The entity and vertices pointers then point into this space. But by trying to use std::vector, you are mixing up two completely incompatible approaches.
1) You can do this with the language and the standard library, which means no raw pointers, no "new", no "sizeof".
struct Point {float x; float y;} // usually this is int, not float
struct InstanceData {
Entity entity;
std::vector<Point> vertices;
}
This is the way I would recommend. If you need to output to a specific binary format for serialization, just handle that in the save method.
2) You can manage the memory internal to the class, using oldschool C, which means using N*sizeof(float) for the vertices. Since this will be extremely error prone for a new programmer (and still rough for vets), you must make all of this private to class InstanceData, and do not allow any code outside InstanceData to manage them. Use unit tests. Provide public getter functions. I've done stuff like this for data structures that go across the network, or when reading/writing files with a specified format (Tiff, pgp, z39.50). But just to store in memory using difficult data structures -- no way.
Some other questions you asked:
How do I allocate memory for std::vector?
You don't. The vector allocates its own memory, and manages it. You can tell it to resize() or reserve() space, or push_back, but it will handle it. Look at http://en.cppreference.com/w/cpp/container/vector
How do I allocate memory for a vector [sic] in a buffer like this?
You seem to be thinking of an array. You're way off with your pseudo code so far, so you really need to work your way up through a tutorial. You have to allocate with "new". I could post some starter code for this, if you really need, which I would edit into the answer here.
Also, you said something about vector increasing along another dimension. Vectors are one dimensional. You can make a vector of vectors, but let's not get into that.
edit addendum:
The basic idea with a megabuffer is that you allocate all the required space in the buffer, then you initialize the values, then you use it through the getters.
The data layout is "Header, Entity1, Entity2, ..., EntityN"
// I did not check this code in a compiler, sorry, need to get to work soon
MegaBuffer::MegaBuffer() {AllocateBuffer(0);}
MegaBuffer::~MegaBuffer() {ReleaseBuffer();}
MegaBuffer::AllocateBuffer(size_t size /*, whatever is needed for the header*/){
if (nullptr!=buffer)
ReleaseBuffer();
size_t total_bytes = sizeof(Header) + count * sizeof(Entity)
buffer = new unsigned char [total_bytes];
header = buffer;
// need to set up the header
header->count = 0;
header->allocated = size;
// set up internal pointer
entity = buffer + sizeof(Header);
}
MegaBuffer::ReleaseBuffer(){
delete [] buffer;
}
Entity* MegaBuffer::operator[](int n) {return entity[n];}
The header is always a fixed size, and appears exactly once, and tells you how many entities you have. In your case there's no header because you are using member variables "usedInstances" and "allocatednstances" instead. So you do sort of have a header but it is not part of the allocated buffer. But you don't want to allocate 0 bytes, so just set usedInstances=0; allocatedInstances=0; buffer=nullptr;
I did not code for changing the size of the buffer, because the bitsquid ECS example covers that, but he doesn't show the first time initialization. Make sure you initialize n and allocated, and assign meaningful values for each entity before you use them.
You are not doing the bitsquid ECS the same as the link you posted. In that, he has several different objects of fixed size in parallel arrays. There is an entity, its mass, its position, etc. So entity[4] is an entity which has mass equal to "mass[4]" and its acceleration is "acceleration[4]". This uses pointer arithmetic to access array elements. (built in array, NOT std::Array, NOT std::vector)
The data layout is "Entity1, Entity2, ..., EntityN, mass1, mass2, ..., massN, position1, position2, ..., positionN, velocity1 ... " you get the idea.
If you read the article, you'll notice he says basically the same thing everyone else said about the standard library. You can use an std container to store each of these arrays, OR you can allocate one megabuffer and use pointers and "built in array" math to get to the exact memory location within that buffer for each item. In the classic faux-pas, he even says "This avoids any hidden overheads that might exist in the Array class and we only have a single allocation to keep track of." But you don't know if this is faster or slower than std::Array, and you're introducing a lot of bugs and extra development time dealing with raw pointers.
I think I see what you are trying to do.
There are numerous issues. First. You are making a buffer of random data, telling C++ that a Vector sized piece of it is a Vector. But, at no time do you actually call the constructor to Vector which will initialize the pointers and constructs inside to viable values.
This has already been answered here: Call a constructor on a already allocated memory
The second issue is the line
instanceData.vertices[i] = vertices;
instanceData.vertices is a pointer to a Vector, so you actually need to write
(*(instanceData.vertices))[i]
The third issue is that the contents of *(instanceData.vertices) are floats, and not Vector, so you should not be able to do the assignment there.

How to access a vector through a pointer?

I want to insert new element into a vector using a pointer I have the following sample code:
struct info {
string Name;
int places; // i will use the binary value to identfy the visited places example 29 is 100101
// this means he visited three places (London,LA,Rome)
vector<int> times; // will represent the visiting time,e.g. 1,2,5 means london 1 time, LA
// twice and Rome five times
};
map<string,vector<info> *> log;
Peaple are coming from different cities, I will check if the city exists, just add the new person to the vector, else creat a new map object:
vector<info> tp;
info tmp;
if(log.size()==0|| log.count(city)==0) //empty or not exist
{
tp.push_back(tmp);
vector<info>* ss = new vector<info>;
ss=&(tp);
// create a new object
log.insert(map<string,vector<info> * >::value_type(city,ss)); // new object
}
else // city exist, just add the information to the vector
{
map<string,vector<info> *>::iterator t;
t=log.find(city);
*(t->second).push_back(tmp); //the problem in this line
}
How can I insert the new tmp into the vector?
The information to be read as follows:
Paris,Juli,5,3,6
Paris,John,24,2
Canberra,John,4,3
London,Mary,29,4,1,2
There are a lot of mistakes here, and they all stem from misusing pointers. The line that is mentioned as the cause of the problem is a minor syntactic issue. There are bigger issues at hand.
All of them can be easily solved by not misusing pointers. There is no reason to use pointers here, so the ultimate fix is to make the map have this type map<string,vector<info>> log;.
Then the code becomes something like this:
info tmp;
log[city].push_back(tmp);
// the [] operator creates a new empty vector if it doesn't exist yet
// there's no point in doing the checks by hand
Now that we have a simple solution, I'll mention the elephant in the room code.
vector<info>* ss = new vector<info>;
ss=&(tp);
// ...
log.insert(map<string,vector<info> * >::value_type(city,ss));
This sequence of operations will create a vector with dynamic storage duration, and immediately discard the sole pointer to it. That causes the vector that was just created to be lost, and the memory it uses is leaked; it cannot be recovered anymore.
To make matters worse, it sets ss to point to a local variable, and then saves that pointer to the local variable in the map. Because the local variable has automatic storage duration, it is gone once the function returns. That makes the pointer that was just stored in the map invalid, because it no longer has a vector to point to. After that, all kinds of havoc would be wreaked.
Looks like you need to do like this
(t->second)->push_back(tmp);

Initializing and maintaining structs of structs

I’m writing C++ code to deal with a bunch of histograms that are populated from laboratory measurements. I’m running into problems when I try to organize things better, and I think my problems come from mishandling pointers and/or structs.
My original design looked something like this:
// the following are member variables
Histogram *MassHistograms[3];
Histogram *MomentumHistograms[3];
Histogram *PositionHistograms[3];
where element 0 of each array corresponded to one laboratory measurement, element 1 of each corresponded to another, etc. I could access the individual histograms via MassHistograms[0] or similar, and that worked okay. However, the organization didn't seem right to me—if I were to perform a new measurement, I’d have to add an element to each of the histogram arrays. Instead, I came up with
struct Measurement {
Histogram *MassHistogram;
Histogram *MomentumHistogram;
Histogram *PositionHistogram;
};
As an added layer of complexity, I further wanted to bundle these measurements according to the processing that has been done on their data, so I made
struct MeasurementSet {
Measurement SignalMeasurement;
Measurement BackgroundMeasurement;
};
I think this arrangement is much more logical and extensible—but it doesn’t work ;-) If I have code like
MeasurementSet ms;
Measurement m = ms.SignalMeasurement;
Histogram *h = m.MassHistogram;
and then try to do stuff with h, I get a segmentation fault. Since the analogous code was working fine before, I assume that I’m not properly handling the structs in my code. Specifically, do structs need to be initialized explicitly in any way? (The Histograms are provided by someone else’s library, and just declaring Histogram *SomeHistograms[4] sufficed to initialize them before.)
I appreciate the feedback. I’m decently familar with Python and Clojure, but my limited knowledge of C++ doesn’t extend to [what seems like] the arcana of the care and feeding of structs :-)
What I ended up doing
I turned Measurement into a full-blown class:
class Measurement {
Measurement() {
MassHistogram = new Histogram();
MomentumHistogram = new Histogram();
PositionHistogram = new Histogram();
};
~Measurement() {
delete MassHistogram;
delete MomentumHistogram;
delete PositionHistogram;
};
Histogram *MassHistogram;
Histogram *MomentumHistogram;
Histogram *PositionHistogram;
}
(The generic Histogram() constructor I call works fine.) The other problem I was having was solved by always passing Measurements by reference; otherwise, the destructor would be called at the end of any function that received a Measurement and the next attempt to do something with one of the histograms would segfault.
Thank you all for your answers!
Are you sure that Histogram *SomeHistograms[4] initialized the data? How do you populate the Histogram structs?
The problem here is not the structs so much as the pointers that are tripping you up. When you do this: MeasurementSet ms; it declares an 'automatic variable' of type MeasurementSet. What it means is that all the memory for MeasurementSet is 'allocated' and ready to go. MeasurementSet, in turn, has two variables of type Measurement that are also 'allocated' and 'ready to go'. Measurement, in turn, has 3 variables of type Histogram * that are also 'allocated' and 'ready to go'... but wait! The type 'Histogram *' is a 'pointer'. That means it's an address - a 32 or 64 bit (or whatever bit) value that describes an actual memory location. And that's it. It's up to you to make it point to something - to put something at that location. Before it points to anything, it will have literally random data in it (or 0'd out data, or some special debug data, or something like that) - the point is that if you try to do something with it, you'll get a segmentation fault, because you will likely be attempting to read a part of data your program isn't supposed to be reading.
In c++, a struct is almost exactly the same thing as a class (which has a similar concept in python), and you typically allocate one like so:
m.MassHistogram = new Histogram();
...after that, the histogram is ready-to-go. However, YMMV: can you allocate one yourself? Or can you only get one from some library, maybe from a device reading, etc? Furthermore, although you can do what I wrote, it's not necessarily 'pretty'. A c++-ic solution would be to put the allocation in a constructor (like init in python) and delete in a destructor.
When your struct contains a pointer, you have to initialize that variable yourself.
Example
struct foo
{
int *value;
};
foo bar;
// bar.value so far is not initialized and points to a random piece of data
bar.value = new int(0);
// bar.value now points to a int with the value 0
// remember, that you have to delete everything that you new'd, once your done with it:
delete bar.value;
First, always remember that structs and classes are almost exactly the same things. The only difference is that struct members are public by default, and a class member is private by default.
But all the rest is exactly the same.
Second, carefully differentiate between pointers and objects.
If I write
Histogram h;
space for histogram's data will be allocated, and it's constructor will be called. ( A construct is a method with exactly the same name as the class, here Historgram() )
If I write
Histogram* h;
I'm declaring a variable of 32/64 bits that will be used as a pointer to memory. It's initialzed with a random value. Dangerous!
If I write
Histogram* h = new Histogram();
memory will be allocated for one Histogram's data members, and it's constructor will be called. The address in memory will be stored in "h".
If I write
Histogram* copy = h;
I'm again declaring a 32/64 bit variable that points to exactly the same address in memory as h
If I write
Histogram* h = new Historgram;
Histogram* copy = h;
delete h;
the following happens
memory is allocated for a Histogram object
The constructor of Histogram will be called (even if you didn't write it, your compiler will generate one).
h will contain the memory address of this object
the delete operator will call the destructor of Histogram (even if you didn't write it, your compiler will generate one).
the memory allocated for the Histogram object will be deallocated
copy will still contain the memory address where the object used to be allocated. But you're not allowed to use it. It's called a "dangling pointer"
h's contents will be undefined
In short: the "n.MassHistogram" in your code is referring to a random area in memory. Don't use it. Either allocated it first using operator "new", or declare it as "Histogram" (object instead of pointer)
Welcome to CPP :D
You are aware that your definition of Measurement does not allocate memory for actual Histograms? In your code, m.MassHistogram is a dangling (uninitialized) pointer, it's not pointing to any measured Histogram, nor to any memory capable of storing a Histogram. As #Nari Rennlos posted just now, you need to point it to an existing (or newly allocated) Histogram.
What does your 3rd party library's interface look like? If it's at all possible, you should have a Measurement containing 3 Histograms (as opposed to 3 pointers to Histograms). That way when you create a Measurement or a MeasurementSet the corresponding Histograms will be created for you, and the same goes for destruction. If you still need a pointer, you can use the & operator:
struct Measurement2 {
Histogram MassHistogram;
Histogram MomentumHistogram;
Histogram PositionHistogram;
};
MeasurementSet2 ms;
Histogram *h = &ms.SignalMeasurement.MassHistogram; //h valid as long as ms lives
Also note that as long as you're not working with pointers (or references), objects will be copied and assigned by value:
MeasurementSet ms; //6 uninitialized pointers to Histograms
Measurement m = ms.SignalMeasurement; //3 more pointers, values taken from first 3 above
Histogram *h = m.MassHistogram; //one more pointer, same uninitialized value
Though if the pointers had been initialized, all 10 of them would be pointing to an actual Histogram at this point.
It gets worse if you have actual members instead of pointers:
MeasurementSet2 ms; //6 Histograms
Measurement2 m = ms.SignalMeasurement; //3 more Histograms, copies of first 3 above
Histogram h = m.MassHistogram; //one more Histogram
h.firstPoint = 42;
m.MassHistogram.firstPoint = 43;
ms.SignalMeasurement.MassHistogram.firstPoint = 44;
...now you have 3 slightly different mass signal histograms, 2 pairs of identical momentum and position signal histograms, and a triplet of background histograms.

c++ vectors and pointers

As i understand if i dont store pointers, everything in c++ gets copied, which can lead to bad performance (ignore the simplicity of my example). So i thought i store my objects as pointers instead of string object inside my vector, thats better for performance right? (assumining i have very long strings and lots of them).
The problem when i try to iterate over my vector of string pointers is i cant extract the actual value from them
string test = "my-name";
vector<string*> names(20);
names.push_back(&test);
vector<string*>::iterator iterator = names.begin();
while (iterator != names.end())
{
std::cout << (*iterator) << ":" << std::endl;
// std::cout << *(*iterator); // fails
iterator++;
}
See the commented line, i have no problem in receiving the string pointer. But when i try to get the string pointers value i get an error (i couldnt find what excatly the error is but the program just fails).
I also tried storing (iterator) in a new string variable and but it didnt help?
You've created the vector and initialized it to contain 20 items. Those items are being default initialized, which in the case of a pointer is a null pointer. The program is having trouble dereferencing those null pointers.
One piece of advice is to not worry about what's most efficient until you have a demonstrated problem. This code would certainly work much better with a vector<string> versus a vector<string*>.
No, no, a thousand times no.
Don't prematurely optimize. If the program is fast, there's no need to worry about performance. In this instance, the pointers clearly reduce performance by consuming memory and time, since each object is only the target of a single pointer!
Not to mention that manual pointer programming tends to introduce errors, especially for novices. Sacrificing correctness and stability for performance is a huge step backwards.
The advantage of C++ is that it simplifies the optimization process by providing encapsulated data structures and algorithms. So when you decide to optimize, you can usually do so by swapping in standard parts.
If you want to learn about optimizing data structures, read up on smart pointers.
This is probably the program you want:
vector<string> names(20, "my-name");
for ( vector<string>::iterator iterator = names.begin();
iterator != names.end();
++ iterator )
{
std::cout << *iterator << '\n';
}
Your code looks like you're storing a pointer to a stack-based variable into a vector. As soon as the function where your string is declared returns, that string becomes garbage and the pointer is invalid. If you're going to store pointers into a vector, you probably need to allocate your strings dynamically (using new).
Have a look at your initialization:
string test = "my-name";
vector<string*> names(20);
names.push_back(&test);
You first create a std::vector with 20 elements.
Then you use push_back to append a 21st element, which points to a valid string. That's fine, but this element is never reached in the loop: the first iteration crashes already since the first 20 pointers stored in the vector don't point to valid strings.
Dereferencing an invalid pointer causes a crash. If you make sure that you have a valid pointers in your vector, **iterator is just fine to access an element.
Try
if (*iterator)
{
std::cout << *(*iterator) << ":" << std::endl;
}
Mark Ransom explains why some of the pointers are now
string test = "my-name";
vector<string*> names(20);
The size of vector is 20, meaning it can hold 20 string pointers.
names.push_back(&test);
With the push_back operation, you are leaving out the first 20 elements and adding a new element to the vector which holds the address of test. First 20 elements of vector are uninitialized and might be pointing to garbage. And the while loop runs till the end of vector whose size is 21 and dereferencing uninitialized pointers is what causing the problem. Since the size of vector can be dynamically increased with a push_back operation, there is no need to explicitly mention the size.
vector<string*> names; // Not explicitly mentioning the size and the rest of
// the program should work as expected.

In C++, how do I push an object to a vector while maintaining a pointer to the object?

In my code, I have a vector of Student objects.
vector<Student> m_students;
I want to:
Check to see if the vector contains any Student of a certain name.
If no such Student exists, add a new one.
Add data to the Student of that name.
Consider the following code:
// Check to see if the Student already exists.
Student* targetStudent = NULL;
for each (Student student in m_students)
{
if (student.Name() == strName)
{
targetStudent = &student;
break;
}
}
// If the Student didn't exist, add it.
if (targetStudent == NULL)
{
targetStudent = new Student(strName);
m_students.push_back(*targetStudent);
}
// Add the course info to the Student.
targetStudent->Add(strQuarter, strCourse, strCredits, strGrade);
When I make the call to m_students.push_back(*targetStudent); it seems that the vector "m_students" ends up with a copy of the Student object that "targetStudent" points to at that time.
The subsequent attempt to add to targetStudent does not change the object contained in the vector.
How can I, starting with a pointer to an object, add that object to a vector and then access the object that is in the vector?
Get the pointer to the object after it was inserted into the vector, so instead of
targetStudent = new Student(strName);
m_students.push_back(*targetStudent);
use
m_students.push_back(Student(strName));
targetStudent = &m_students.back();
Also note that your example leaks memory, the targetStudent copied into the vector is never deleted.
Furthermore keep in mind that the pointers into the vector become invalid when new elements are added (if the vector increases in phsyical size and elements have to be copied into the new, larger vector all pointers into the previous buffer become invalid).
STL containers copy the objects they contain. There is no way to work around this.
You can, however, have a std::vector<std::shared_ptr<Student> >, which allow you to have a container of smart pointers. For this to work, though, your objects must all be attached to the shared_ptr at the time of construction.
So, something like:
std::vector<std::shared_ptr<Student> > m_students;
std::shared_ptr<Student> targetStudent;
for each (std::shared_ptr<Student> student in m_students)
{
if (student->Name() == strName)
{
targetStudent = student;
break;
}
}
// If the Student didn't exist, add it.
if (!targetStudent)
{
// creates a new Student and attaches it to smart pointer
targetStudent.reset(new Student(strName));
m_students.push_back(targetStudent);
}
std::shared_ptr is defined in the <memory> header in C++11. (In TR1, you can use std::tr1::shared_ptr instead.) If you're using C++98 without TR1, or need to be portable with it, you can use boost::shared_ptr instead; download from Boost.
You've already gotten a reasonably direct answer to your question. Based on what you seem to be trying to accomplish, however, it seems to me that a less direct answer may really be a better one.
At least as I read your description, you have a number of unique students, and a number of courses for each. When a student has completed a course, you want to look for the student. If they're not in the collection, add them. Then add the data for the course they completed.
That being the case, a vector strikes me as a less than ideal solution. You could implement code a couple of different ways, but I'd probably do it like this:
struct course {
std::string Quarter_, Course_, Credits_, Grade_;
using std::string;
course(string const &q, string const &c, string const &cr, string const &g)
: Quarter_(q), Course_(c), Credits_(cr), Grade_(g)
{}
};
std::map<std::string, std::vector<course> > m_students;
Using this, your entire sequence to look up a student, insert a new student if there isn't one by that name, then adding the course work to the (new or existing) student's record would work out as:
m_students[strName].push_back(course(strQuarter, strCourse, strCredits, strGrade));
Getting back to your original question, the standard containers are intended to work with values. You pass a value to them, and they store a copy of that value. One consequence of that is that anything like push_back(new XXX) is essentially always a mistake (pretty much a guaranteed memory leak). If you have an object, just pass it. If you don't, just create a temporary and pass that. In Java (for one example) seeing new XXX all over the place is routine and nearly unavoidable. While you can write C++ that way as well, it's not something you should expect as a rule.