Jump to content
  • Advertisement
Sign in to follow this  
jinuuchukuu

Arrays with Structs and constructors !?

This topic is 4649 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

It seems I went writing this same threat in the bad folder: For beginners… So I retry my chance here if somebody could help me with a typedef struct I took from a tutorial, having thought I could add another struct containing a constructor too, to ease the operations on those packages of data…the package is not so big, but it could be it fast… So here they are:
typedef struct tVector3
{			
	tVector3() {}	// constructor
	tVector3 (float new_x, float new_y, float new_z) // initialize constructor	 
	{x = new_x; y = new_y; z = new_z;}
	tVector3 operator+(tVector3 vVector) {return tVector3(vVector.x+x, vVector.y+y, vVector.z+z);}
	tVector3 operator-(tVector3 vVector) {return tVector3(x-vVector.x, y-vVector.y, z-vVector.z);}
	tVector3 operator*(float number)	 {return tVector3(x*number, y*number, z*number);}
	tVector3 operator/(float number)	 {return tVector3(x/number, y/number, z/number);}
	
	float x, y, z;
}tVector3;

typedef struct tPlan2
{
	tPlan2() {}
	tPlan2(tVector3 new_a, tVector3 new_b)
	{a= new_a; b=new_b;}
	tPlan2 operator+(tPlan2 vPlan) {return tPlan2(vPlan.a+a, vPlan.b+b);}
	tPlan2 operator-(tPlan2 vPlan) {return tPlan2(a-vPlan.a, b-vPlan.b);}
	tPlan2 operator*(float num) {return tPlan2(num*a, num*b);}
	tPlan2 operator/(float num) {return tPlan2(a/num, b/num);}

	tVector3	a;
	tVector3	b;
}tPlan2;

typedef struct tPlan4
{
	tPlan4() {}
	tPlan4(tVector3 new_a, tVector3 new_b, tVector3 new_c, tVector3 new_d)
	{a=new_a; b=new_b; c=new_c; d=new_d;}
	tPlan4 operator+(tPlan4 vPlan) {return tPlan4(vPlan.a+a, vPlan.b+b, vPlan.c+c, vPlan.d+d);}
	tPlan4 operator-(tPlan4 vPlan) {return tPlan4(vPlan.a-a, vPlan.b-b, vPlan.c-c, vPlan.d-d);}
	tPlan4 operator*(float num) {return tPlan4(num*a, num*b, num*c, num*d);}
	tPlan4 operator/(float num) {return tPlan4(a/num, b/num, c/num, d/num);}

	tVector3	a;
	tVector3	b;
	tVector3	c;
	tVector3	d;
}tPlan4;

Pretty please, how could I retrieve a usable form from these structures to avoid any error from the compiler ?! Which are: error C2677: binary '*' : no global operator defined which takes type 'struct tVector3' (or there is no acceptable conversion) all for the lines only concerning * operator in tPlan4 or tPlan2…?! Someone somebody some spirit ? After that most important if that somewhat is known how to declare and use arrays with that type of typedefStruct pleaseYourself !!
edit by k2: added source tags [Edited by - kSquared on October 22, 2005 7:49:49 AM]

Share this post


Link to post
Share on other sites
Advertisement
You haven't defined a '*' operator to take, respectively, a float then a tVector3, but you have an operator '*' defined for tVector3 that takes a float as its argument. You just need to replace all instances of "num*x" with "x*num", where x is any tVector3.

Also, if this is C++, you don't need the typedefs.

EDIT: So something like this:
struct tVector3
{
tVector3() {} // constructor
tVector3 (float new_x, float new_y, float new_z) // initialize constructor
{x = new_x; y = new_y; z = new_z;}
tVector3 operator+(tVector3 vVector) {return tVector3(vVector.x+x, vVector.y+y, vVector.z+z);}
tVector3 operator-(tVector3 vVector) {return tVector3(x-vVector.x, y-vVector.y, z-vVector.z);}
tVector3 operator*(float number) {return tVector3(x*number, y*number, z*number);}
tVector3 operator/(float number) {return tVector3(x/number, y/number, z/number);}

float x, y, z;
};

struct tPlan2
{
tPlan2() {}
tPlan2(tVector3 new_a, tVector3 new_b)
{a= new_a; b=new_b;}
tPlan2 operator+(tPlan2 vPlan) {return tPlan2(vPlan.a+a, vPlan.b+b);}
tPlan2 operator-(tPlan2 vPlan) {return tPlan2(a-vPlan.a, b-vPlan.b);}
tPlan2 operator*(float num) {return tPlan2(a*num, b*num);}
tPlan2 operator/(float num) {return tPlan2(a/num, b/num);}

tVector3 a;
tVector3 b;
};

struct tPlan4
{
tPlan4() {}
tPlan4(tVector3 new_a, tVector3 new_b, tVector3 new_c, tVector3 new_d)
{a=new_a; b=new_b; c=new_c; d=new_d;}
tPlan4 operator+(tPlan4 vPlan) {return tPlan4(vPlan.a+a, vPlan.b+b, vPlan.c+c, vPlan.d+d);}
tPlan4 operator-(tPlan4 vPlan) {return tPlan4(vPlan.a-a, vPlan.b-b, vPlan.c-c, vPlan.d-d);}
tPlan4 operator*(float num) {return tPlan4(a*num, b*num, c*num, d*num);}
tPlan4 operator/(float num) {return tPlan4(a/num, b/num, c/num, d/num);}

tVector3 a;
tVector3 b;
tVector3 c;
tVector3 d;
};

Share this post


Link to post
Share on other sites
More generally, so that you do not fall foul of this problem when you use operator* in other code, you should implement a non-member operator* which takes a first parameter of type float and a second parameter of type tPlan2 or tPlan4 as appropriate.

Additionally, you should add const qualification to your operators and I would advise making the existing versions non member as well, since there is no need for them to be members:
struct Vector3
{

Vector3()
{
}

Vector3(float new_x, float new_y, float new_z)
:
x(new_x),
y(new_y),
z(new_z)
{
}

float x, y, z;

};

Vector3 operator+(Vector3 const & lhs, Vector3 const & rhs)
{
return Vector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}

Vector3 operator-(Vector3 const & lhs, Vector3 const & rhs)
{
return Vector3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);
}

Vector3 operator*(Vector3 const & vector, float multiplier)
{
return Vector3(vector.x * multiplier, vector.y * multiplier, vector.z * multiplier);
}

Vector3 operator*(float multiplier, Vector3 const & vector)
{
return vector * multiplier;
}

Vector3 operator/(Vector3 const & vector, float divisor)
{
return Vector3(vector.x / divisor, vector.y / divisor, vector.z / divisor);
}

struct Plan2 // Plane2 or PlanePointNormal?
{

Plan2()
{
}

Plan2(Vector3 new_a, Vector3 new_b) // use better names
:
a(new_a),
b(new_b)
{
}

Vector3 a;
Vector3 b;

};

Plan2 operator+(Plan2 const & lhs, Plan2 const & rhs)
{
return Plan2(lhs.a + rhs.a, lhs.b + rhs.b);
}

Plan2 operator-(Plan2 const & lhs, Plan2 const & rhs)
{
return Plan2(lhs.a - rhs.a, lhs.b - rhs.b);
}

Plan2 operator*(Plan2 const & plan, float multiplier)
{
return Plan2(plan.a * multiplier, plan.b * multiplier);
}

Plan2 operator*(float multiplier, Plan2 const & plan)
{
return plan * multiplier;
}

Plan2 operator/(Plan2 const & plan, float divisor)
{
return Plan2(plan.a / divisor, plan.b / divisor);
}

struct Plan3 // Plane3 or PlaneCartesian?
{

Plan3()
{
}

Plan3(Vector3 new_a, Vector3 new_b, Vector3 new_c, Vector3 new_d) // use better names
:
a(new_a),
b(new_b),
c(new_c),
d(new_d)
{
}

Vector3 a;
Vector3 b;
Vector3 c;
Vector3 d;

};

Plan3 operator+(Plan3 const & lhs, Plan3 const & rhs)
{
return Plan3(lhs.a + rhs.a, lhs.b + rhs.b, lhs.c + rhs.c, lhs.d + rhs.d);
}

Plan3 operator-(Plan3 const & lhs, Plan3 const & rhs)
{
return Plan3(lhs.a - rhs.a, lhs.b - rhs.b, lhs.c - rhs.c, lhs.d - rhs.d);
}

Plan3 operator*(Plan3 const & plan, float multiplier)
{
return Plan3(plan.a * multiplier, plan.b * multiplier, plan.c * multiplier, plan.d * multiplier);
}

Plan3 operator*(float multiplier, Plan3 const & plan)
{
return plan * multiplier;
}

Plan3 operator/(Plan3 const & plan, float divisor)
{
return Plan3(plan.a / divisor, plan.b / divisor, plan.c / divisor, plan.d / divisor);
}

It may also be an idea to implement operator+=, operator-=, operator*= and operator/= and implement your current operators in terms of those, i.e.:
Vector3 & operator+=(Vector3 & lhs, Vector3 const & rhs)
{
lhs.x += rhs.x;
lhs.y += rhs.y;
lhs.z += rhs.z;
return lhs;
}

Vector3 operator+(Vector3 lhs, Vector3 const & rhs)
{
return lhs += rhs;
}

Enigma

Share this post


Link to post
Share on other sites
Mostly consistency, but also (a very small part) future-proofing.

Consistency for two reasons. First, operatorX(Non-ClassInstance, ClassInstance) must be a non-member and therefore for consistency the other operators should be non-members as well. Secondly any class that has an implicit type conversion from X (i.e. any class with a non-explicit constructor taking at least one parameter for which all or all but the first parameter have default values) will need to have non-member operators in order to gain type conversion of the left hand object:
struct OtherType
{
};

struct Type
{

Type();
Type(OtherType const & ot);
Type operator+(Type const & t) const; // member operator

};

Type operator-(Type const & lhs, Type const & rhs); // non-member operator

int main()
{
Type t1;
OtherType ot1;
Type t2 = t1 + ot1; // fine - type conversion on parameter
Type t3 = ot1 + t1; // error - no type conversion possible on "this"
Type t4 = t1 - ot1; // fine - type conversion on second parameter
Type t5 = ot1 - t1; // fine - type conversion on first parameter
}

Since classes which require implicit type conversion on the left hand object must have non-member operators all classes should have non-member operators for consistency.

Finally, the future proofing leads on from the last point. If the class were ever to gain a type conversion from X then it's operators will need to be non-members. There is obviously less maintenance to do if the operators are already non-members.

Enigma

Share this post


Link to post
Share on other sites
Everything went super well! For sure it was a sure thing ;-) It works as on wheels, but as the truncated f numbers that dig you intervals of mistakes, the unknown of those quadric parentheses welcomes the times of “arrays uses” seeking and tries…with that kind of structure.
I thought about something like this :
struct zen{
The wind flies, the water wets, the fire burns;

No idea of how;
}


To be continued...

Share this post


Link to post
Share on other sites
Quote:
Original post by Enigma
Mostly consistency, but also (a very small part) future-proofing.

Consistency for two reasons. First, operatorX(Non-ClassInstance, ClassInstance) must be a non-member and therefore for consistency the other operators should be non-members as well. Secondly any class that has an implicit type conversion from X (i.e. any class with a non-explicit constructor taking at least one parameter for which all or all but the first parameter have default values) will need to have non-member operators in order to gain type conversion of the left hand object:
*** Source Snippet Removed ***
Since classes which require implicit type conversion on the left hand object must have non-member operators all classes should have non-member operators for consistency.

Finally, the future proofing leads on from the last point. If the class were ever to gain a type conversion from X then it's operators will need to be non-members. There is obviously less maintenance to do if the operators are already non-members.

Enigma




I see, but why does hardly anyone make those operators non member?
Even in 99 C++ gotchas they make operators members of the class which sounds seems logical to me, because you encapsulate the implementation into the type which makes maintainance a lot easier if you switch class types later on in your project
you just make sure the Class-Instance Class-Instance operators fullfil the specification and you are fine
only the none-Class-Instance operators need to be changes

Share this post


Link to post
Share on other sites
You didn’t write for nothing enigma if you counted to be understood…
After revising my pointers and references I got it fine ‘bout the type conversion.

Thanks Basiror for asking ‘bout the non-member, I wouldn’t ask until the next error!

The “opened” construction of operators making possible the conversion of data between references… but as usual? I mean as a simple conversion between an integer and a float for example, following the same rules?
Conserving the required use of references and pointers I see…

Share this post


Link to post
Share on other sites
Quote:
Original post by Basiror
I see, but why does hardly anyone make those operators non member?

Because Enigma's approach requires that your class/struct have public member variables, which (generally) contradicts encapsulation and data hiding. For a simple numerical type like a vector, his approach may be adequate, but not for more complex types with internal representations requiring a high degree of numerical stability. Exposing those publicly almost invites (dumber) programmers to directly access the values.

Of course, property-like accessors could ameliorate the problem somewhat.

Share this post


Link to post
Share on other sites
In response to Oluseyi's point I would point out that although I didn't state so explicitly (and probably should have done) what I wrote only fully applies to operatorX and not operatorX=. Since operatorX= can never require an implicit type conversion on the left hand operand or have a left hand operand of non-class type operatorX= can safely be made a member and operatorX a non-member implemented in terms of operatorX=.

You may feel that breaks the point I was making about consistancy, but the real meat of the point is that if you always implement operatorX as a non-member it will never be a wrong choice. Equally if you always implement operatorX= as a member it will never be a wrong choice. The fact that I wrote a non-member operatorX= in my first post nonwithstanding (since that was mostly to save a few lines rather than for any deep reason).

Enigma

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!