How and when to use the getline function to perform calculations?

Viewed 291

I am taking a course on C++ and I have been asked to compute the area of a circle by using getline. I was advised to refrain from using cin unless it is really necessary.

Below is my code..

#include <iostream>
#include <string>

int main()
{
    std::string question ("Enter the radius: ");
    std::string s_radius;
    std::cout << question;

    getline(std::cin, s_radius);

    const double PI = 3.14159;
    double n_radius = std::stod(s_radius);
    double area = PI * n_radius * n_radius;

    std::cout << "The area of the circle = " << area << std::endl;

    return 0;
}

Is it really necessary to go through the process of accepting a string as input and convert it to a numeral to perform the calculation?

2 Answers

Is it really necessary to go through the process of accepting a string as input and convert it to a numeral to perform the calculation?

It isn't strictly necessary but it does enforce good habits. Let's assume you are going to ask for 2 numbers from the users and use something like

std::cout << "Enter first number: ";
std::cin >> first_number;
std::cout << "Enter second number: ";
std::cin >> second_number;

For the first number the user enters 123bob. operator>> is going to start reading it in and once it hits b it is going to stop and store 123 into first_number. Now the stream has bob still in it. When it asks for the second number since bob is still in the stream it will fail and second_number will get set to 0. Then the program will continue on and you will get garbage output because you accepted garbage input.

Now, if you read in as a std:string and then convert to what you want it makes it easier to catch these types of errors. getline(std::cin, some_string); would get all of 123bob out of the input buffer so you don't have to worry about having to clean it up. Then using stoi (or any of the stox functions) it will read the valid value of of it. The functions also have a second parameter which is a pointer to a size_t and if you pass it one it will store the position of the first unconverted character in the string and you can use that to tell if the entire input string is valid or not. So, if you had

std::string input;
std::cout << "Enter some number: ";
std::getline(std::cin, input);
std::size_t pos;
double number = std::stod(input, &pos);
if (pos == input.size())
    std::cout << "valid input\n";
else
    std::cout << "invalid input\n";

Then 1.23bob would cause invalid input to print where 1.23 cause valid input to print. You could even use this in a loop to make sure you get only valid input like

double number;
do
{   
    std::string input;
    std::cout << "Enter some number: ";
    std::getline(std::cin, input);
    std::size_t pos;
    number = std::stod(input, &pos);
    if (pos != input.size())
        std::cout << "invalid input\n";
    else
        break;
} while(true)

TL;DR: If you rely on the user to only input valid input eventually you are going to get burned. Reading in as a string and converting offers a consistent way to ensure that you are only getting good input from your user.

I was advised to refrain from using cin unless it is really necessary.

First of all, there is a misunderstanding, because you are still using std::cin. I will interpret your question as using std::cin::operator >> vs getline(std::cin,...). Next I would rather suggest you the opposite: Use operator>> directly if you can and fall back to getline if you have to.

A very contrived example:

Imagine you have integers stored in a file in this (unnecessarily complex) format,

3123212
^-------- number of digits of the first number
 ^^^----- first number
    ^---- number of digits of the second number
     ^^-- second number

You cannot use a bare std::cin >> to read those numbers. Instead you need to read the whole line and then parse the numbers. However, even for this I would highly recommend to provide an overload for operator>>, something along the line of

struct NumbersReadFromStrangeFormat {
    std::vector<int> numbers;
};

std::isstream& operator>>(std::istream& in,NumbersReadFromStrangeFormat& num) {
    // use getline and parse the numbers from the string
    return in;
}

Such that you can again write:

NumbersReadFromStrangeFormat numbers;
std::cin >> numbers;

Conclusion: Overloading the operator>> is the idomatic way to read something from an input stream. "Using std::cin" or "using getline" are not really alternatives. "Refraining from using std::cin" must be a misunderstanding. If there is no overload of operator>> that already does what you need, you can write your own and nicely hide any getline or other parsing details.

Related