Using C functions in C++ is not *always* "bad style". However, whenever C++ provides the means to do something also implemented in C, you should prefer the C++ version. A '\0'-terminated character array can lead to serious problems, both regarding security and debugging effort - just write one char to many... 'printf' and 'scanf' have a syntax which is hard to read and to debug, for a function has to output a type it knows nothing about except the 'typename' the programmer specified. This is either redundant (if there is no error) or contains an error, both are Bad Things. operator<< can be defined by the implementer of the type, and c++ automatically calls the right output function.
The same goes for C-style casts, where you can cast away constness without complaint. Or, how do you know if (int) 2.0f is 2 or whatever number a 2.0 as float would be as an int? With static_cast and reinterpret_cast, this becomes quite clear (and you have to know what you are doing when you use const_cast, the only C++-way to get rid of the const specifier)
Also, if you write code, reuse algorithms whenever possible - espectially when they are from the stl. This is something often overlooked by novice programmers. Consider:
1 2 3 4 5 6 7
|
std::vector<std::string> v;
bool is_in_v(std::string s)
{
for(unsigned int i=0; i<v.size(); i++)
if(s==v[i]) return true;
return false;
}
| |
What are the problems or could be done better?
2: 's' should be passed as a const reference. This is (a) faster and (b) cannot lead to an exception, what could happen with an implicit type conversion
4: v.size() is evaluated everey loop cycle (might be optimized away, then again, why rely on it?)
4: i++ has to return the old value of i. ++i would be the better choice, as it avoids an unneccessary temorary object (cannot be optimized away in the general case, since pre- and postincrement might have completely different semantics with user-defined types)
If the author had written
1 2
|
if(std::find(v.begin(), v.end(), "Hello, World!") != v.end())
// "Hello, World!" is in v...
| |
none of these problems would have occurred, it would be easier to read, it is guaranteed to be bug-free and if you change the container type of v this line does not need to be modified.
This might be constructed, yes, but how many of the questions here would not have been asked if people would start to use std::for_each etc. appropriately? Half of them?
So, in essence, I use the term "good style" for code which is efficient, easy to read and maintain and "hard to get wrong" (try to put an error in the second code which does not lead to an compiler error and is not *obviously* wrong - good luck)
Edit
you always get a mix of these [C & C++] when you continue developing an old product. |
Of course, if you don't want to edit the old code. Then again, if it is not self-contained, it might be time for a re-write. And the interface of self-contained C-code can easily be satisfied with the means of C++ without introducing C-style in the new code (that is one of the main reasons why things std::string::c_str() do exist, after all)