2
\$\begingroup\$

I am pretty new to coding and I wanted to know how to use header and source files to organize a C++ project. I could not yet find out how I should use header and source files to still have my project work - I first thought I could just include .cpp files but I heard it is considered bad practice. I want to learn this from the beginning so it won't be a problem later.

So this is my project at the moment (pretty simple so far): (I also eventually want to be able to output certain variables into a textfile and if possible input them everytime I start the program or when I need them.) (Also open to any suggestions if I have any "bad code" or already some bad practices.)

#include "stdafx.h"
#include "iostream"
#include "string"
#include "sstream"

using namespace std;


// BMICalculator function
void BMICalculator()
{
    cout << "Welcome to the BMI Calculator!" << endl;

    float weightInKilograms;
    float heightInCentimeters; // Should be int but is float so heightInMeters is correctly calculated
    float heightInMeters;
    float BMI;

    cout << endl;

    // User has to input their weight in kilograms (float)
    {
        cout << "Input your weight in kilograms: ";
        cin >> weightInKilograms;

        // Loops as long as cin fails (if input is NOT of type float)
        while (cin.fail())
        {
            cout << "Error: Enter a number!" << endl;
            cin.clear();
            cin.ignore(256, '\n');
            cin >> weightInKilograms;
        }
    }

    // User has to input their height in centimeters (float)
    {
        cout << "Input your height in centimeters: ";
        cin >> heightInCentimeters;

        // Loops as long as cin fails (if input is NOT of type float)
        while (cin.fail())
        {
            cout << "Error: Enter a number!" << endl;
            cin.clear();
            cin.ignore(256, '\n');
            cin >> heightInCentimeters;
        }
    }

    // Converts heightInCentimeters to heightInMeters for calculating the BMI
    heightInMeters = heightInCentimeters / 100;

    // BMI formula
    BMI = weightInKilograms / (heightInMeters * heightInMeters);

    cout << "Your BMI (Body Mass Index) is " << BMI << " kilograms per cubic meter." << endl;
}

// BMRCalculator function
void BMRCalculator()
{
    cout << "Welcome to the BMR Calculator!" << endl << endl;

    int ageInYears;
    char gender;
    float weightInKilograms;
    float heightInCentimeters;
    float BMR;

    // User has to input their age in years (int)
    {
        cout << "Input your age in years: ";
        cin >> ageInYears;

        // Loops as long as cin fails (if input is NOT of type int)
        while (cin.fail())
        {
            cout << "Error: Enter a whole number!" << endl;
            cin.clear();
            cin.ignore(256, '\n');
            cin >> ageInYears;
        }
    }

    // User has to input their gender (male M or female F) (char)

    {
        // If the input (char) isn't either 'M' or 'F' repeat until it is
        do {
            cout << "Input your gender (male 'M' or female 'F'): ";
            cin.ignore();
            cin >> gender;
        } while (gender != 'M' && gender != 'F');
    }



    // User has to input their weight in kilograms (float)
    {
        cout << "Input your weight in kilograms: ";
        cin >> weightInKilograms;

        // Loops as long as cin fails (if input is NOT of type float)
        while (cin.fail())
        {
            cout << "Error: Enter a number!" << endl;
            cin.clear();
            cin.ignore(256, '\n');
            cin >> weightInKilograms;
        }
    }

    // User has to input their height in centimeters (float)
    {
        cout << "Input your height in centimeters: ";
        cin >> heightInCentimeters;

        // Loops as long as cin fails (if input is NOT of type float)
        while (cin.fail())
        {
            cout << "Error: Enter an integer number!" << endl;
            cin.clear();
            cin.ignore(256, '\n');
            cin >> heightInCentimeters;
        }
    }

    cout << endl;


    if (gender = 'M')
    {
        BMR = 10 * weightInKilograms + 6.25 * heightInCentimeters - 5 * ageInYears + 5;
    }

    else if (gender = 'F')
    {
        BMR = 10 * weightInKilograms + 6.25 * heightInCentimeters - 5 * ageInYears - 161;
    }

    cout << "Your BMR (Basal Metabolic Rate) is: " << BMR << endl;
    cout << "This is how much calories your body needs to maintain itsself." << endl;

}

// ToolSelection function
void ToolSelection()
{
    // User input to open a certain function
    string commandString;

    // All available tools in main - update everytime a new tool is added
    {
        cout << "To calculate your BMI (Body Mass Index) type 'BMI'" << endl;
        cout << "To calculate your BMR (Basal Metabolic Rate) type 'BMR'" << endl;
        cout << "To exit the program type 'EXIT'" << endl;
    }

    // Gets the user input (string)
    getline(cin, commandString);

    do {
        // If user input is "BMI" open the BMICalculator
        if (commandString == "BMI")
        {
            BMICalculator();
        }

        // If user input is "BMR" open the BMRCalculator
        else if (commandString == "BMR")
        {
            BMRCalculator();
        }
        // If no valid tool is called let the user choose again
        else
        {
            cout << "Enter a valid input for a tool you want to use!" << endl;
            cin.clear();
            getline(cin, commandString);
        }
    } while (commandString != "BMI" || commandString != "BMR"); // Update everytime a new possible commandString is introduced
}

// Gets called at the start - all other functions have to be written before it so the compiler knows of them!
int main()
{
    cout << "Welcome to my first program - Romans Fitness Tools!" << endl << endl;

    ToolSelection();

    return 0;
}
\$\endgroup\$
2
  • \$\begingroup\$ @RomanRoth - Also as an aside, Mifflin St. Jeor is a good equation, I like Katch-McCardle, but for a truly robust calculator you could easily incorporate several and give them an average. \$\endgroup\$
    – JohnP
    Sep 6 '16 at 21:31
  • 1
    \$\begingroup\$ you mean if (gender == 'M') not if (gender = 'M') \$\endgroup\$
    – pm100
    Sep 7 '16 at 17:17
6
\$\begingroup\$

using namespace std;

using namespace std;

You may have already seen Why is “using namespace std;” considered bad practice?

Use functions not blocks

    // User has to input their weight in kilograms (float)
    {
        cout << "Input your weight in kilograms: ";
        cin >> weightInKilograms;

        // Loops as long as cin fails (if input is NOT of type float)
        while (cin.fail())
        {
            cout << "Error: Enter a number!" << endl;
            cin.clear();
            cin.ignore(256, '\n');
            cin >> weightInKilograms;
        }
    }

If you're going this far, you might as well go ahead and create a function for this. E.g.

    std::cout << "Input your weight in kilograms: ";
    float weightInKilograms = inputFloat();

Note that this has the side effect of declaring and initializing the variable at the same time, which is generally preferable in C++ (and other languages that allow it). The general rule is to declare a variable as close to first use as possible.

This is easy to follow and eliminates the need for a comment. But of course it requires

    float inputFloat()
    {
        float value;
        std::cin >> value;

        // Loops as long as cin fails (if input is NOT of type float)
        while (std::cin.fail())
        {
            std::cout << "Error: Enter a number!" << std::endl;
            std::cin.clear();
            std::cin.ignore(256, '\n');
            std::cin >> value;
        }

        return value;
    }

And now we can reuse this function.

    // User has to input their height in centimeters (float)
    {
        cout << "Input your height in centimeters: ";
        cin >> heightInCentimeters;

        // Loops as long as cin fails (if input is NOT of type float)
        while (cin.fail())
        {
            cout << "Error: Enter a number!" << endl;
            cin.clear();
            cin.ignore(256, '\n');
            cin >> heightInCentimeters;
        }
    }

Becomes

    std::cout << "Input your height in centimeters: ";
    float heightInCentimeters = inputFloat();

And

    {
        // If the input (char) isn't either 'M' or 'F' repeat until it is
        do {
            cout << "Input your gender (male 'M' or female 'F'): ";
            cin.ignore();
            cin >> gender;
        } while (gender != 'M' && gender != 'F');
    }

could become

    char gender = inputGender();

with

    char inputGender()
    {
        char gender = 0;

        // If the input (char) isn't either 'M' or 'F' repeat until it is
        do {
            std::cout << "Input your gender (male 'M' or female 'F'): ";
            std::cin.ignore();
            std::cin >> gender;
        } while (gender != 'M' && gender != 'F');

        return gender;
    }

Don't mislead me

        if (commandString == "BMI")
        {
            BMICalculator();
        }

        // If user input is "BMR" open the BMRCalculator
        else if (commandString == "BMR")
        {

So I'm reading this if statement. I get to a }. I think it's over. But in reality, somewhere down the page, there's an else waiting to start things all over again. But what if we always write it like this:

        if (commandString == "BMI")
        {
            BMICalculator();
        }
        else if (commandString == "BMR")
        {
            // If user input is "BMR" open the BMRCalculator

Now I know immediately that the } isn't ending the control structure, just the first then block.

Some will point out that this is a weakness in C-style languages. The same symbol closes the then block and terminates the statement. So you have to look forward to find out if you're done or not. In other languages, this is clearer.

if 
    condition
then
    statements
else 
    statements
end

That's true, but note that that kind of setup essentially enforces a similar standard. It requires that the else be there to close the then block.

A side benefit of this is that if you get used to putting the opening and closing braces on their own lines, you start a new job and write code that way, and then they tell you that they prefer everything on the same line, it's easy to turn the above into

        if (commandString == "BMI") {
            BMICalculator();
        } else if (commandString == "BMR") {
            // If user input is "BMR" open the BMRCalculator

You can even do a search and replace. Some IDEs have functions for exactly this. But if there are comments in the middle, it's going to be more complicated.

Don't comment the obvious

And the truth is that you don't really need that comment at all. The code already says this. Comments should be used to say things that the code doesn't, not simply to state what the code does.

For example, you don't comment

    if (gender = 'M')
    {
        BMR = 10 * weightInKilograms + 6.25 * heightInCentimeters - 5 * ageInYears + 5;
    }

    else if (gender = 'F')
    {
        BMR = 10 * weightInKilograms + 6.25 * heightInCentimeters - 5 * ageInYears - 161;
    }

But you probably should. Why are the male and female BMR formulas different? On what do you base this? Something like

    // BMR formulas taken from book/paper Blah written by Blah
    if ('M' == gender)
    {
        BMR = 10 * weightInKilograms + 6.25 * heightInCentimeters - 5 * ageInYears + 5;
    }
    else if ('F' == gender)
    {
        BMR = 10 * weightInKilograms + 6.25 * heightInCentimeters - 5 * ageInYears - 161;
    }

Blah of course should be replaced with real names.

I switched the order of the constant value and the variable in the comparison. The reason is that you weren't doing a comparison. You were doing an assignment. If you made that mistake with this order, the compiler would have given you an error. A side effect of that is that you would always have used the first BMR formula, as 'M' evaluates as true in C++.

Functions should have verb names

void ToolSelection()

Functions do things. So name them as verbs. This would be better as selectTool.

Classes, objects, and variables are things. Use the noun names for them. For example,

    string commandString;

Could be

    std::string toolSelection;

As a general rule, everything starts with a lower case letter except types (classes which start with an upper case letter) and constants (which are all upper case).

Obviously the most important thing is to be consistent, but almost always you will be complying with someone else's rules in the real world. It's very rare for you to be the one setting the naming convention. So use your initiative now to pick someone else's style and comply with that. Don't try to invent your own naming convention, as it only leads to frustration when you get a job and can't use it.

And of course, the more standard your choice, the easier it is to find developers who will use it. So even on your own projects that you control, a standard choice can be better for the project.

Breaking into separate files

As stands, this isn't really long enough to break into separate files. But for future expansion, consider having files (possibly as classes) for saving/loading (file operations); user-entered statistics (height, weight, age, gender, etc.); calculated statistics (BMI, BMR, etc.). For how, see

The key word for that search was linking. To give you some additional search terms: you include header files which have function prototypes and class definitions. You link cpp files with function definitions, often by compiling based on a makefile.

Compiling is off-topic on Code Review. That portion of your question would make more sense on Stack Overflow or Programmers.SE.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.