Arrays with Structs and constructors !?

Started by
14 comments, last by stylin 18 years, 5 months ago
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]
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;};
MumbleFuzz
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
why should he make the operators non members?

http://www.8ung.at/basiror/theironcross.html
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 operatorint 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
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...
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

http://www.8ung.at/basiror/theironcross.html
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…
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.
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

This topic is closed to new replies.

Advertisement