I was taught in school that using cin
/cout
to get and show information to the user in a function was considered bad practice. I was told that all input/output should be done in the main
function.
Is this true and why?
I was taught in school that using cin
/cout
to get and show information to the user in a function was considered bad practice. I was told that all input/output should be done in the main
function.
Is this true and why?
Typical for schools, this is an oversimplification. What was really meant was probably that
Input/Output should be separate from data processing.
This is easily illustrated by an example:
void doStuff() {
std::string input;
std::cout << "Please provide input: ";
std::cin >> input;
for (int i = 0; i < input.size(); i += 2) {
input[i] = ' ';
}
std::cout << input << std::endl;
}
int main() {
doStuff();
}
std::string getInput() {
std::string input;
std::cin >> input;
return input;
}
std::string processData(std::string input) {
for (int i = 0; i < input.size(); i += 2) {
input[i] = ' ';
}
return std::move(input);
}
void printOutput(std::string const& s) {
std::cout << s << std::endl;
}
int main() {
auto input = getInput();
auto output = processData(input);
printOutput(output);
}
This way you can easily mock or test each of the functions separately, and it's much easier to for example add input validation right now.
std::string getInput() {
std::string input;
if (!(std::cin >> input)) {
throw std::runtime_error("Input problem!");
}
if (input.empty()) {
throw std::length_error("Input can't be empty!");
}
return input;
}
As a side note, main could also be written as:
int main () {
printOutput(processData(getInput()));
}
What your teacher is most likely about is to prevent code like
void add(void) {
int a, b;
cin >> a >> b;
cout << (a + b) << endl;
}
Which is often seen from beginner programmers. What is bad about this code is that it's on the one hand lying about what it does (because it doesn't add, it reads, adds and prints) and on the other hand misuses the most important abstraction tool, the function:
A function is intended to solve one task (which may be complex and consist of subtask) and more over - keeping to the mathematical origin of the term - act as a kind of transformation from some given parameters to some result, ideally like in purely functional languages without changing our having any state. (That is no matter when, how often and in what circumstances you call f(a)
, if you once get b
as result you'll get that every time you call f
with a
)
Applying that to the simple example gives
int add(int lhs, int rhs) {
return lhs + rhs;
}
Nonetheless we somehow need to access the real world (which is very stateful), so for things like the following it's OK to have IO in a function:
int askInteger(void) {
int result;
cout << "Please give me an integer!";
cin >> result;
return result;
}
Please note that this is just an relatively stupid example, without any error handling or parametrisation.
To keep closer to the functional style it's advisable to not hard code the source and target of the IO like above, but rather pass them as parameters:
int askInteger(std::istream & from, std::ostream & to) {
int result;
to << "Please give me an integer!";
from >> result;
return result;
}
// called like
askInteger(cin, cout);