std::list and a composite type ..

M

ma740988

Consider:

# include <iostream>
using std::cout;
using std::cin;
using std::endl;

# include <list>
using std::list;

struct my_struct
{
std::string mName;
int mValue;
};

typedef std::list<my_struct> my_list;

int main()
{

my_list cc;
my_struct s;
s.mName = "Foo";
s.mValue = 42;
cc.push_back(s); // add contents

s.mName = "Bar";
s.mValue = 67;
cc.push_back(s); // add contents

my_list::iterator it(cc.begin());
for (; it != cc.end(); ++it)
{
cout << "Name = " << (*it).mName.c_str()
<< " Value = " << (*it).mValue << endl;
}

it = cc.begin();
for (; it != cc.end(); ++it)
{
if (it->mName == "Foo")
{
//cout << "found " << '\n';
//cc.erase(it); // exception. How do I erase "Foo"??
}
}

//my_struct* ptr = *it;

my_struct* ptr = new my_struct;
if (!ptr)
std::cerr << "memory allocation failure " << '\n';
it = cc.begin();
for (; it != cc.end(); ++it)
//ptr = it // assign contents of iterator to memory?

delete ptr;
}

The standard list container my_list holds a composite type my_struct.
At issue is the need to 'erase' Foo and it's corresponding value of
42. How do I achieve this?

ptr is a block of memory that's been allocated the sizeof my_struct.
Is it possible to 'assign' the contents of the iterator to that
memory? Silly though this may seem, a classmate/lab partner was
opting to do as such and I informed him. 1. Doesn't make sense and
2. I don't think that can be done.
 
J

John Harrison

Consider:

# include <iostream>
using std::cout;
using std::cin;
using std::endl;

# include <list>
using std::list;

struct my_struct
{
std::string mName;
int mValue;
};

typedef std::list<my_struct> my_list;

int main()
{

my_list cc;
my_struct s;
s.mName = "Foo";
s.mValue = 42;
cc.push_back(s); // add contents

s.mName = "Bar";
s.mValue = 67;
cc.push_back(s); // add contents

my_list::iterator it(cc.begin());
for (; it != cc.end(); ++it)
{
cout << "Name = " << (*it).mName.c_str()
<< " Value = " << (*it).mValue << endl;
}

it = cc.begin();
for (; it != cc.end(); ++it)
{
if (it->mName == "Foo")
{
//cout << "found " << '\n';
//cc.erase(it); // exception. How do I erase "Foo"??
}
}

//my_struct* ptr = *it;

my_struct* ptr = new my_struct;
if (!ptr)
std::cerr << "memory allocation failure " << '\n';
it = cc.begin();
for (; it != cc.end(); ++it)
//ptr = it // assign contents of iterator to memory?

delete ptr;
}

The standard list container my_list holds a composite type my_struct.
At issue is the need to 'erase' Foo and it's corresponding value of
42. How do I achieve this?

Same way as you would erase anything from a list.
ptr is a block of memory that's been allocated the sizeof my_struct.
Is it possible to 'assign' the contents of the iterator to that
memory? Silly though this may seem, a classmate/lab partner was
opting to do as such and I informed him. 1. Doesn't make sense and
2. I don't think that can be done.

Yes it's possible (why you would want to do it is another matter) but

*ptr = *it;

The mistake you made in deleting from a list is not what you thought. Look
at the code again.

it = cc.begin();
for (; it != cc.end(); ++it)
{
if (it->mName == "Foo")
{
cout << "found " << '\n';
cc.erase(it); // exception. How do I erase "Foo"??
}
}

The erase is fine, that works, but what is the very next thing that
happens?

Answer

++it

When you erase something from a list (or anything else) using an iterator
you invalidate that iterator. You cannot use that iterator afterwards for
anything else.

Here is how you should have written the code

it = cc.begin();
while (it != cc.end())
{
if (it->mName == "Foo")
{
cout << "found " << '\n';
it = cc.erase(it);
}
else
{
++it;
}
}

list::erase returns an iterator to the next position.

john
 
M

ma740988

[...]
Yes it's possible (why you would want to do it is another matter) but

*ptr = *it;

There's no correlation between the sizeof the memory allocated by new
and the length/memory block of the iterator? In other words, assume
we added(push_back's) some very large number of my_struct's to the
list.
Now I'd envision that care should be taken when assigning the contents
of the iterator to the memory allocated by new, since 'the large
number' of contents may not fit. Yes/No?

The mistake you made in deleting from a list is not what you thought. Look
at the code again.

it = cc.begin();
for (; it != cc.end(); ++it)
{
if (it->mName == "Foo")
{
cout << "found " << '\n';
cc.erase(it); // exception. How do I erase "Foo"??
}
}

The erase is fine, that works, but what is the very next thing that
happens?

Answer

++it

When you erase something from a list (or anything else) using an iterator
you invalidate that iterator. You cannot use that iterator afterwards for
anything else.

I overlooked this.

Thanks
Mark
 
J

John Harrison

[...]
Yes it's possible (why you would want to do it is another matter) but

*ptr = *it;

There's no correlation between the sizeof the memory allocated by new
and the length/memory block of the iterator? In other words, assume
we added(push_back's) some very large number of my_struct's to the
list.
Now I'd envision that care should be taken when assigning the contents
of the iterator to the memory allocated by new, since 'the large
number' of contents may not fit. Yes/No?

No. An iterator refers to a *single* item that has been added to the list,
and *it returns a reference to that single item. The number of items in
the list is irrelevant.

This is a new misunderstanding of iterators to me, so I not sure quite
what you are thinking, but clearly you've got some serious misconception
about what iterators are.

Actually my statement is inaccurate. What happens is that when an item is
erased from an list, by any means, all iterators that refer to the erased
item become invalid. Iterators that refer to unerased items remain valid.

john
 
J

John Harrison

[...]
ptr is a block of memory that's been allocated the sizeof my_struct.
Is it possible to 'assign' the contents of the iterator to that
memory? Silly though this may seem, a classmate/lab partner was
opting to do as such and I informed him. 1. Doesn't make sense and
2. I don't think that can be done.

Perhaps you meant the contents of the list, not the contents of the
iterator. Then your objection would make more sense (but still not be
valid).

#include <algorithm>

my_struct* ptr = new my_struct[cc.size()]; // enough room for the entire
list
std::copy(cc.begin(), cc.end(), ptr); // copy entire list to ptr

john
 
M

ma740988

John Harrison said:
Perhaps you meant the contents of the list, not the contents of the
iterator. Then your objection would make more sense (but still not be
valid).
'contents of the list'. Thats correct. I tend to mix up C++
terminology quite often.
#include <algorithm>

my_struct* ptr = new my_struct[cc.size()]; // enough room for the entire
list
std::copy(cc.begin(), cc.end(), ptr); // copy entire list to ptr
Ah!! cc.size() is what I was missing. I knew there had to be a
connection between the size of the memory allocated and the size of
the list to be copied.
The previous version (ie. my_struct* ptr = new my_struct;) could lead
to problems.

One other question.
I recalled reading about the preferred use of std::fill to memset.
One of the prime arguments against memset - as i understood it -
revolved around memset limitation with respect to alignment on byte
boundaries.

For starters std::fill is memset under the hood when viewed from
Visual Studio 7 compiler. Secondly, how would I (couldn't find within
my text and online) std::fill a struct to - say 0.
So now.

struct my_struct
{
std::string mName;
int mValue1;
int mValue2;
};

typedef std::list<my_struct> my_list;

int main()
{
my_struct s;
/// try passing the address of the struct
//std::fill(&s, sizeof s, 0);
my_list cc;
/// try iterators
//std::fill(cc.begin(), cc.end(), 0);
memset (&s, 0, sizeof s);
cout << s.mName.c_str() << '\n';
cout << s.mValue1 << '\n';
cout << s.mValue2 << '\n';
}
 
J

John Harrison

One other question.
I recalled reading about the preferred use of std::fill to memset.
One of the prime arguments against memset - as i understood it -
revolved around memset limitation with respect to alignment on byte
boundaries.

For starters std::fill is memset under the hood when viewed from
Visual Studio 7 compiler.

That's not true in general, it might be true for particular types.
Secondly, how would I (couldn't find within
my text and online) std::fill a struct to - say 0.
So now.

struct my_struct
{
std::string mName;
int mValue1;
int mValue2;
};

That struct cannot be filled to 0 because it contains a std::string. It's
just not a meaningful thing to try and do. What do you think the value of
mName should be after you've filled it with 0?

Further std::fill is for sequences and arrays, it is not for individual
objects.

typedef std::list<my_struct> my_list;

int main()
{
my_struct s;
/// try passing the address of the struct
//std::fill(&s, sizeof s, 0);
my_list cc;
/// try iterators
//std::fill(cc.begin(), cc.end(), 0);
memset (&s, 0, sizeof s);
cout << s.mName.c_str() << '\n';
cout << s.mValue1 << '\n';
cout << s.mValue2 << '\n';
}

You are just getting undefined behaviour here. You cannot memset a string.

The C++ way of doing what I think you are trying to do is to use a
constructor.

struct my_struct
{
my_struct() : mValue1(0), mValue2(0) {}
std::string mName;
int mValue1;
int mValue2;
};

int main()
{
my_struct s;
cout << s.mName << '\n'; // prints empty line
cout << s.mValue1 << '\n'; // prints 0
cout << s.mValue2 << '\n'; // prints 0
}

The constructor ensures that every my_struct has defined values when it is
created.

john
 
D

David Rubin

[snip]
That struct cannot be filled to 0 because it contains a std::string. It's
just not a meaningful thing to try and do. What do you think the value of
mName should be after you've filled it with 0?

Does this mean that you cannot

my_struct s = {0}; // works?

/david
 
J

John Harrison

[snip]
That struct cannot be filled to 0 because it contains a std::string.
It's
just not a meaningful thing to try and do. What do you think the value
of
mName should be after you've filled it with 0?

Does this mean that you cannot

my_struct s = {0}; // works?

/david

You can do it, and it will compile, but it results in a call to the
std::string constructor with a NULL pointer, which is undefined behaviour
as I recall.

You could do this

my_struct s = { "" };

john
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,187
Latest member
RosaDemko

Latest Threads

Top