LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-18-2011, 02:31 PM   #1
worzel1968
Member
 
Registered: Mar 2011
Location: Melbourne, Australia
Distribution: fedora17_64, ubuntu
Posts: 44

Rep: Reputation: 2
C++ classes - a matter of style? inherit or not inherit?


Hello everyone,

First of all, I would like to thank everyone in advance - I am looking forward to your input and appreciate the time & thought involved in helping others.

Also this post isn't to do with any sort of homework or study - I am doing this because I find it more interesting than Sudoku!!

My question is related to classes that don't really need to be inherited by any other class, (because they have no functions or variables that would be used by the derived class), but will be used by the other classes as an object type. I might call these orphan classes.

As an example, I have classes called CDecDeg, CDegMinSec, CGrad, CMil which are all various types of angle units. Each of these classes stores its angle as a double. There are no other functions or variables in these classes. The idea is to have types for all my objects.

I also have a Class called CAngle, which has an overloaded function called Set, which takes an argument that is one of the angle units classes and converts the angle to Radians. The purpose of the CAngle class is to hold all the functions & operators that will be used by its derived classes shown below.

My first question is: Should CAngle be derived from CDecDeg and CDegMinSec and CGrad and CMil, or should they be left as an orphan classes? Or more generally, should all classes in an App derived from some other class so that all the classes are connected?

I could go as far as doing this: Psuedocode showing class inheritance.

Code:
class CUnits{};
class CAngUnits : CUnits {};
class CDistUnits : CUnits {};

class CDecDeg : CAngUnits {};
class CDegMinSec : CAngUnits {}; 
class CGrad : CAngUnits {};
class CMil : CAngUnits {};

class CAngle : CDecDeg, CDegMinSec, CGrad, CMil {};

class CBearing : CAngle {};    //like a compass bearing 
class CHAngle : CAngle {};     // a horizontal angle
class CZenAngle : CAngle {};   //Zenith Angle ( angle in vertical plane

class CDistMetre : CDistUnits {};
class CDistFeet : CDistUnits {};
class CDistYard : CDistUnits {};
class CDistLink : CDistUnits {};
class CDistChain : CDistUnits {};

class CDist : CDistMetre, CDistFeet, CDistYard, CDistLink, CDistChain {};

class CHDist : CDist {};   // a horizontal distance
class CSlopeDist : CDist {}; 
class CVertDist  : CDist {};
I am planning to further extend all this to implement 3D points and all kinds of geometry.

The Psuedocode above ties all the classes together (quite tidy) but there is nothing in the CUnits, CAngUnits, CDistUnits classes.

Is all this good form? Just trying to see if I am on the right track with my thinking.

Now this brings me to my next question:

I have gathered that classes are extended from the most general to the more detailed, so for an application the top class is the Application class. This is extended to a Window class, which is extended to say a Widget class. Let me know if I have any of this wrong.

I am wondering how all my classes shown above will fit into this scheme?

Thanks again for your help & look forward to your reply.

Cheers David.

capio nox noctis

Einstein was wrong
 
Old 03-18-2011, 02:43 PM   #2
dugan
LQ Guru
 
Registered: Nov 2003
Location: Canada
Distribution: distro hopper
Posts: 9,486

Rep: Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223Reputation: 4223
Quote:
Originally Posted by worzel1968 View Post
should all classes in an App derived from some other class so that all the classes are connected?
No. There are relationships other than "is a" (inheritance). There are also "has a" (when class has an instance or static variable that is the same type as another class) and "uses" (where a class' method takes as a parameter a variable that is the same type as another class). Therefore, you can easily connect (couple) all the classes without having them all part of the same inheritance tree. Note that minimizing coupling is generally a good thing.

The very best resource I've found for getting up to speed on object oriented analysis and design is this book, which I recommend buying:

http://www.amazon.com/First-Design-P...dp/0596007124/

Last edited by dugan; 03-18-2011 at 02:48 PM.
 
1 members found this post helpful.
Old 03-18-2011, 02:50 PM   #3
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194
Quote:
Originally Posted by worzel1968 View Post
CDegMinSec, . . . Each of these classes stores its angle as a double.
I can't tell whether you have thought about this badly or just described it badly.

Can you give a better description of what CDegMinSec is or stores or does? I think that might clear up some ambiguity that runs across your whole discussion.

I can't see how you can directly store Degrees, Minutes, Seconds in a double.

Maybe you didn't really mean "a double". Maybe it stores two int's (degrees and minutes) as well as a double (seconds).

Maybe you meant each of those classes actually stores the value in some common form and the class exists to somehow convert between its nominal form and common form, but never stores its nominal form. But that is still a very ambiguous/confusing idea without an interface spec.

Whatever you mean, I have a lot of trouble imagining CAngle inheriting from a bunch of different kinds of angle. That would mean each CAngle object contains a whole object of each one of those types.

There are a lot of style questions around what should inherit from what. Object Oriented orthodoxy holds that inheritance strictly corresponds to real world "is a" relationships. That strict theory just doesn't hold up in practice. What should inherit from what is a complex question with multiple valid points of view.

But first you need to understand the basic concept of what inheritance means.

Your post seems to show that you don't yet get the basic concept. Worry about style questions only after you have the basics down.

Last edited by johnsfine; 03-18-2011 at 03:03 PM.
 
1 members found this post helpful.
Old 03-18-2011, 04:04 PM   #4
paulsm4
LQ Guru
 
Registered: Mar 2004
Distribution: SusE 8.2
Posts: 5,863
Blog Entries: 1

Rep: Reputation: Disabled
Hi -

1. Abstracting things like "CDecDeg", "CBearing" and stuff like that into classes was a Good Idea.
As a general rule of thumb, if you *can* implement something with a class, then you usually *should*.

2. As another general rule of thumb, "K.I.S.S".
Unless you absolutely need inheritance, then you usually *should't* do it.

3. Perhaps CAngle might be a good base class (from which CDegMinSec, or CDegrees vs CRadians might derive).
If you must use inheritance (and again, I'd discourage it unless it actively *helps* your implementation), then be sure the more GENERAL class is the base class, and the more SPECIFIC classes are subclasses.

'Hope that helps .. PSM

PS:
I wholeheartedly endorse Dugan's book recommendation, "Head First Patterns". The whole "Head First" series is great; this particular book is no exception.

Last edited by paulsm4; 03-18-2011 at 11:50 PM.
 
1 members found this post helpful.
Old 03-18-2011, 04:30 PM   #5
worzel1968
Member
 
Registered: Mar 2011
Location: Melbourne, Australia
Distribution: fedora17_64, ubuntu
Posts: 44

Original Poster
Rep: Reputation: 2
Thanks for your quick replies.

Ok, I understand now that CAngle has no need to inherit CDecDeg or any of the other angle types. So the purpose of CDecDeg et al is to act as types so that I can overload the Set function in CAngle. I understand now that this is the "uses" case mentioned by dugan.


Quote:
Can you give a better description of what CDegMinSec is or stores or does?
Quote:
I can't see how you can directly store Degrees, Minutes, Seconds in a double.
Sorry johnsfine I wasn't very clear about that. I should also mention that I have been working as a Land Surveyor for the last 25 years - so I am very aware of how deg, min, secs works.

Ok, my intention was for CDegMinSec to have a member variable of type double in ddd.mmssssss... format with sssss.. meaning ss.sss seconds with a variable number of decimal places. This value will be converted in a CAngle::Set function to radians by the following algorithm:

The integer part gives the degrees. The fraction part multiplied by 100.0 gives the minutes, in the format mm.sssss... the integer part of this gives the minutes, and the fraction part multiplied by 100.0 gives the seconds. These 3 variables can easily be used to convert to decimal degrees & then to Radians. Once CAngle::Set is finished with the CDegMinSec object, it will be deleted.

I should clear up about the purpose of the CAngle class:

Firstly it's class that will be extended to CBearing, CHAngle & CZenAngle. So the intention is to create objects of these types, not to create CAngle objects.

CAngle has the overloaded function Set which takes a pointer to one of the CDecDeg, CDegMinSec, CGrad, CMil objects. The Set function converts the double pointed to, to radians in the range 0<= x < 2PI & stores it in a private member variable in say CBearing.

The reasons for doing this are twofold. First, the trig functions require a value in radians, in this range. Second, If I have a number of different units, and wish to be able to convert from any unit to any other unit, then it makes sense to convert the original unit into a standard unit, and convert from this into the target unit. In this way, I would have 2(n-1) conversion functions to write instead of n squared functions. So if I was dealing with distances, I would convert the original distance to metres, and the convert the metres to the target unit.

The CAngle class will also have functions to convert from radians back to the original input units for the purpose of output eg to a file. Also there will be any overloaded operators or anything else that might be required by CBearing, CHAngle & CZenAngle. So the idea is to write all these things once in the CAngle class, so they can be used in the derived classes. Am I right in thinking that this is the idea behind inheritance?

Thanks again for your help.

David
 
Old 03-18-2011, 07:40 PM   #6
paulsm4
LQ Guru
 
Registered: Mar 2004
Distribution: SusE 8.2
Posts: 5,863
Blog Entries: 1

Rep: Reputation: Disabled
Hi -
I'm still not sure the design is entirely appropriate for the problem (because "angles" aren't very complex or very active entities...), but:
Quote:
So the idea is to write all these things once in the CAngle class, so they can be used in the derived classes. Am I right in thinking that this is the idea behind inheritance?
Absolutely correct
 
Old 03-18-2011, 08:35 PM   #7
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194
Quote:
Originally Posted by worzel1968 View Post
my intention was for CDegMinSec to have a member variable of type double in ddd.mmssssss... format with sssss.. meaning ss.sss seconds with a variable number of decimal places.
I think that is a bad idea. The data type double does not store exact decimal values.

I am pretty sure that there are values of ddd and mm such that when you combine ddd+.01*mm with zero for sssss, you actually get slightly less that the correct value. Writing the value to output may look right because of more rounding (making debugging even harder) but splitting the value back out would give you ddd, mm-1 and 99.999 seconds.

The obvious rounding glitches could be solved by checking for unreasonable values after unpacking. If after unpacking sssss is slightly greater than 59999, then just change it to 59999. If sssss is 99999 or slightly less then change sssss to 00000 and deduct 1 from mm and if that results in -1 change it to 59 and deduct 1 from ddd.

That is already messy enough I'd rethink the plan. But I'm not sure enough of your intent to say that coverup of rounding glitches would be sufficient.
 
Old 03-18-2011, 08:46 PM   #8
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194
Quote:
Originally Posted by worzel1968 View Post
Firstly it's class that will be extended to CBearing, CHAngle & CZenAngle. So the intention is to create objects of these types, not to create CAngle objects.
I think you might be confused about the conceptual purpose of a class. I think you do have the nominal "is a" relationship in saying CBearing should extend CAngle. But what about bearing makes it appropriate to be a class at all?

I would expect bearing to be a member of some more interesting object, but the type of that member would just be CAngle.

Quote:
The Set function converts the double pointed to, to radians in the range 0<= x < 2PI & stores it in a private member variable in say CBearing.
That also misses the point of using the CAngle class. Whether CAngle is a base class of CBearing or a bearing is just a data value of type CAngle, the CAngle itself should contain the value. It is wrong for CAngle to just contain a method for accessing some other member of CBearing.
 
Old 03-19-2011, 12:51 AM   #9
worzel1968
Member
 
Registered: Mar 2011
Location: Melbourne, Australia
Distribution: fedora17_64, ubuntu
Posts: 44

Original Poster
Rep: Reputation: 2
Hi Paul, John

Quote:
I'm still not sure the design is entirely appropriate for the problem (because "angles" aren't very complex or very active entities...), but:
If I can explain a bit more about how & why I am trying to implement all this.

1: I am trying to abstract all of my objects so that they have types, this makes it easier to supply meaningful arguments to overloaded functions. I could just try to implement everything with basic types such as double or strings, I am sure that I would run into trouble with args to overloaded functions because of the arg types. Eg I could not have function(double, double) and function(double, double).
So I am creating classes for all the different types to get over this problem. The classes are also used to separate different units such CDecDeg & CMils both of which are angles.

2: I need to differentiate between 2 types that look similar, but are actually used for different things. Eg CBearing & CZenAng. CBearing is an Angle from north in the horizontal plane, whereas CZenAng is angle in the vertical plane. CZenAng is used along with CSlopeDist and sin & cos to calculate CHDist & CVertDist.
CBearing is used along with CHDist (a Horizontal Distance) and sin & cos and CVertDist to calc new point coordinates.
Also CBearing + CHAng = CBearing and similarly CBearing - CBearing = CHAng. So CBearing, CZenAng & CHAng are all angles, but used for different purposes. Likewise for the Distances.

Quote:
But what about bearing makes it appropriate to be a class at all?

I would expect bearing to be a member of some more interesting object, but the type of that member would just be CAngle.
3: I am intending to use the CBearing, CZenAng & CHAng and the CSlopeDist, CHDist & CVertDist as arguments to overloaded functions in the C3DPoint class. I would like to do the following : (Psuedocode)

Code:
class C3DPoint{
CEast East;          //a x ordinate
CNorth North;        //a y ordinate
CReducedLevel Rl;    //a z ordinate height above Sea Level
   
int    CreatePoint(CEast *e, CNorth *n, CReducedLevel *rl);   //return an int for success or failure
int    CreatePoint(C3DPoint *Point, CBearing *Brg, CZenAng *ZenAng, CSlopeDist *SlopeDist);
}
The classes CEast, CNorth, CReducedLevel all have 1 private member which is a double. This may seem over the top, but it is in keeping with the policy of abstracting everything.

I could have had CEast, CNorth, CReducedLevel as doubles inside C3Dpoint, but it will be important to distinguish the different types in more complicated calculations. Eg CEast is an easting in a plane co-ordinate system, whereas CEastGrid is an easting in an ellipsoidal co-ordinate system, so it is important to make the distinction. In Surveying there are a lot of examples like this.

I made CreatePoint an overloaded function not an overloaded constructor, because I might need to extend C3DPoint further.

The class C3Dpoint is also going to implement the following to do with vector algebra: Addition & subtraction of points, scalar multiplication, dot & cross products, magnitude, unit vectors, plus all the other stuff concerning points, lines & planes.

I hope that this makes it clearer about what I am trying to do, and why I am abstracting all these classes.

Quote:
I am pretty sure that there are values of ddd and mm such that when you combine ddd+.01*mm with zero for sssss, you actually get slightly less that the correct value. Writing the value to output may look right because of more rounding (making debugging even harder) but splitting the value back out would give you ddd, mm-1 and 99.999 seconds.

The obvious rounding glitches could be solved by checking for unreasonable values after unpacking. If after unpacking sssss is slightly greater than 59999, then just change it to 59999. If sssss is 99999 or slightly less then change sssss to 00000 and deduct 1 from mm and if that results in -1 change it to 59 and deduct 1 from ddd.
4: In converting from ddd.mmsssss... to decimal degress, I am sure that rounding doesn't affect the answer, because I am multipling & then taking the integer part as being the minutes, so rounding doesn't affect the integer part. Also the epsilon value for a double is at most 1E-9, so I would not worry about that for the seconds.

You are right, there will be error checking in the conversion functions. In ddd.mmsssss,0 <= ddd <= 359, 0 <= mm <= 59, 0.0 <= ss.sss... < 60.0. These are valid values for ddd.mmssssss : 359.5959999 ; 0.4359357. These values are not: 359.6059999, 359.5960000, 215.5999999. Invalid input values should flag an error.

When converting from decimal degrees to ddd.mmsssss there is also error checking to cope with mm >= 60.0 and ss.sss >= 60.0 involving the adjustment of ddd & mm respectively (similar to what you have outlined).

I would like to think that I am sure about the maths involved in these calcs, as I have been a Land Surveyor for the last 25 years. I also remember doing all this at Uni about 23 years ago - We used Fortran back then......

Quote:
That also misses the point of using the CAngle class. Whether CAngle is a base class of CBearing or a bearing is just a data value of type CAngle, the CAngle itself should contain the value. It is wrong for CAngle to just contain a method for accessing some other member of CBearing.
Quote:
Firstly it's class that will be extended to CBearing, CHAngle & CZenAngle. So the intention is to create objects of these types, not to create CAngle objects.
Ok, it seems I wasn't very clear about this either.

CBearing is derived from CAngle, so if I create a new CBearing object and then call it's Set function (inherited from CAngle), the value will be stored in the CBearing object, which is what I wanted. It is the same situation if I created a new CZenAngle object and called it's Set function.

Quote:
So the idea is to write all these things once in the CAngle class, so they can be used in the derived classes. Am I right in thinking that this is the idea behind inheritance?
Quote:
Absolutely correct
Thanks again for your helpful thoughts and input - I am pleased that I have learnt several things today. Now that I have the concepts clear, I can get going on writing the code.
 
Old 03-19-2011, 09:42 AM   #10
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194
Quote:
Originally Posted by worzel1968 View Post
In converting from ddd.mmsssss... to decimal degress, I am sure that rounding doesn't affect the answer, because I am multipling & then taking the integer part as being the minutes, so rounding doesn't affect the integer part.
I'm pretty sure you are wrong about that. I am absolutely certain that what you appear to be saying is incorrect. My only uncertainty is about the meaning of your statement, not about the behavior of double.

To make that unambiguous, look at the following simple program. Your statement seems to imply this program would print nothing. Try running the program and explain its output:
Code:
#include <iostream>
int main() {
    double store[60];
    int n;
    for (n=0; n<60; ++n)
        store[n] = n / 100.;
    for (n=0; n<60; ++n) {
        int m = (int)( store[n] * 100. );
        if ( m < n )
            std::cout << store[n] << " * 100. < " << n << std::endl;
    }
    return 0;
}
This example is much simpler than what you propose to do. Every rounding problem in this example is also present in your plan, but your plan has many more rounding problems.

Look at how m is computed in that code. Based on what you seem to have said, m could never be different from n, yet it is.

I ran that in x86_64 architecture, then ran it again in x86 architecture. The results are different, of course. But both results contradict what you seem to be saying:
Code:
john@mepis1:~$ g++ tmp.cpp
john@mepis1:~$ ./a.out
0.29 * 100. < 29
0.57 * 100. < 57
0.58 * 100. < 58
john@mepis1:~$ g++ -m32 tmp.cpp -o tmp32
john@mepis1:~$ ./tmp32
0.03 * 100. < 3
0.06 * 100. < 6
0.09 * 100. < 9
0.12 * 100. < 12
0.15 * 100. < 15
0.18 * 100. < 18
0.21 * 100. < 21
0.24 * 100. < 24
0.29 * 100. < 29
0.3 * 100. < 30
0.31 * 100. < 31
0.35 * 100. < 35
0.36 * 100. < 36
0.37 * 100. < 37
0.41 * 100. < 41
0.42 * 100. < 42
0.43 * 100. < 43
0.47 * 100. < 47
0.48 * 100. < 48
0.49 * 100. < 49
0.57 * 100. < 57
0.58 * 100. < 58
0.59 * 100. < 59
Quote:
I would like to think that I am sure about the maths involved in these calcs, as I have been a Land Surveyor for the last 25 years. I also remember doing all this at Uni about 23 years ago - We used Fortran back then......
These are fundamental aspects of floating point. They haven't changed since I learned them forty years ago.

I'll assume you know how to use more trig than I remember. But knowing floating point is not the same.

Quote:
Originally Posted by worzel1968 View Post
to supply meaningful arguments to overloaded functions.
That answers my question about why all these things ought to be classes. So the basic ideas are OK, but several of the details still look wrong.

Quote:
int CreatePoint(CEast *e, CNorth *n, CReducedLevel *rl); //return an int for success or failure
An int for success/failure is a strange choice given the basic style of ultra strong typing. Wouldn't you prefer a status enum?

The name Create is misleading. Sometimes good names are hard to choose and you're stuch with bad names confusing anyone reading your code. But where a clear name is common for a purpose, you can avoid a lot of confusion by using it. The expected name for what you're doing might be InitPoint, but not CreatePoint. Assuming you apply this operation to an existing object derived from C3DPoint, you are initializing the point portion of that object. You are not creating a new point.

Also, CEast* etc. seems a poor choice for the parameter types. An experienced C++ programmer would normally use CEast const& etc.

Quote:
The classes CEast, CNorth, CReducedLevel all have 1 private member which is a double.
So something that is a kind of distance has its own private double, but something that is a kind of angle inherits from an angle base class? Maybe I'm misunderstanding what you mean by "private member". I'm taking it to mean what it means in C++.

Quote:
CBearing is derived from CAngle, so if I create a new CBearing object and then call it's Set function (inherited from CAngle), the value will be stored in the CBearing object, which is what I wanted.
I can't tell whether that describes a bad design or is a bad description of the good design. So here is my description of the good design:

CAngle contains the double that represents the angle. That might be a protected or private member of CAngle depending on details of your design I am not guessing at.

CBearing is derived from CAngle so it contains that double as part of the CAngle object it contains. CAngle might be a public, protected, or private base class of CBearing. The actual double might be inaccessable, private or protected in CBearing depending on the combination of choices private/protected for the double in CAngle with public/private/protected CAngle withing CBearing.

Last edited by johnsfine; 03-19-2011 at 04:52 PM.
 
Old 03-19-2011, 10:04 AM   #11
z1p
Member
 
Registered: Jan 2011
Location: the right coast of the US
Distribution: Ubuntu 10.04
Posts: 80

Rep: Reputation: 23
Hi, I figure I'd throw my 2cents in for what it's worth...

Quote:
Originally Posted by worzel1968 View Post
Hi Paul, John

If I can explain a bit more about how & why I am trying to implement all this.

1: I am trying to abstract all of my objects so that they have types, this makes it easier to supply meaningful arguments to overloaded functions. I could just try to implement everything with basic types such as double or strings, I am sure that I would run into trouble with args to overloaded functions because of the arg types. Eg I could not have function(double, double) and function(double, double).
So I am creating classes for all the different types to get over this problem. The classes are also used to separate different units such CDecDeg & CMils both of which are angles.
I'd say the more appropriate way to tackle that issue is with typedefs. Don't promote something to a class just to distinguish its type.

Code:
typedef Cbearing double
typedef CZenAng  double

class myClass
{
   myOverLoadedMethod (CBearing arg);
   myOverLoadedMethod  (CZenAng arg);
  
}
Quote:
Originally Posted by worzel1968 View Post


2: I need to differentiate between 2 types that look similar, but are actually used for different things. Eg CBearing & CZenAng. CBearing is an Angle from north in the horizontal plane, whereas CZenAng is angle in the vertical plane. CZenAng is used along with CSlopeDist and sin & cos to calculate CHDist & CVertDist.
CBearing is used along with CHDist (a Horizontal Distance) and sin & cos and CVertDist to calc new point coordinates.
Also CBearing + CHAng = CBearing and similarly CBearing - CBearing = CHAng. So CBearing, CZenAng & CHAng are all angles, but used for different purposes. Likewise for the Distances.
again typedefs can help in this space.

Quote:
Originally Posted by worzel1968 View Post
3: I am intending to use the CBearing, CZenAng & CHAng and the CSlopeDist, CHDist & CVertDist as arguments to overloaded functions in the C3DPoint class. I would like to do the following : (Psuedocode)

Code:
class C3DPoint{
CEast East;          //a x ordinate
CNorth North;        //a y ordinate
CReducedLevel Rl;    //a z ordinate height above Sea Level
   
int    CreatePoint(CEast *e, CNorth *n, CReducedLevel *rl);   //return an int for success or failure
int    CreatePoint(C3DPoint *Point, CBearing *Brg, CZenAng *ZenAng, CSlopeDist *SlopeDist);
}
The classes CEast, CNorth, CReducedLevel all have 1 private member which is a double. This may seem over the top, but it is in keeping with the policy of abstracting everything.

I could have had CEast, CNorth, CReducedLevel as doubles inside C3Dpoint, but it will be important to distinguish the different types in more complicated calculations. Eg CEast is an easting in a plane co-ordinate system, whereas CEastGrid is an easting in an ellipsoidal co-ordinate system, so it is important to make the distinction. In Surveying there are a lot of examples like this.

I made CreatePoint an overloaded function not an overloaded constructor, because I might need to extend C3DPoint further.

The class C3Dpoint is also going to implement the following to do with vector algebra: Addition & subtraction of points, scalar multiplication, dot & cross products, magnitude, unit vectors, plus all the other stuff concerning points, lines & planes.

I hope that this makes it clearer about what I am trying to do, and why I am abstracting all these classes.
I'd say typically that abstraction is used when you don't want to distinguish between similar/related entities. That seems to go against what you say you are trying to accomplish.
 
Old 03-19-2011, 10:21 AM   #12
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194
Quote:
Originally Posted by z1p View Post
I'd say the more appropriate way to tackle that issue is with typedefs. Don't promote something to a class just to distinguish its type.

Code:
typedef Cbearing double
typedef CZenAng  double
Wrong on the syntax, but more seriously wrong on the concepts.

typedef does not accomplish what the OP wants.
 
Old 03-19-2011, 11:26 AM   #13
SigTerm
Member
 
Registered: Dec 2009
Distribution: Slackware 12.2
Posts: 379

Rep: Reputation: 234Reputation: 234Reputation: 234
Quote:
Originally Posted by worzel1968 View Post
should all classes in an App derived from some other class so that all the classes are connected?
No. Don't do it unless you have to - you'll simply waste time and achieve nothing.
In my opinion:
A design decision must have a reason for it. If you can think of a reason, decision may be an incorrect one. Program should be kept as simple as possible - introducing a lot of concepts just because you felt like it might make you waste time later. It is very easy to get distracted and start playing around with class hierarchies instead of actually doing something useful for the program.
 
Old 03-19-2011, 05:17 PM   #14
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194Reputation: 1194
If you plan to have classes CEast, CNorth, CReducedLevel as you described, you ought to have basic math on those objects. You want that math to obey correct type rules, but you don't want to write all that code multiple times over. Here is a good method to reduce that code duplication:

I don't have a compiler handy to catch typos etc. at the moment, so I expect this code will have a few errors, but should be close enough to give you the idea even if you see it before I fix it:

I'm also leaving out most of the code, just giving you an idea how it works:

Code:
template <class ActualType, class DataType>
class CWrap {
protected:
   // CWrap<ActualType,DataType> will be used only as a base class
   // of ActualType.  So every object of type CWrap<> is actually an
   // object of type ActualType.
   // A function to implement that fact.
   ActualType& actual() { return *static_cast<ActualType*>(this); }
public:
   void init(DataType const& d) { m_value = d; }
   ActualType& operator+=( ActualType const& x ) {
      m_value += x.m_value;
      return actual(); }
   ActualType operator+( ActualType const& x ) {
      ActualType result( actual() );
      result += x;
      return result; }
private:
   DataType m_value;
};

class CEast : public CWrap<CEast,double> {};
class CNorth : public CWrap<CNorth,double> {};
class CReducedLevel : public CWrap<CReducedLevel,double> {};
If I did that right, I defined only one operator+= and only one operator+, but that provides one of them to each of those three classes and all three are independent. You can add a CEast object to a CEast object, but you can't add a CEast object to a CNorth object etc.

Last edited by johnsfine; 03-19-2011 at 05:29 PM.
 
Old 03-19-2011, 05:19 PM   #15
worzel1968
Member
 
Registered: Mar 2011
Location: Melbourne, Australia
Distribution: fedora17_64, ubuntu
Posts: 44

Original Poster
Rep: Reputation: 2
doubles

Hi John,

The thing is, I am not using ints - everything is double.

Also, I was going to use a library function like modf(x, double *ip) found in math.h. This function returns the fractional part and stores the integral part in in *ip.

Code:
#include <iostream>
#include <cstdlib>
#include <cmath>

using namespace std;

int main(int argc, char *argv[])
{
	double MyDMS = 315.235999999;   //ddd.mmmsssssss format
	double Degrees = 0.0;
	double mmss = 0.0;
	double Minutes = 0.0;
	double Seconds = 0.0;
	double ssss = 0.0;
	double DecDeg = 0.0;
	
	
	mmss = modf(MyDMS, &Degrees); 
	
	ssss = modf(mmss * 100.0, &Minutes);
	Seconds = ssss * 100.0;
	
	DecDeg = Degrees + (Minutes / 60.0) + (Seconds / 3600.0);
	
	cout.setf(ios::fixed);
	cout.precision(10);
	cout.width(15);
	
	cout << "DMS Degrees is " << MyDMS << endl;
	cout << "Degrees is " << Degrees << endl;
	cout << "Minutes " << Minutes << endl;
	cout << "Seconds " << Seconds << endl;
	cout << "Decimal Degrees is " << DecDeg << endl;
		
  return EXIT_SUCCESS;
}
Quote:
DMS Degrees is 315.2359999990
Degrees is 315.0000000000
Minutes 23.0000000000
Seconds 59.9999900000
Decimal Degrees is 315.3999999972
This works for conversion because all the variables are double.

However, the problem is (as we have both mentioned before) when we want to output a string in ddd.mmss format. In this case ss = 60, which is wrong. So this is where I need the logic "If ss == 60, ss = 00, increment mm. if mm ==60, mm = 00, increment ddd. if ddd ==360, ddd=000".

Having said all of that - there is another way, that is to use strings. The input will probably come from a file anyway. The method is to extract ddd, mm, ss.ssss from "ddd.mmsssss", using string functions. The complexity is about the same as using double, because d, dd, ddd are all valid and one has to take care of the variable number decimal places in ss.sssss... It's all basic programming though.

I hope this shows that I have a method that works.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
inherit permissions raghoos666 Linux - General 7 03-10-2011 07:10 AM
Subdomain inherit and override theodm Linux - Server 5 07-18-2008 11:31 AM
How to inherit permissions thejasondean Linux - General 8 08-31-2006 02:18 PM
Samba Won't Inherit Permissions blackrain69 Linux - Software 1 10-13-2004 01:46 AM
Inherit Permissions in Samba mslane Linux - Software 1 10-09-2003 05:37 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 03:27 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration