Skip to main content
GameDev.net gamedev.net
🔒 Locked

Initializing array member objects in c++

Started by asp_ Jun 10, 2004 at 4:50 AM 35 replies 94.7k views
Original Post
asp_
asp_
Hello, I have somewhat of a problem and no idea how this is done or even if it's possible. But this is what I have:
class Plane3
{
   public:
      inline
         Plane3(const Point3 &p0, 
                const Point3 &p1,
                const Point3 &p2);
      ...

   private:
      Point3 mP[3];
};
  
What I want to do is initialize the mP array member in the constructor. Something like this:
inline 
   Plane3:: Plane3(const Point3 &p0,
                  const Point3 &p1,
                  const Point3 &p2)
      : mP[0](p0), mP[1](p1), mP[2](p2)
   {
   }
  
Of course this isn't valid code so my question is what syntax can you use when initializing array member objects when constructing an object? Having three separate member variables and initializing them isn't acceptable, I need it to be an array. Thanks! *edit* Damn smilies. [edited by - asp_ on June 10, 2004 5:51:47 AM]
paulecoyote
paulecoyote
there are special tags you can use on this forum that format code properly.

Anyway you can''t program like that.

Understand that:

Point3 mP[3];

... is just declaring a pointer to an array, not the array itself.

you have to actually create the array with a new Point3[3] to allocate the memory actually used for the array. and importantly in the destructor using delete [] mP; rather then just delete mp;

I can see you are trying to be smart and do it in the initaliser list, which is admirable but also wrong - because at that stage the memory hasn''t been allocated for the array yet.

so first
mp = new Point3[3];

to allocate the memory.

then you just do

mp[0] = p0;
...
...


Also be warey of optimising before you know there is a bottleneck. Get it done, if there''s a bottleneck you can find it and deal with it later.
inline is subjective to the compiler anyway - unless you specifically force it to inline it may disregard that. It may also inline stuff you haven''t asked it too... and it will do that itself if it knows there will be a speed increase.

Careful with mixing references and consts and things like that too. Do you really want to be sending over a reference to an object rather then a pointer to it? There is a difference when it comes to assignment.

Using a reference and setting a reference like that will invoke a copy contructor or overloaded assignment constructor, and create a new copy of the object in the array.

Using a pointer would just copy over the pointer... but then you have to decide who gets control of the lifetime of Point3... so that it gets disposed of properly.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
asp_
asp_
That was me up there. Sorry
Miserable
Miserable
quote:
Original post by paulsdsrubbish
Understand that:

Point3 mP[3];

... is just declaring a pointer to an array, not the array itself.

Er, no, it''s a statically allocated array. What are you talking about?

quote:
you have to actually create the array with a new Point3[3] to allocate the memory actually used for the array. and importantly in the destructor using delete [] mP; rather then just delete mp;

This would be true if it were a dynamically allocated array (Point3*), which it isn''t.
NLDEV
NLDEV
asp_, you may consider creating an ambiguous union inside your class to remedy the problem. Here, let me whip up what I mean.


class Plane3
{
public:
inline Plane3( const Point3 &p0, const Point3 &p1, const Point3 &p2 );

private:

union
{
Point3 m_rgPoints[ 3 ];
struct
{
Point3 m_P0, m_P1, m_P2;
};
};
};

inline Plane3::Plane3( const Point3 &p0, const Point3 &p1, const Point3 &p2 ): m_P0( p0 ), m_P1( p1 ), m_P2( p2 ) { }


This way you can have an array 'interface' to your Point3 data and still be able to initialize the data in the constructor.

[edited by - MikeMJH on June 10, 2004 4:11:00 PM]
asp_
asp_
MikeMJH,
That was beautiful. But unfortunatley it bails with error C2621 which says:

member 'identifier' of union 'union' has copy constructor
A union member cannot have a copy constructor.

So that would kind of kill the purpose of the entire thing since I HAVE to have a copy constructor. I'm using MS Visual C++ 7.1 btw, just in case it's another annoying standard non-compliance thing.

*edit*
Added MSVC version.

[edited by - asp_ on June 10, 2004 4:27:05 PM]
NLDEV
NLDEV
quote:
Original post by asp_
MikeMJH,
That was beautiful. But unfortunatley it bails with error C2621 which says:

member 'identifier' of union 'union' has copy constructor
A union member cannot have a copy constructor.

So that would kind of kill the purpose of the entire thing since I HAVE to have a copy constructor. I'm using MS Visual C++ 7.1 btw, just in case it's another annoying standard non-compliance thing.

*edit*
Added MSVC version.

[edited by - asp_ on June 10, 2004 4:27:05 PM]


Let me just write up a quick test and see if GCC gives me a similar error.

Yeah, I do. So looks like that solution is out. :-/

[edited by - MikeMJH on June 10, 2004 4:57:33 PM]
ChaosEngine
ChaosEngine
why does it have to be an array?

from the looks of things you''re hardcoding the size of the array so just split it into three Point3 objects and initialise them in the initialiser list.

if you really need array access you could declare an array of 3 pointers to Point3 objects and access them through that for loops, whatever.

You basically have the following options (there are probably more, but it''s early):
1) leave the code as it is and pay for the default constructor
2) make an array of heap objects
3) use a vector of objects
4) see above

BTW don''t listen to paulsdsrubbish, I think he thinks you''re using java or something!!

"That''s not a bug, it''s a feature!"
--me
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
asp_
asp_
ChaosEngine,
quote:

why does it have to be an array?


It doesn''t HAVE to be. I could have three member variables but that would prevent me from accessing it like an array. Of course I could assume that they will be thighly packed and just take the pointer of the first member variable and use that as the base address of the array but that doesn''t feel like solid coding.

quote:

from the looks of things you''re hardcoding the size of the array so just split it into three Point3 objects and initialise them in the initialiser list.


No.

quote:

if you really need array access you could declare an array of 3 pointers to Point3 objects and access them through that for loops, whatever.


Pointers = needs to be allocated with new which is too slow for what I want. Using loops when accessing the elements would definitely kill my performance and I see no reason why I''d need to do that even with pointers.

quote:

You basically have the following options (there are probably more, but it''s early):
1) leave the code as it is and pay for the default constructor
2) make an array of heap objects
3) use a vector of objects
4) see above


1) Yeah that is an option and is what I''m currently using. But I have several classes like this and even though it''s not THAT costly, it''s not free either.
2) Not an option. Would be slower than option 1.
3) Even slower than option 2.
4) Error: C4717

I''m going to be using option 1 until Stroustrup appears in a vision and tells me what the true path is. Thanks for the replies
Fruny
Fruny
quote:
Original post by asp_
It doesn't HAVE to be. I could have three member variables but that would prevent me from accessing it like an array. Of course I could assume that they will be thighly packed and just take the pointer of the first member variable and use that as the base address of the array but that doesn't feel like solid coding.


Class-static array of pointers to your member variables


class Foo
{
Bar a, b, c;

// array of 3 pointers to Bar members of Foo
static Bar Foo::*array[3];

public:
Bar& operator[](size_t idx)
{ return this->*array[idx]; }

const Bar& operator[](size_t idx) const
{ return this->*array[idx]; }
};

// Parse this
Bar Foo::*Foo::array[3] = { &Foo::a, &Foo::b, &Foo::c };



“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan

[edited by - Fruny on June 11, 2004 2:57:48 AM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
thedevdan
thedevdan
Okay, first of all, stop everything you are doing. You don't need to be optimizing like this, expecially with that compiler. The inline keyword doesn't do anything , by the way, in any recent compiler.

How do you know you need to optimize? Was your old code too slow?

[edited by - thedevdan on June 11, 2004 3:21:00 AM]

[edited by - thedevdan on June 11, 2004 3:21:14 AM]
Not giving is not stealing.
quorn
quorn
quote:
Original post by thedevdan
Okay, first of all, stop everything you are doing. You don''t need to be optimizing like this, expecially with that compiler.

it does seem a little premature.

quote:
The inline keyword doesn''t do anything , by the way, in any recent compiler.


Cough. Splutter... you are most certainly making that up.

quote:
How do you know you need to optimize? Was your old code too slow?


good point.
asp_
asp_
thedevdan,

quote:

Okay, first of all, stop everything you are doing. You don''t need to be optimizing like this, expecially with that compiler. The inline keyword doesn''t do anything , by the way, in any recent compiler.

How do you know you need to optimize? Was your old code too slow?


This isn''t optimizing. My SSE based classes are optimized. This is just writing solid code by using common sense so that you make sure to avoid misstakes the compiler might make. The way I''m doing it I know what will happen as it''s specified by the standard. The way you want me to do it I''ll be praying for all compilers to all be heavily optimizing and similar in implementation. I prefer my way and it takes me a second or so more to write the code my way
I''ve pretty much gone thru my entire coding life hearing: "Premature optimization is the root of evil.". I don''t optimize prematurely (ahem). What I do is design algorithmic optimizations, write solid code and then profile what needs to be optimized. In this case this is just a matter of solid code and the fact that this is something I need. It must be indexable like an array and constructed without the default constructor.
By the way, the inline keyword actually causes some functions to be considered and inlined which wouldn''t normally be so. It''s a hint not a command. Then again you probably knew this

Fruny,
Thanks for the suggestion! So you take a static array, fill it with the addresses of the members and then dereference the pointers inside, hehe. Clever Kind of reminds me of a manual jump table but for variables. However, when the class is constructed wont the array have to be filled with the data, causing three assignments which wouldn''t otherwise be there? The problem is I to have as close to 0 unnecessary overhead as possible. Cool suggestion though.

Anyone have a good reason for why MikeMJH''s suggestion isn''t allowed?

quoting myself:
quote:

Of course I could assume that they will be thighly packed and just take the pointer of the first member variable and use that as the base address of the array but that doesn''t feel like solid coding.


Is there a reason, other than alignment, why they wouldn''t be tightly packed if in a struct? Just thinking of the MikeMJH''s suggestion which would essentially be the same:


class Plane3
{
public:
inline
Plane3(const Point3 &p0,
const Point3 &p1,
const Point3 &p2)
: mP0(p0), mP1(p1), mP2(p2)
{
};

inline Point3 &
operator [](int el);

private:
// Hopefully tightly packed? Yes I''m from an asm and C

// background ;) If the compiler chooses a padding in

// order to accomodate alignment we''re screwed. Fortunatley

// Point3''s are 16-byte aligned and fits perfectly ;). No this

// isn''t ugly, it''s much worse than that. Hideous? It does the

// work though and would work in any case where the

// sizeof(element) % 4 == 0 since 4 byte alignment is what you

// usually see and would yield a distance of 0.

// Does the struct even effect distance between member

// objects?

struct
{
Point3 p0;
Point3 p1;
Point3 p2;
};
};

inline Point3 &
Plane3::operator[](int el)
{
assert(el < 3);
return (&mP0)[el];
};



Maybe that''s just plain stupid and going way overboard...
Fruny
Fruny
quote:
However, when the class is constructed wont the array have to be filled with the data, causing three assignments which wouldn''t otherwise be there? The problem is I to have as close to 0 unnecessary overhead as possible.


It''s a static array.


“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
paulecoyote
paulecoyote
*DISCLAIMER - all code provided after 3PM and before 10AM may be complete bollocks, and no quality is assured. Any code produced on Fridays in particular is "before the weekend" quality code and may contain alcohol inspired functions and comments. Author has recently been having to do way too much Java and C# for his own good*

Yeah sorry everyone about the array thing.

Pretty sure when you pass things using func( foo array[] ) that you pretty much get a reference because arrays are passed by "simulated call-by-reference". Arrays don't tend to be put on the stack.

Anyway with my tail between my legs I've tried for a few minutes to do the initalisation in an initaliser list - and I get this:
error C2536: 'Plane3::arPoints' : cannot specify explicit initializer for arrays

And I can't do it with dynamic arrays either
error C2538: new : cannot specify initializer for arrays

So I think you'll just have to do it in the constructor body rather then the list.

At least I was right about the inline stuff... I think.
[EDIT: Well it depends on the compiler and compiler settings - in Visual C++ 6 you can set it to create larger code for speed, or smaller code for size - which can override your inline statements]

Anyway I'm going to have a very strong cup of coffee, sit at the back of the class with a dunce hat on and think about what I've done. ;-)

[edited by - paulsdsrubbish on June 11, 2004 5:01:41 AM]
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
asp_
asp_
Fruny,
quote:

It's a static array.


Yeah I noticed that The problem is that the members in the class aren't occupying the same memory space from class instance to instance. Thus the array would have to be refilled with the memory address of each of the member variables every time the class is instatiated. Seems to me like one instance of your Plane3 class would overwrite the address of the previous instance making having several Plane3 instances impossible. Am I being a total retard?? ;P

paulsdsrubbish,
quote:

And I can't do it with dynamic arrays either
error C2538: new : cannot specify initializer for arrays


I think I provided an example with dynamic array initialization previously. There's no reason to initialize a dynamic array in the initializer list of a constructor though.

Thanks for the help everyone is providing even though no one seems to agree with what I'm doing or rather trying to do

*edit*
Spelling.

[edited by - asp_ on June 11, 2004 5:19:37 AM]
Fruny
Fruny
The problem is that the members in the class aren't occupying the same memory space from class instance to instance.

That's not a problem.

Thus the array would have to be refilled with the memory address of each of the member variables every time the class is instatiated. Seems to me like one instance of your Plane3 class would overwrite the address of the previous instance making having several Plane3 instances impossible.

No. Look harder at the code I've written. It doesn't quite work the way you think it works. If you pay close attention, I believe you'll learn something new

Am I being a total retard?? ;P

*shrug*

Edit - hint: there is a huge difference between the Bar* and Bar Foo::* types.


“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan

[edited by - Fruny on June 11, 2004 5:32:25 AM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
SiCrane
SiCrane
quote:
Original post by asp_
Fruny,
quote:

It''s a static array.


Yeah I noticed that The problem is that the members in the class aren''t occupying the same memory space from class instance to instance. Thus the array would have to be refilled with the memory address of each of the member variables every time the class is instatiated. Seems to me like one instance of your Plane3 class would overwrite the address of the previous instance making having several Plane3 instances impossible. Am I being a total retard?? ;P




The array doesn''t need to be refilled for every class instance. The array is filled with pointers to members, which is different from standard pointers. Hence the strange this->* notation. It might help if you considered the pointers to members to be offsets into the class (though that''s not an entirely accurate description).
asp_
asp_
SiCrane,
Ahh I see what it does now.

Fruny,
I guess your solution is the way to go then Thanks! Tried it out and it works excellent. As I said previously, clever solution


Thanks everyone.
Aprosenf
Aprosenf
The reason that MikeMJH''s solution doesn''t work is because, as the error says, members of a union can''t have default constructors. Think about it - if you had:

union
{
Foo foo;
Bar bar;
};
and Foo and Bar had their own constructors that did different things, what would happen? They would both be trying to do stuff with the same memory, and the order they''d get constructed in would be undefined also.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.