473,325 Members | 2,816 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,325 software developers and data experts.

Traversing members of a class with a pointer

While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
....

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
....
Badness badness;

std::string *it = &badness.m_begin;

for (++it; it != &badness.m_end; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?

/Glen
Dec 9 '06 #1
14 1842
Glen Dayton wrote:
While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begin;

for (++it; it != &badness.m_end; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?
What about the undefined behavior that the code has; does that count?
Best

Kai-Uwe Bux
Dec 9 '06 #2
Glen Dayton wrote:
>
Looking wrong and evil, though, are not rational arguments that would
allow me to convince my technical lead that this code needs to be
changed sooner than later.
If it works, it works. The behavior isn't defined by the C++ standard,
but I haven't run into a compiler where it won't do just what it ooks
like it does.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Dec 10 '06 #3
Pete Becker wrote:
Glen Dayton wrote:
>>
Looking wrong and evil, though, are not rational arguments that would
allow me to convince my technical lead that this code needs to be
changed sooner than later.

If it works, it works. The behavior isn't defined by the C++ standard,
but I haven't run into a compiler where it won't do just what it ooks
like it does.
That it is undefined by the standard is one problem. It means
at a future date the code will stop working. That it works is
another part of the problem, because it removes a motivation for
removing a time bomb from the code. In the real-life code from
which this example was taken, macros obscured the class
beginning and end with their m_begin and m_end guards. The code
maintainer needed to add a bool and an int field to the class,
which would've broken the code if maintainer had not first
carefully analyzed it or just had good luck in realizing what
was happening. The next programmer to touch that code may not
be so lucky or smart.

I'll search the standard for a relevant reference, then I'll
write a unit test and refactor this sucker.

Because the compiler knows all the offsets of the members, is
there any chance a near future version of C++ will offer
iterators to access the typeinfo structures and offsets of each
member of a class?

/Glen

Dec 10 '06 #4
Glen Dayton wrote:
While looking at some old code I ran across a snippet that increments a
pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begin;

for (++it; it != &badness.m_end; ++it) {
std::cout << it->c_str() << std::endl;
}
I think if you want to avoid UB, you're toast, unless you want to use
pointers to members. Maybe something like this? Note: m_members should
probably be some sort of static.

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

typedef std::vector<std::string Badness::*member_vec_t;
member_vec_t m_members;
Badness()
{
m_members.push_back(&Badness::m_begin);
m_members.push_back(&Badness::m_param1);
m_members.push_back(&Badness::m_param2);
m_members.push_back(&Badness::m_end);
}

};
// ...
Badness badness;
for (Badness::member_vec_t it = badness.m_members.begin();
it != badness.m_members.end();
++it)
std::cout << badness.*(*it) << std::endl;
Dec 10 '06 #5
Glen Dayton wrote:
Pete Becker wrote:
>Glen Dayton wrote:
>>>
Looking wrong and evil, though, are not rational arguments that would
allow me to convince my technical lead that this code needs to be
changed sooner than later.

If it works, it works. The behavior isn't defined by the C++ standard,
but I haven't run into a compiler where it won't do just what it ooks
like it does.

That it is undefined by the standard is one problem. It means at a
future date the code will stop working.
No, it means that at a future date the code might stop working. That's
not to say that it's a good way to write code, but hyperventilating is
not a good way to start a design.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Dec 10 '06 #6

Pete Becker wrote:
Glen Dayton wrote:
Pete Becker wrote:
If it works, it works. The behavior isn't defined by the C++ standard,
but I haven't run into a compiler where it won't do just what it ooks
like it does.
That it is undefined by the standard is one problem. It means at a
future date the code will stop working.

No, it means that at a future date the code might stop working. That's
not to say that it's a good way to write code, but hyperventilating is
not a good way to start a design.
I think there is quite a difference between intending to use formally
undefined behaviour in a new design and changing existing code that
works when you discover it relies on formally undefined behaviour.

Personally, for a new design my initial position would be to avoid any
code for which the behaviour is undefined. With an existing code base,
if I was satisfied that the behaviour on my particular compiler and
platform was as intended, my initial position would be if it ain't
broken, don't fix it. Of course, formally the code is broken by
definition. But in practice I would take into account whether I
expected to be changing compilers any time soon, and how much
regression test coverage I had of the code before deciding whether to
change it.

Gavin Deane

Dec 10 '06 #7

Glen Dayton wrote:
While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begin;

for (++it; it != &badness.m_end; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?

/Glen
Loose the pointers.
Can you not do something like this? Note: you must keep the
std::vector< std::string container at the top of the struct in order
to guarentee that it is initialized before the references.

#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>

struct Badness {
private:
std::vector< std::string vs;
public:
std::string& m_begin;
std::string& m_param1;
std::string& m_param2;
std::string& m_end;
typedef std::vector< std::string >::iterator baditerator;
typedef std::vector< std::string >::const_iterator const_baditerator;

Badness() : vs(4),
m_begin(vs[0]), m_param1(vs[1]),
m_param2(vs[2]), m_end(vs[3])
{ }
baditerator begin() { return vs.begin(); }
baditerator end() { return vs.end(); }

// friend
friend std::ostream&
operator<<(std::ostream& os, const Badness& r_bad)
{
std::copy( r_bad.vs.begin(),
r_bad.vs.end(),
std::ostream_iterator< std::string >(os, "\n") );
return os;
}
};

int main()
{
Badness badness;
badness.m_begin = "string m_begin";
badness.m_param1 = "string m_param1";
badness.m_param2 = "string m_param2";
badness.m_end = "string m_end";

for (Badness::baditerator iter = badness.begin();
iter != badness.end();
++iter)
{
std::cout << *iter << std::endl;
}

std::cout << "\n using op<<...\n";
std::cout << badness;
}

Dec 10 '06 #8
Gavin Deane wrote:
Of course, formally the code is broken by
definition.
For a somewhat artificial definition of "broken." If it does what it's
supposed to do most people wouldn't consider it broken.
But in practice I would take into account whether I
expected to be changing compilers any time soon, and how much
regression test coverage I had of the code before deciding whether to
change it.
Portability is not an absolute. It's about how hard it is to move to a
different platform. Code that's well formed according to the language
definition isn't automatically easier to port than code that isn't.
Compilers vary.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Dec 10 '06 #9

Pete Becker wrote:
Gavin Deane wrote:
Of course, formally the code is broken by
definition.

For a somewhat artificial definition of "broken." If it does what it's
supposed to do most people wouldn't consider it broken.
It does what it's supposed to do, but how do I know that? Where is the
specification that defines what behavior that code should have?

If I am relying on undefined behaviour, the simple fact that there is
nowhere I can go to read documentation that explains what the behaviour
should be makes me uncomfortable as an engineer. Operating outside the
world of specification is moving away from sound engineering principles
and towards voodoo. I would suggest that a software engineer should
only do that with good reason.

Gavin Deane

Dec 10 '06 #10
Gavin Deane wrote:
Pete Becker wrote:
>Gavin Deane wrote:
>>Of course, formally the code is broken by
definition.
For a somewhat artificial definition of "broken." If it does what it's
supposed to do most people wouldn't consider it broken.

It does what it's supposed to do, but how do I know that?
You test it.
Where is the
specification that defines what behavior that code should have?
If I am relying on undefined behaviour, the simple fact that there is
nowhere I can go to read documentation that explains what the behaviour
should be makes me uncomfortable as an engineer.
That's not necessarily the case. The standard does not prohibit compiler
vendors from documenting behavior that is formally undefined. But that
aside...
Operating outside the
world of specification is moving away from sound engineering principles
and towards voodoo.
Only if you don't test that the code does what you need it to do.
I would suggest that a software engineer should
only do that with good reason.
That's a different issue.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Dec 10 '06 #11


On Dec 10, 5:46 am, Pete Becker <p...@versatilecoding.comwrote:
Glen Dayton wrote:
Pete Becker wrote:
Glen Dayton wrote:
>Looking wrong and evil, though, are not rational arguments that would
allow me to convince my technical lead that this code needs to be
changed sooner than later.
If it works, it works. The behavior isn't defined by the C++ standard,
but I haven't run into a compiler where it won't do just what it ooks
like it does.
That it is undefined by the standard is one problem. It means at a
future date the code will stop working.
No, it means that at a future date the code might stop working. That's
not to say that it's a good way to write code, but hyperventilating is
not a good way to start a design.
If it works, it works, but surely that isn't the only criteria for
whether code is acceptable.
You didn't quote from this poster what I think is the most compelling
reason for modifying this code, which is that a future maintainer may
modify the class in what seems to be an innocuous manner, but breaks
the code that assumes contiguous layout. I don't think being concerned
about that merits the label 'hyperventilating'.

Dec 11 '06 #12
Salt_Peter wrote:
Glen Dayton wrote:
>While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begin;

for (++it; it != &badness.m_end; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?

/Glen

Loose the pointers.
Can you not do something like this? Note: you must keep the
std::vector< std::string container at the top of the struct in order
to guarentee that it is initialized before the references.

#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>

struct Badness {
private:
std::vector< std::string vs;
public:
std::string& m_begin;
std::string& m_param1;
std::string& m_param2;
std::string& m_end;
typedef std::vector< std::string >::iterator baditerator;
typedef std::vector< std::string >::const_iterator const_baditerator;

Badness() : vs(4),
m_begin(vs[0]), m_param1(vs[1]),
m_param2(vs[2]), m_end(vs[3])
{ }
baditerator begin() { return vs.begin(); }
baditerator end() { return vs.end(); }

// friend
friend std::ostream&
operator<<(std::ostream& os, const Badness& r_bad)
{
std::copy( r_bad.vs.begin(),
r_bad.vs.end(),
std::ostream_iterator< std::string >(os, "\n") );
return os;
}
};

int main()
{
Badness badness;
badness.m_begin = "string m_begin";
badness.m_param1 = "string m_param1";
badness.m_param2 = "string m_param2";
badness.m_end = "string m_end";

for (Badness::baditerator iter = badness.begin();
iter != badness.end();
++iter)
{
std::cout << *iter << std::endl;
}

std::cout << "\n using op<<...\n";
std::cout << badness;
}
Thanks.

Instead of using a std::vector<which requires run-time
initialization, how about just using a static array of member
pointers? That way the initialization is done at load time and
the normal type safety applies:

struct Sample
{
std::string m_param1;
std::string m_param2;

static std::string Sample::* params[2];

// Provide a convenient means of stepping through
class iterator
{
private:
iterator() : m_dex(0), m_base(NULL) {}
unsigned int m_dex;
Sample *m_base;

public:
iterator(Sample *base) : m_dex(0), m_base(base) {}
iterator(Sample *base, unsigned int dex) : m_dex(dex),
m_base(base) {}

std::string& operator*()
{ return m_base->*Sample::params[m_dex]; }

std::string* operator->()
{ return &(m_base->*Sample::params[m_dex]); }
iterator& operator++()
{ ++m_dex; return *this; }

bool operator==(const iterator& other)
{ return m_base == other.m_base && m_dex == other.m_dex; }

bool operator!=(const iterator& other)
{ return m_base != other.m_base || m_dex != other.m_dex; }
};

Sample();
iterator begin() { return iterator(this); }
iterator end()
{ return iterator(this,
sizeof(Sample::params)/sizeof(Sample::params[0])); }

};
....

for (Sample::iterator iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Glen Dayton
Dec 13 '06 #13

Glen Dayton wrote:
Salt_Peter wrote:
Glen Dayton wrote:
While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begin;

for (++it; it != &badness.m_end; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?

/Glen
Loose the pointers.
Can you not do something like this? Note: you must keep the
std::vector< std::string container at the top of the struct in order
to guarentee that it is initialized before the references.

#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>

struct Badness {
private:
std::vector< std::string vs;
public:
std::string& m_begin;
std::string& m_param1;
std::string& m_param2;
std::string& m_end;
typedef std::vector< std::string >::iterator baditerator;
typedef std::vector< std::string >::const_iterator const_baditerator;

Badness() : vs(4),
m_begin(vs[0]), m_param1(vs[1]),
m_param2(vs[2]), m_end(vs[3])
{ }
baditerator begin() { return vs.begin(); }
baditerator end() { return vs.end(); }

// friend
friend std::ostream&
operator<<(std::ostream& os, const Badness& r_bad)
{
std::copy( r_bad.vs.begin(),
r_bad.vs.end(),
std::ostream_iterator< std::string >(os, "\n") );
return os;
}
};

int main()
{
Badness badness;
badness.m_begin = "string m_begin";
badness.m_param1 = "string m_param1";
badness.m_param2 = "string m_param2";
badness.m_end = "string m_end";

for (Badness::baditerator iter = badness.begin();
iter != badness.end();
++iter)
{
std::cout << *iter << std::endl;
}

std::cout << "\n using op<<...\n";
std::cout << badness;
}
Thanks.

Instead of using a std::vector<which requires run-time
initialization, how about just using a static array of member
pointers? That way the initialization is done at load time and
the normal type safety applies:
Run-time initialization has huge advantages and a design involving
static arrays will fail.
A static is essentially a singleton but not accross compilation units
<ewww>.
C++ has a solution, however, statics should be replaced with
namespaces.
>
struct Sample
{
std::string m_param1;
std::string m_param2;

static std::string Sample::* params[2];
There is no this in a static variable or container, therefore:

static std::string* params[2];

Static arrays can't be initialized in ctor initialization lists and you
can't control when these are allocated. Note also, that statics are
confined to their compilation unit. So a static in that source isn't
the same static variable as in the other compilation unit. Arrays are
evil, static arrays are downright dangerous accross compilation units.
Debugging these is a nightmare.
>
// Provide a convenient means of stepping through
class iterator
{
private:
iterator() : m_dex(0), m_base(NULL) {}
unsigned int m_dex;
Sample *m_base;

public:
iterator(Sample *base) : m_dex(0), m_base(base) {}
iterator(Sample *base, unsigned int dex) : m_dex(dex),
m_base(base) {}

std::string& operator*()
{ return m_base->*Sample::params[m_dex]; }
{ return *m_base->params[m_dex]; }
>
std::string* operator->()
{ return &(m_base->*Sample::params[m_dex]); }
{ return m_base->params[m_dex]; }
>

iterator& operator++()
{ ++m_dex; return *this; }

bool operator==(const iterator& other)
{ return m_base == other.m_base && m_dex == other.m_dex; }

bool operator!=(const iterator& other)
{ return m_base != other.m_base || m_dex != other.m_dex; }
};

Sample();
Sample(std::string s1, std::string s2)
: m_param1(s1), m_param2(s2)
{
params[0] = &m_param1; // not allowed in init lists
params[1] = &m_param2;
};

iterator begin() { return iterator(this); }
iterator end()
{ return iterator(this,
sizeof(Sample::params)/sizeof(Sample::params[0])); }

};
// required:
std::string* Sample::params[2];
...

for (Sample::iterator iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Glen Dayton
// Now watch how nasty this gets, not to mention insidious:

int main()
{
Sample sample("string 1", "string 2"); // sample
for (Sample::iterator iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Sample another("string 3", "string 4"); // another
for (Sample::iterator iter = another.begin();
iter != another.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

// print the original sample strings - sample
for (Sample::iterator iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}
}

/*
string 1
string 2
string 3
string 4
string 3 // the array no longer points to the original strings
string 4
*/

Dec 13 '06 #14
Salt_Peter wrote:
Glen Dayton wrote:
>Salt_Peter wrote:
>>Glen Dayton wrote:
While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begin;

for (++it; it != &badness.m_end; ++it) {
std::cout << it->c_str() << std::endl;
}
>>Loose the pointers.
Can you not do something like this? Note: you must keep the
std::vector< std::string container at the top of the struct in order
to guarentee that it is initialized before the references.

#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>

struct Badness {
private:
std::vector< std::string vs;
public:
std::string& m_begin;
std::string& m_param1;
std::string& m_param2;
std::string& m_end;
typedef std::vector< std::string >::iterator baditerator;
typedef std::vector< std::string >::const_iterator const_baditerator;

Badness() : vs(4),
m_begin(vs[0]), m_param1(vs[1]),
m_param2(vs[2]), m_end(vs[3])
{ }
baditerator begin() { return vs.begin(); }
baditerator end() { return vs.end(); }

// friend
friend std::ostream&
operator<<(std::ostream& os, const Badness& r_bad)
{
std::copy( r_bad.vs.begin(),
r_bad.vs.end(),
std::ostream_iterator< std::string >(os, "\n") );
return os;
}
};
>Instead of using a std::vector<which requires run-time
initialization, how about just using a static array of member
pointers? That way the initialization is done at load time and
the normal type safety applies:

Run-time initialization has huge advantages and a design involving
static arrays will fail.
A static is essentially a singleton but not accross compilation units
<ewww>.
C++ has a solution, however, statics should be replaced with
namespaces.
>struct Sample
{
std::string m_param1;
std::string m_param2;

static std::string Sample::* params[2];

There is no this in a static variable or container, therefore:

static std::string* params[2];

Static arrays can't be initialized in ctor initialization lists and you
can't control when these are allocated. Note also, that statics are
confined to their compilation unit. So a static in that source isn't
the same static variable as in the other compilation unit. Arrays are
evil, static arrays are downright dangerous accross compilation units.
Debugging these is a nightmare.
> // Provide a convenient means of stepping through
class iterator
{
private:
iterator() : m_dex(0), m_base(NULL) {}
unsigned int m_dex;
Sample *m_base;

public:
iterator(Sample *base) : m_dex(0), m_base(base) {}
iterator(Sample *base, unsigned int dex) : m_dex(dex),
m_base(base) {}

std::string& operator*()
{ return m_base->*Sample::params[m_dex]; }

{ return *m_base->params[m_dex]; }
> std::string* operator->()
{ return &(m_base->*Sample::params[m_dex]); }

{ return m_base->params[m_dex]; }
>>
iterator& operator++()
{ ++m_dex; return *this; }

bool operator==(const iterator& other)
{ return m_base == other.m_base && m_dex == other.m_dex; }

bool operator!=(const iterator& other)
{ return m_base != other.m_base || m_dex != other.m_dex; }
};

Sample();

Sample(std::string s1, std::string s2)
: m_param1(s1), m_param2(s2)
{
params[0] = &m_param1; // not allowed in init lists
params[1] = &m_param2;
};
Whoa! params is an array of pointers to members! You've
initialized it incorrectly. See my oops below.

>
> iterator begin() { return iterator(this); }
iterator end()
{ return iterator(this,
sizeof(Sample::params)/sizeof(Sample::params[0])); }

};

// required:
std::string* Sample::params[2];

Sorry, left off the initialization:
std::string Sample::* Sample::params[2] =
{&Sample::m_param1, &Sample::m_param2};

>
>...

for (Sample::iterator iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Glen Dayton

// Now watch how nasty this gets, not to mention insidious:

int main()
{
Sample sample("string 1", "string 2"); // sample
for (Sample::iterator iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Sample another("string 3", "string 4"); // another
for (Sample::iterator iter = another.begin();
iter != another.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

// print the original sample strings - sample
for (Sample::iterator iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}
}

/*
string 1
string 2
string 3
string 4
string 3 // the array no longer points to the original strings
string 4
*/
Thank you for your time analyzing my little program. In the
future I will include the complete source to help prevent
misunderstandings like this.

I'm surprised your code compiled. The iterator uses the pointer
to members which must be used with a base pointer. I
specifically used pointers to members so I could have diffent
instances of the same class. Notice the ->* syntax in the iterator.

/Glen Dayton

Dec 14 '06 #15

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Christian Nolte | last post by:
I want to implement dynamic calling of interface methods using pointers to members and have some trouble to get it right. An example of what I am talking about looks like that: ...
4
by: Oystein Haare | last post by:
Is it best to declare class member objects as pointers or not pointers? class A { public: A(int i); //.... };
4
by: Steven T. Hatton | last post by:
I mistakenly set this to the comp.std.c++ a few days back. I don't believe it passed the moderator's veto - and I did not expect or desire anything different. But the question remains: ISO/IEC...
5
by: pmatos | last post by:
Hi all, I have a style question. I've been for long programming in Lisp-like languages and C when I need a low-level language. Now, I'm programming for professional reasons in C++ (which I had...
6
by: lovecreatesbeauty | last post by:
Hello Experts, Why static data members can be declared as the type of class which it belongs to? Inside a class, non-static data members such as pointers and references can be declared as...
4
by: Paolo Capriotti | last post by:
Compiling the following code (gcc 3.3.6), I get an invalid conversion error. Is there a valid reason why this conversion can't be performed? --- class A {}; class B : public A {}; class T...
3
by: Ryan Steckler | last post by:
I found this behavior while trying to implement a singleton class. Below is a somewhat more straight forward example, though admittedly less useful in real life. The basic problem is that when a...
14
by: lovecreatesbea... | last post by:
Could you tell me how many class members the C++ language synthesizes for a class type? Which members in a class aren't derived from parent classes? I have read the book The C++ Programming...
6
by: Bhawna | last post by:
I am into c++ code maintenance for last 3-4 years but recently I am put into design phase of a new project. Being a small comapany I dont have enough guidance from seniors. Currently I am into a...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.