I would like to convert a procedural database program that I have created to an object oriented program. In my procedural program I use a struct:

struct shoes{

    char Name[20];
    int Number;
    double ShoeSize;
};

Putting this into a class would I simply use:

class Shoes{

public:

    //Function declerations

private:
    chat Name[20]
    int Number;
    double ShoeSize;

};

Originally I passed the struct between functions, for example:

void view(Shoes *Shoe);

Can I do the same with the class? would this pass all the variables or would I have to separetly pass each variable? if not what is the best way to pass these variables as a group while using classes? would it be normal to use a struct inside a class?

Edited 3 Years Ago by Hey90

The only difference between struct and class in C++ is default visibility. Structures are public by default and classes are private. Other than that they're identical in features and functionality. Use whichever makes more sense to you.

I just have a problem when passing between functions:

class Shoes{

public:

    void Load(Shoes* Shoe);

private:
    char Name[20];
    int Number;
    double ShoeSize;

};

void Shoes::Load(Shoes* Shoe)
{

    //Code

}

int main()
{

    Shoes Shoe;

    Shoe.Load(Shoe);

}

I get an error with line 26 saying 'No viable conversion from Shoes to Shoes *'
Why is this?

A pointer to T and an object of T aren't compatible types. You can convert Shoe into a pointer, but that seems kind of silly since the member function is being called on Shoe...

Shoe.Load(&Shoe);

Edited 3 Years Ago by deceptikon

what is the best way to pass these variables as a group while using classes?

By make the function a member function of the class. In the case of a function like:

void view(Shoes *Shoe);

The easiest way to do this in object-oriented code, is just as so:

class Shoes{
public:

    void view();

private:
    char Name[20]
    int Number;
    double ShoeSize;
};

And when you have an object like:

Shoes my_shoe;

You can simply call the member function like this:

my_shoe.view();

And because view() is a member function, a pointer to the object my_shoe is implicitely passed to the function. Inside the implementation of the view() function, you can access the data members either directly by name, or using the this pointer which is always pointing to the object on which the function was called. In other words, you can define the view() function like this:

void Shoes::view() {
  std::cout << Number << std::endl;
  std::cout << this->ShoeSize << std::endl;
};

In the above, when calling view() with my_shoe.view(), the variable Number refers to my_shoe.Number, and the variable this->ShoeSize equivalently refers to my_shoe.ShoeSize. This is really the whole point of having this so-called object-oriented syntax in C++, having the implicit passing of and reference to an instance of the class within member functions.

Ok thanks for the help. I have simply tried reading a text file with the shoe information in but the output is garbage. I have the following files:

Shoes.h:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#ifndef Project1_Shoes_h
#define Project1_Shoes_h

class Shoes
{    
public:
    //Constructor
    Shoes();

    void View(Shoes* Shoe);  
    void Display(Shoes* Shoe);  

private:
    unsigned long length;
    char Name[10];  
    unsigned int Number;
    double ShoeSize;

};

#endif

Shoes.cpp:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#include "Shoes.h"  

Shoes::Shoes()
{

}

void Shoes::Load(Shoes* Shoe)
{

    fstream input_file;  

    input_file.open("shoe_list.txt");

    // load length information from file
    if(input_file.good())
    {
        input_file >> length;

        for (unsigned long i=0; i<6; i++)
        {
            input_file >> Shoe[i].Name;
            input_file >> Shoe[i].Number;
            input_file >> Shoe[i].ShoeSize;
        }
    }

    input_file.close();
}

void Shoes::View(Shoes* Shoe)
{
    cout << length << endl <<endl;

    for (unsigned long i=0; i<6 ;i++)
    {
        cout << Shoe[i].Name << endl;
        cout << Shoe[i].Number << endl;
        cout << Shoe[i].ShoeSize << endl;
        cout << endl;
    }

}

main.cpp:

#include <iostream>
#include <fstream>
#include <string>

#include "Shoes.h"

using namespace std;

//#define MAX 50

int main()
{
    Shoes Shoe[MAX];

    char input;

    while (1) {
        cout << endl;
        cout << "Menu" << endl;
        cout << "(1) Load" << endl;
        cout << "(2) View" << endl;
        cout << "(3) Exit" << endl;

        cout << "Enter your option: ";
        cin >> UserInput;
        cout << endl; 

        switch(input) {
            case '1':
                Shoe.Load(&Shoe);
                break;
            case '2':
                Shoe.View(&Shoe);
                break;
            case '3':
                return 0;
            default:
                cout << "Invalid entry" << endl << endl;
                break;
        }
    }
    return 0;
}

shoe_list.txt:

6

Nike
2
7.5

Puma
4
6

Nike
4
8.5

Addidas
1
9

Puma
2
10

Reebok
1
11.5

The output result given after loading the file and then viewing it is just a load of garbage in place of how the data should appear. Could anyone help me? Is it to do with the way I am passing the variables?

Edited 3 Years Ago by Hey90

Hey, slow down tenderfoot. You seem to be moving so fast you're stumbling over your own feet.

Inclusion guards are great. I'm glad you know about them. However, they should go at the top of the file so you don't include std or fstream or other files multiple times if you are linking/including multiple files , in your program.

Syntax such as,

const int MAX = 50;

is preferred to

define int MAX 50;

It would also be preferrable for MAX to have some scope other than global, so I'd declare it is main() rather than before it. As posted, MAX has no value whatsoever, as the line declaring MAX is commented out.

I would tend to name the class Shoe rather than Shoes. That way I can use Shoes as the name of a container (in this case an array) of multiple instances of type Shoe. I like to have each type be able to display an instance of itself. I would use functions outside the type declare to accumulate a group of instances of the type and to display any, or all, of the instances belonging to that group of instances.

The file you are using has a value indicating how many instances of type Shoe are in the file. You should use the variable length, instead of literal int 6, when dealing with actual number of instances of type Shoe are actually in the array, for example in the loop used to load array when reading from the file.

Syntax such as,

const int MAX = 50;

is preferred to

define int MAX 50;

It would also be preferrable for MAX to have some scope other than global, so I'd declare it in main() rather than before it.

As posted, MAX has no value whatsoever, as the line declaring MAX is commented out.

The pointer parameter you are using refers to an array of type, not to a single instance of type. Typically, you would also send the size of the array and the actual number of elements in the array.

const int MAX = 50;
T t[MAX]; //declare an array of type T holding, at most, MAX number of instances of type T
int len = 6; //assume there are only 6 instances of type T in t.
void view(T* t, int len); //declaration of a function, outside of the class and global in scope, that displays an array of type T called t which has len instances of type T in it.

Here is an alternate syntax to declare a function called view to display the elements of type T contained in an array of type T called t that could hold MAX elements but actually has only len elements in it at this time.
void view(T t[MAX], int len);

The pointer verssion to decare view() is quicker to type, and would be preferred if t were declared using dynamic memory. The alternate version of view() is explicit in indicating that t is an array of type T, not a pointer to single instance, and that t could hold up to MAX elements, but actually only has len.

Edited 3 Years Ago by Lerner

I put length equal to 6 as the program was printing garbage so it read in something like 345657 into length :-S

Making changes I now get some errors:

error C2228: left of '.Load' must have class/struct/union
          type is 'Shoes [50]'

and:

error C2065: 'length' : undeclared identifier

even though I have declared length in the class Shoes ...

Shoe is an array of 50 shoe objects. Is an array of 50 objects of type shoe the same thing as a single object of type shoe? No.
Can you call a class function of the shoe class on an object that is not an object of type shoe? No.

So would I simply call like this instead:

Shoe[MAX].Load(&Shoe)

I realised after all this I didn't have the text file in the correct place......

Edited 3 Years Ago by Hey90

The only other problem is that it says length is undeclared when I add it as a variable to pass in the functions, which I dont understand as it is declared in the shoes class in Shoes.h :-S does anyone know why this is?

Sure;

Shoes.h:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#ifndef Project1_Shoes_h
#define Project1_Shoes_h

class Shoes
{    
public:
    //Constructor
    Shoes();

    void Enter(Shoes&, unsigned long&);
    void Load(Shoes*, unsigned long&);  
    void View(const Shoes*, unsigned long);  

private:
    unsigned long length;
    char Name[20];  
    unsigned int Number;
    double ShoeSize;

};


#endif

Shoes.cpp

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#include "Shoes.h"  

Shoes::Shoes()
{
   Number = 0;
   ShoeSize = 0.0;
   length = 0;
}

void Shoes::Enter(Shoes& Shoe, unsigned long& length)
{
    cout << "Enter New Shoe" << endl;
}

void Shoes::Load(Shoes* Shoe, unsigned long& length)
{

    fstream input_file;  

    input_file.open("shoe_list.txt");

    if(input_file.good())
    {
        input_file >> length;

        for (unsigned long i=0; i<length; i++)
        {
            input_file >> Shoe[i].Name;
            input_file >> Shoe[i].Number;
            input_file >> Shoe[i].ShoeSize;
        }
    }

    input_file.close();
}

void Shoes::View(const Shoes* Shoe, unsigned long length)
{
    cout << length << endl <<endl;

    for (unsigned long i=0; i<length ;i++)
    {
        cout << Shoe[i].Name << endl;
        cout << Shoe[i].Number << endl;
        cout << Shoe[i].ShoeSize << endl;
        cout << endl;
    }

}

main.cpp

#include <iostream>
#include <fstream>
#include <string>

#include "Shoes.h"

using namespace std;

int main()
{
    const int MAX = 10;
    int i =0;
    Shoes Shoe[MAX];

    char input;

    while (1) {
        // show the menu of options
        cout << endl;
        cout << "Menu" << endl;
        cout << "1. Enter" << endl;
        cout << "2. Load" << endl;
        cout << "3. View" << endl;
        cout << "4. Exit" << endl << endl;

        cout << "Enter your option: ";
        cin >> input;
        cout << endl;

        switch(input) {
            case '1':
                Shoe[i].Enter(Shoe[length]);
                break;
            case '2':
                Shoe[i].Load(Shoe, length);
                break;
            case '3':
                Shoe[i].View(Shoe, length);
                break;
            case '4':
                return 0;
            default:
                cout << "No such choice" << endl << endl;
                break;
        }
    }
    return 0;
}

shoe_list.txt:

6

Nike
2
7.5

Puma
4
6

Nike
4
8.5

Addidas
1
9

Puma
2
10

Reebok
1
11.5

Edited 3 Years Ago by Hey90

I dont think you are implementing this correctly. You are trying to use class functions to take the place of global functiuons you used to have. If you dont change anything then it is not going to work. Take load() for example. If you want to load an array a shoes from a file you should have that as a global function since one shoe should not be responsible for populating an array of other shoes.

Im not quite sure what you mean. Do you mind explain a bit further?

Something like this

// shoe should be a single object
class Shoe
{
public:
    Shoe() : name(""), shoeSize(0), number(0) {}
    Shoe(string _name, double _size, int _number) : name(_name), shoeSize(_size), number(_number) {}

    // getters
    string getName() const { return name; }
    double getSize() const { return shoeSize; }
    int getNumber() const { reutrn number; }

    //setters
    void setName(string _name) { name = _name; }
    void setSize(double _size) { shoeSize = _size; }
    void setNumber(int _number) { number = _number; }

private:
    string name;
    double shoeSize;
    int number;
};


// now we make a shoe list
class ShoeList
{
public:
    ShoeList()

    bool Load(string filename)  // use bool to signle if operation was succesful
    {
        // read in shoes and add them to the vector
    }
    void Display() const
    {
        for(int i = 0 i < shoes.size; i++)
            cout << shoes[i].display() << endl
    }
private:
    vector<Shoe> shoes
};

This way you have a ShoeList that manages all of the seperate shoes and the Shoe class just takes care of itself. You could also just do this with functions but if you are changing it to OOP then this should be the way you go.

What are the advantages of using a vector over an array?
I don't really understand setters and getters...I haven't been taught to use this. Im not sure what exactly they are doing but I shall read up on them.

Edited 3 Years Ago by Hey90

I like a vector because it is scalable so you don’t need to know what the size is ahead of time. It really is a matter of preference but it also keeps with the OOP side of the code. Setters and getters are used to access the variables of the class. Since the variables should be private the programmer cannot access them directly so you need an in-between. This also allows for greater control of what goes on since you can add code to make sure the value being provided is valid.

Right ok that makes sense. One last question for now. You put:

Shoe() : name(""), shoeSize(0), number(0) {}

Would putting:

name(""), shoeSize(0), number(0)

inside:

{}

instead, be the same?

Not really. you have two ways to inilizie class variables. you can do

Shoe() : name(""), shoeSize(0), number(0) {}

or you can do

Shoe()
{
    name = "";
    shoeSize = 0;
    number = 0;
}

Here is a nice little article on the matter

Edited 3 Years Ago by NathanOliver

Ok. Can you then set a max on the number of shoe objects then like before?
Also loading the data from the file should that be done by overloading the stream extraction operator?

I really appreciate your help so far.

Ok. Can you then set a max on the number of shoe objects then like before?

No you would have to either use an array of fixed length or make your own vector.

Also loading the data from the file should that be done by overloading the stream extraction operator?

You can defenitly do that. That will make the syntax look much cleaner. You could also get the values from the file and use the setters to set the data to the shoe object. If I was coding it I would overide the stream operators. You could also change the file to a binary file and use serialization.

Is there no way of using the shoe characteristics that have already been set in class shoe?

Could I not just use Shoe Shoes[50];

Edited 3 Years Ago by Hey90

But then how would I call the function in main.cpp because load or display are not part of shoe. I tried using:

Shoe_List[i].load(); 

but this gave the errors:

missing ';' before ']'...
'i' attribute not found

I then tried passing the Shoe object:

void load(Shoe* shoes);

Shoe_List[i].load(&shoe);

but I still get the same errors....

ShoeList will have a member that is a shoe array. Then when you want to load the ShoeList you would call load from the shoe list and pass it the file name or ask for the file name from the user.

class ShoeList
{
public:
    ShoeList() { shoes = NULL; }

    void Load()
    {
        int size;
        ifstream fin(some file name);
        fin >> size;

        shoes = new Shoe[size];

        // read in file here into the shoes array
    }

private:
    Shoe * shoes
};

// in main
ShoeList shoes;
shoes.Load();  
// ^ this will read the file and store all of the shoes in shoes.  
//   then all you need is to add access members to allow you to use the shoes.

I was reading the discussion here and wondering why did you give up the stl vector idea?

Ok. Can you then set a max on the number of shoe objects then like before?

Yes, you can define a maximum of elements in stl vector, even if stl doesn't provide that. All the time you can provide a constant and limit your iterator to the specified value of the constant.

STL containers/iterators are proved to work efficiently and there is no point to reinvent the wheel, wouldn't you agree?

If you prefer not to use stl vectors, there are other options out there, depending on the needs. If you really want to rely on arrays, I would recommend arrays from boost library (http://www.boost.org/doc/libs/1_46_1/doc/html/array.html).

If you want just brain teaser, yes, you can design your own dedicated containers and iterators, but they will not help you further because you will need to modify them in order to be reusable.

To access the shoes would this simply be done using get. So Shoe::get();
Doesn't having these two classes also go against the idea of encapsulation?

Also putting:

shoes = new Shoe[size];

That is setting a dynamic array right? I would like a max size of 50. I tried:

shoes = Shoe[50];

but it said that it was an illegal use of this type as an expression.

That aside, as a test I set the variables in Shoe to public so I could access them. I loaded the data using (where length was declared in Shoe_List):

fstream fin; 

    fin.open("database.txt", ios::in);

    if(fin.good()) 
    {
        fin >> size;

        shoes = new Shoe[size];

        for (int i=0; i<size; i++) 
        {
            fin >> shoes[i].Name;
            fin >> shoes[i].ShoeSize;
            fin >> shoes[i].Number;
        }
    }

    fin.close();

When I then printed out the data I got the correct size (6 in this case) but only the first shoe was printed correctly and the other 5 printed as 0's :-S

This article has been dead for over six months. Start a new discussion instead.