Sign in to follow this  

Class problem [C++]

This topic is 3292 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

Ok, so I'm trying to get this silly little class to work, I'm not really sure about classes yet and as far as I know it should work, but ofcourse it won't. The class is supposed to store information about height, width and length of an object and the function inside the class should produce the cubic metres of the object. Getting errors on height, width and length being undeclared aswell as cubicmetre so the error is there, but I've already declared those and tried to declare them again in several places, but nothing seems to help. As for what comes to google helping me on this I've had zero luck so far. Here's the code so far, feel free to laugh, I'm just learning this silly thing. #include <iostream> int main(void) { class cube { int length, width, height; int cubicmetre(int length,int width,int height) { int CubicMetre; CubicMetre = length * width * height; return(CubicMetre); } }; int cube; std::cout << "Enter height of the cube: " ; std::cin >> cube.height; std::cout << "Enter width of the cube: " ; std::cin >> cube.width; std::cout << "Enter length of the cube: " ; std::cin >> cube.length; std::cout << "Your cube is : " << cube.cubicmetre(length,width,height) << " Cubic metres" ; return 0; }

Share this post


Link to post
Share on other sites
You haven't declared the visibility of cubes members. Since the visibility is private as default, you won't be able to access cubes members outside of the class. Declare them public instead.

But then you also declare the variable cube as an int, but what you really wanted was an instance of cube. Here is a quick tutorial about classes, which should give you a better understanding about how classes work.

Share this post


Link to post
Share on other sites
Thank you for pointing me in the right direction, the link was very helpfull. somehow I've managed to ignore the very basic idea of classes and tried to create an int called cube rather than cube called cube, well I called it MyCube, but I got the idea anyhow.

Share this post


Link to post
Share on other sites
Hi mate,

as Perost says you can't access the class members directly since they are private members. If you want (and you generally want) class's members to be private, you have to provide functions to access(the get and set methods) for your members.

They are generally private since this is one of the many good features of class concept. Suppose you give your class to a friend: you generally don't want let him khnow the way the class is implemented. He doesn't khnow that your class has 3 int fields, but he khnows the way to access 'em.

For example, you can even use, instead of 3 ints, an array of int (int array[3]). For your friend won't change since he will just use the functions you provided to manipulate data in your class.

Have a nice day
Bro

Share this post


Link to post
Share on other sites
Bro: I think your choice of 'friend' as a descriptor is a little confusing because i don't think you are actually referring to a friend class, which of course would not need to use accessor functions; as all of classes members would be public to a friend class anyway regardless of access specification.

If you were referring to friend classes thats cool just wanted to clarify. OP check out friend classes to know the whole story :)

Edit : I think you were refering to a real life friend... :s ... I think i should get out more :p

Anyway here is some code.


class A
{
friend class myFriend;
public:
void Set( int i, int j) { I = i; J = j; }
private:
int I, J;
}

class MyFriend
{
public:
MyFriend() { I = 2; J = 2; }
void Intrude ( A &a ) { a.I = K; a.J = L; }
private:
int K, L;
}

main ()
{
A MyA;
MyA.Set(1, 1); // MyA now as 1, 1
MyFriend AIntruder; // Who have we here
AIntruder.Intrude ( MyA ); // MyA feels violated an now has 2, 2 :)
}




Share this post


Link to post
Share on other sites
Some points and comments:

1) If a function doesn't take any arguments then, unlike C, you don't need to explicitly define void in the parameter list, so: int main() works just fine.

2) The main function is different to all other C++ functions that return values, in that you don't need to explicitly return any value at all. The compiler will automatically add an equivalent to return 0;

3) Everything doesn't have to be crammed into the main function and there's no real reason to declare your class within main itself, so may as well move it out - if anything to demonstrate that it's perfectly fine to [smile].

4) A class has private members by default so you cannot access the variables or member function from outside the class. To fix that use the public scope modifier.

5) When you make the call cube.cubicmetre(length,width,height) none of the variables (length, width and height) have been declared in an accessible scope, the compiler will complain. I assume you meant something like: cube.cubicmetre(cube.length,cube.width,cube.height).

6) I think you know this now, but when you created your cube variable you made its type an integer rather than of your cube class. You also cannot name the class and the variable the same thing (for obvious reasons). Something like naming the class cube and the variable mycube should work just fine.

Making those amendments get you a fully working program, here:

#include <iostream>

class cube {
public:
int length, width, height;

int cubicmetre(int length,int width,int height) {
int CubicMetre;
CubicMetre = length * width * height;
return(CubicMetre);
}
};

int main()
{
cube mycube;

std::cout << "Enter height of the cube: " ;
std::cin >> mycube.height;
std::cout << "Enter width of the cube: " ;
std::cin >> mycube.width;
std::cout << "Enter length of the cube: " ;
std::cin >> mycube.length;
std::cout << "Your cube is : " << mycube.cubicmetre(mycube.length, mycube.width, mycube.height) << " Cubic metres" ;
}




So now further comments to improve on what you have:

7) Your cubmicmetre function is a bit complicated in its implementation, you can simply do this:
int cubicmetre(int length,int width,int height)
{
return length * width * height;
}

8)
Quote:
Original post by TheHeck
The class is supposed to store information about height, width and length of an object and the function inside the class should produce the cubic metres of the object.


I thought so. Essentially you're trying to model a three-dimensional cube primitive. You've made some good progress but your cubicmetre function doesn't really make total sense given the context. What you've really written is more along the lines of a "power of 3 calculator" rather than a "cube". Getting from the former to the latter is easy, just make your cubicmetre function into a volume function which returns the volume (in cublic meters) of the cube:
int volume()
{
return length * width * height;
}
Now it's looking more like a cube class [smile]

9) Constructors! Suppose you call volume before assigning all three of length, width, and height - it would crash. You should provide a constructor which ensures the values are initialised right from go:
cube(int length, int width, int height)
: length(length)
, width(width)
, height(height)
{ }
The actual body of the constructor is empty because I've used an initialisation list to initialise the values.

You supply the values when you construct the object, like so:

cube mycube(l, w, h);

10) Encapsulation! This was touched on by broady. You've implemented your cube class using 3 integers to define the dimensions. That is just one of several possible sensible routes you could have gone and one of many possible solutions in total. Generally you do not want the user of your class to interact with the internals of your class, either because:

A) You might change the way you've implemented it which would break their code, or
B) Mucking about with values in your class could break some invariant that your class is meant to maintain.

Your cube class does not maintain any invariants so it is only susceptible to point A. Either way you want some encapsulation. This involves funnelling the access to the variable though member functions.

We can decide now that the user of your cube class will require both read and write access to those variables at some point.

The simplest interface for getting the dimensions is just three functions that return the values of the variables. I wish to call them just length(), width() and height() but that will clash with your variable names. To avoid the clash I'd prefer to rename all three variables to just: l, w and h, this preserves the ideal interface I want, you could also add an underscore to the variable names at the end to differentiate them from the function names if you'd rather. Anyway:
int length() { return l; }
int width () { return w; }
int height() { return h; }

The simplest interface for setting the dimensions is this:
void resize(int length, int width, int height)
{
l = length;
w = width;
h = height;
}
If you find the need for it then you could also add functions to set the dimensions individually too, however I imagine that a lot of the time if you want to change one of the dimensions then you'll be wanting to change the others too. We won't actually need any of these setter functions once the upcoming changes are added anyway.

Now with those functions in place you can make the variables themselves private.

Here is the class as it stands currently, notice that now the variables are private I've moved them to the bottom to keep the most-public stuff closer to the top:

class cube
{
public:
cube(int length, int width, int height)
: h(length)
, w(width)
, h(height)
{ }

void resize(int length, int width, int height)
{
l = length;
w = width;
h = height;
}

int length() { return l; }
int width () { return w; }
int height() { return h; }

int volume()
{
return l * w * h;
}

private:
int l, w, h;
};




11) Now we have a problem. Previously you were reading the values from the console directly into the cube's member variables. This is no longer possible. So what to do?
It turns out there are several ways to solve this issue, you could:

A) Construct the cube with each dimension as zero. Read in three integers and then call resize passing in those integers. This is in ugly way of doing it.

B) Read in three integers and *then* construct the cube passing those integers to the constructor, e.g:

int l, w, h;

/* use std::cin here to read in the three values */

// now construct the cube
cube mycube(l, w, h);


This method is acceptable and fairly easy if you wish to do it.

C) Write a function that will prompt the user with a message and return a value, call this function three times once for each constructor parameter. This is the one I'd prefer.

Here's the function that prompts for an integer and reads it back:
int readint(const char * message)
{
int value;
std::cout << message;
std::cin >> value;
return value;
}
Now here is how we construct a cube using values given by the user:
cube mycube(
readint("Enter height of the cube: "),
readint("Enter width of the cube: "),
readint("Enter length of the cube: ") );


12) Improving encapsulation. There is a reasonably modern train of thought which says that if a function doesn't need to be a member of a class (i.e. it could be reasonably implemented in terms of existing member functions) then it should become a free-function, not a member function.

We can observe that the function volume() could trivially be implemented in terms of the length(), width() and height() functions.

So we'll move it out of the class altogether and implement it using those functions. The volume function will now need to receive an instance of a cube as a parameter too so instead of calling mycube.volume() you will instead use volume(mycube). Here's the new volume function:
int volume(cube c)
{
return c.length() * c.width() * c.height();
}

13) One issue I have glossed over is error checking - try entering a non-integer as one of values and see what happens.

I won't explain how to handle this but I will give you some code that will do it for you which you can reuse from project to project, feel free to enquire about it if you want to. In order to use them you will need to #include both <string> and <sstream>.

Reusable code:

// reads a value from a stream, returns true if successful
template < typename T >
bool read_value(std::istream & is, T & out)
{
std::string line;
std::getline(is, line);
return (std::stringstream(line) >> out) != 0;
}

// repeatedly prompts the console for a value until a valid one is given
template < typename T >
void prompt_value(const char * promptmsg, const char * errormsg, T & out)
{
std::cout << promptmsg;
while (!read_value(std::cin, out)) {
std::cout << errormsg << "\n";
std::cout << promptmsg;
}
}




To use that code in an program you simply write a wrapper function, here's the one for your program to replace, and be used instead of, the previous readint function:
int read_dimension(std::string side)
{
int val;
prompt_value(("Enter " + side + " of the cube: ").c_str(),
"Invalid value - Must be an integer.",
val);
return val;
}
Note that when you call this function you just supply a string stating the side to enter rather than the full "Enter length/width/height of the cube" string.


Accumulating all the aforementioned changes together gets us to the end of this long post, notice that the main function is now nice and simple:

#include <iostream>
#include <string>
#include <sstream>

class cube
{
public:
cube(int length, int width, int height)
: l(length)
, w(width)
, h(height)
{ }

void resize(int length, int width, int height)
{
l = length;
w = width;
h = height;
}

int length() { return l; }
int width () { return w; }
int height() { return h; }

private:
int l, w, h;
};

int volume(cube c)
{
return c.length() * c.width() * c.height();
}

// reads a value from a stream, returns true if successful
template < typename T >
bool read_value(std::istream & is, T & out)
{
std::string line;
std::getline(is, line);
return (std::stringstream(line) >> out) != 0;
}

// repeatedly prompts the console for a value until a valid one is given
template < typename T >
void prompt_value(const char * promptmsg, const char * errormsg, T & out)
{
std::cout << promptmsg;
while (!read_value(std::cin, out)) {
std::cout << errormsg << "\n";
std::cout << promptmsg;
}
}

int read_dimension(std::string side)
{
int val;
prompt_value(("Enter " + side + " of the cube: ").c_str(),
"Invalid value - Must be an integer.",
val);
return val;
}

int main()
{
cube mycube(
read_dimension("height"),
read_dimension("width"),
read_dimension("length") );

std::cout << "Your cube is : " << volume(mycube) << " Cubic metres" ;
}



The only thing your program doesn't do now is handle non-integer dimension types. I won't go into that but you can look into 'template classes' (to support other types) and 'typedefs' (so other functions can tell what the type is, rather like value_type from a std::vector).

So now I'll stop typing.

[Edited by - dmatter on December 6, 2008 2:37:24 PM]

Share this post


Link to post
Share on other sites
Hey,

guthur with friend i mean a user who has/wants to use your Cube class :D I could be more accurate but, you are right, a starter might result confused!

bye
bro

edit: ah you spotted it yourselve :D

Share this post


Link to post
Share on other sites

This topic is 3292 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this