It is not an all or nothing approach. It's fine to write code that looks and feels mostly like C, but still takes advantage of a few nice C++ features.
One of my largest gripes making the switch, is the OO paradigm. I get it, I understand it, and I really want to love it. But the theory of it vs. the implementations that I've seen, ugh. And the number of ways you can initialize variables etc... just makes no sense to me. It's like they just keep adding new ways to do things, for no other reason than because they can. What's wrong with only have one way to do simple things like that?
I don't know, maybe it's just me but I strive to keep my code as simple, concise as I possibly can. I have enough complexity to deal with solving problems than having to wrestle with my language on top of it. Just to note, I'm strictly talking about having to use other people's code merged with my own. If it was solely me writing, I'd just use C++ for some of the nice things built in and toss classes and all that into the fire. YMMV.
We have default constructors, copy construction, move construction, braced initialisation and std::initializer_list.
For my part I am working at work on a C++2003 codebase and although I can initialise my C-style array with braced initialisers, I would love to be able to initialise my vector in the same way, but I cannot as it is C++2003. eg.
int myArray[] = { 0, 1, 2, 3}; vector<int> myVector = {0, 1, 2, 3}; // This is impossible in C++03
It may be that the OO implementations you have seen are bad because they don't understand encapsulation or haven't designed their classes from the outside in (i.e., design them interfaces first so that they are expressed from the simple point of view from the vocabulary of the user, not from you who designed the class).
I have worked with horrible C++ that had a hierarchy of inheritance yet failed to understand the point of virtual functions so would cast to child elements from a base class to decide which function to call on the child object, or would have functions in the base class that would dynamic_cast to every possible type of possible child to work out what to do. This was truly horrible. (The correct solution is to use a virtual or pure virtual function on the base and implement this in the children so that the right function gets called). This was within an apparently "object-oriented" system, so if this is your sort of experience I can understand why you would detest it.
The other horrible thing is writing C++ like it is C - pointers everywhere, no understanding or use of RAII, C-style casts everywhere (do you really know better than the compiler what a type is???), writing a million functions to do the same thing instead of a single template function etc. etc. And no const correctness, no use of STL algorithms, putting everything in a C array instead of using the correct STL container for the job, etc. etc. the list is endless
int x = 0; // makes sense
int x(0); // sure
int x{0}; // braces?I'm almost positive that isn't the case. Need to find my copy of Effective Modern C++ and get back to you. It's been a while
(Optimizations can change things, but per the spec, they are not the same)
the first creates a new variable with a default value and then copies 0 into it. (This is trivial with an int, but not so with a more complex type)
the second case creates x using the copy constructor
the third uses an initializer list, and works similarly to #2
No, no default constructor is invoked. In this specific example, until C++17, a temporary int object is created then x is copy constructed from it [1]. The compiler is explicitly allowed to omit the temporary+copy and directly construct from the parameter as per int x(0), but the constructor must be non-explicit.
From c++-17 on this is actually required and additionally a copy constructor is not required to exist. In practice is equivalent to #2 except for the non-explicit requirement.
Pedantic, I know, but as long as we are trying to clarify the rules is better to be clear.
[1] note: is different from default initialize then assign.
C++ supports object-oriented programming, but OO is not intrinsic to the language. OO is just one of the many tools. You can write good functional and procedural C++. If you try to shoehorn every problem into the OO model, you're probably doing it wrong.
From the Bjarne Stroustrup himself:
http://www.stroustrup.com/oopsla.pdf
and again here:
https://isocpp.org/blog/2014/12/myths-1
"C++ supports OOP and other programming styles, but is deliberately not limited to any narrow view of “Object Oriented.” It supports a synthesis of programming techniques including object-oriented and generic programming."
Once a C program gets large enough, it seems to become rather "OO" naturally: there's going to be data grouped into structures, and code that operates on that data. That's what classes and methods are useful for. But you don't have to obsess over "forcing" your design to be OO, i.e. things like debating between A.foo(B) or B.foo(A), because sometimes foo(A,B) or even foo(A,B,C) is the answer.
I agree completely with the complexity argument --- and I'll add that hidden ("encapsulated", "abstracted", whatever you want to call it) complexity is still complexity that can get in the way of debugging and efficiency both in terms of machine and programmer time. The latter point is something that a lot of "modern C++" seems to miss, finding more complex ways to do simple things, which look simpler on the surface but are actually far more complex in total.
Very well put. One of my current projects started in plain C but gradually evolved to the point that it just "became" a C++ project. Once you write the same code a few times in a few different places, it's blindingly obvious what would be would be less painful if represented as objects. And so on with other language constructs (inheritance, templates, etc.).
Precise control over those things is a feature in C(++).
If you weren't unfortunately forced to use C++, a principle of Python is to (try to) have one obvious way of doing things. https://www.python.org/dev/peps/pep-0020/
2008 revival in JS is not because of this book but from node.js, npm, jQuery and browsers becoming more capable. There where other factors like that fact that there is no alternative to JS on client side and HTML was lagging behind before HTML5. Both Java Applets an Flash become abandomware leaving no alternatives.
There is Rust, there should be stronger push towards this language. C++ is like PHP even with heroic efforts cannot be fully fixed. There is too much cruft, arcane syntax, platforms compatibility issues and finally lack module system.
Companies are quite happy to use more productive, safer, languages like the ones on top of JVM and CLR, with C++'s role being left to infrastructure code.
The OSes and tooling from Apple, Google and Microsoft are good examples of it.
C++ is there on the lower levels, for hardware support, low level graphics, language runtimes but everything else ends up in Objective-C, Swift, Java, VB.NET, C# and F#.
From all those OSes, UWP is the only one where C++ enjoys parity with the remaining languages and apparently isn't that much used.
Even Microsoft does most of their UWP presentations in C# and despite in ongoing work in C++/WinRT as C++/CX, I doubt it will change the situation that much.
The book should be read by every C programmer, but it is really outdated and has many bad practices including buffer overflows etc. in examples. For learning "good parts" it certainly isn't the right thing.
If one would include vector, string, array, smart pointers and maybe simple template code in C+ it would already have a big safety advantage over plain C.
As Linus wrote when someone told him on the mailing lists that Git should've be done in C++
"""
Quite frankly, even if the choice of C were to do nothing but keep the C++ programmers out, that in itself would be a huge reason to use C.
"""
"Keeping new ideas away" is a good working definition of an old coot.
Templates already let you build generic data structures and algorithms when the size of the data on doesn't vary.
/pedantic++
A good example is how Turbo Vision, Object Windows Library, Visual Components Library, Qt look like and the concessions Microsoft had to make to Afx so that it got rebranded into MFC and appealed to the Windows C developers.
I was first talking about the set of frameworks that dared to embrace the then C++ best practices. All of them are older than C++98, or even before STL was being considered.
Then I mentioned the fact that when Microsoft presented their Afx framework to their beta testers, mostly Windows C developers, it was considered too high level and it was rebuilt as MFC.
By the way, why is this C implementation something that requires C99 or C11 features? It looked just like standard old-fashioned ANSI C to me.
template <typename Function>
void my_vector::munge(Function initialize_gap) {
create_uninitialized_gap();
// Some of our elements now contain uninitialized memory.
// We don't want to expose this to arbitrary code, but
// it's OK to expose this to the function the user
// provided specifically to handle this.
initialize_gap(gap_begin(), gap_end());
// The gap is now initialized, so when we return from
// this method, we'll be in a well-defined state.
}
This is the standard pattern you use for such things in functional languages, and it works very well. The idea is to ensure that intermediate states are only seen by code that explicitly expects to handle them.Actually, you really, really, really want to inline every call to push_back for std::vector. Here is a relevant presentation by Chandler Carruth https://www.youtube.com/watch?v=s4wnuiCwTGU . But, hey, you don't want to make the compiler work for you :).
Writing C takes more effort in some cases, so it's a trade-off.
This isn't a question about the language, it's definitely a quality-of-implementation issue for standard exceptions to have what() give a useful message.
Just to clarify perror will generally give a better error message in:
FILE* fh = fopen("foo.txt", "w+");
if (!fh) {
perror("unable to open foo.txt");
return 1;
}
Than what() will in: std::fstream fs;
fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
fs.open("too.txt");
}
catch (const std::exception& ex)
{
std::cerr << "unable to open foo.txt: "
<< ex.what() << '\n';
}
(Ignoring other issues such as whether exceptions are better than return codes, etc.)Having comprehensible error messages is related to being a simple language, IMO. C++ compile-time errors are notorious for being unreadable. I'm not familiar with the output of what(), but I'd wager that the problem is a consequence of all the abstraction.
Personally, I think early return is lazy programming because not cleaning up non-RAII objects is only one class of bugs that it can introduce. Things like vector renormalization, update notification and even later sections of algorithms can all be missed by early return. Not having early return means you need to explicitly skip later code if you don't want it, rather than essentially turning on "skip-all".
* (Classic): use goto so that instead of returning directly, all branches jump to a cleanup label. This is tricky to do well, but possible.
* (Nonstandard): GCC offers a nonstandard function attribute to register a function as a destructor for this kind of thing. I'm sure they claim it was inspired by Scheme's dynamic-wind/dynamic-wind concept.
* (Memory pooling): Apache APR relies heavily on memory pools. One feature of APR's memory pools is the ability to register functions to run (in, I believe, non-deterministic order) on data when the pool is eventually destroyed.
Containers and resource handles are Stroustrup's advice in his blue C++ book.
Manual deletion is something I would never really want to see outside a destructor.
What's broken about namespaces? If I create a class named Bitmap whilst using Windows.h, will I conflict with GDI::Bitmap???
RAII - how can you possibly guarantee safe lifetimes of objects without RAII?
Templates - do you like writing the same thing a million times and having to update it and fix it in those million places?
The difficulty I have with them is they are open, not closed. Any piece of code can crack open a namespace and insert more names into it. Thus, you can have problems with "hijacking", where foo(int) is inserted, while unknown to you someone else inserted foo(unsigned) that does something completely different, and the compiler decides the latter is a better overload match.
Complicating things further, the names visible in a namespace are dependent on where the compiler is lexically in the code - more names can be inserted further down.
As a counterpoint, many have suggested to me that this openness is a critical capability they need for their code. My answer is that using namespaces in that manner offers little improvement over just using a prefix on the identifiers.
And so the debate goes on!
Btw, visibility rules in a namespace (but not in a struct/class) are the same as for the global namespace, so no, a function in a namespace won't see another declared after it.
If so, C++ has a nice advantage in that all the type checks are at compile-time so you don't pay any more for those, and also you don't duplicate binary code with macros.
Interestingly, C11 defines _Generic selection, a kind of a switch for types, but its usefulness is hindered by the fact that it cannot be used in conjunction with sizeof.
Basically what go and rust have achieved, except you keep most of the C/C++ syntax "taste", and you remove too high level stuff like templates and inheritance. Honestly I don't think templates are so useful, since most programmers already re-use STL containers, which are templates, but nobody really write relevant template classes.
> That's probably the C++ community's fault
The problem with C++ is backward compatibility with C and big corporations trying to not break their codebase. It makes it very hard to make the language evolve. For example D is a very good language, but it can't gain momentum as long as it doesn't have a real way to gain presence. We just have to wait that corporation clean their codebase to make room for C++ compilers to adapt, and then things should improve.
For me, the only selling point of C++ is templates, and I've never written a nontrivial program that didn't use them (and I like to think that I don't just use them frivolously).
We use loads of template classes at where I work. Our code would not be maintainable without them.
The biggest difference is that templates are more expressive, allowing for compile time meta-programming.
On the other hand templates used as a metaprogramming tool fall behind any modern macro system.
Also it is way better with latest standard improvements.
What I'm saying is that you can use C++ without having to use STL and you can use C++ just fine to implement your own exceptionless container if you want (and many such implementations exist already).
And you can also compile with exceptions disabled. Should your code throw it will terminate the process.
The C++ community at its finest.
You can use which bits of C++ that you want - that's its great strength, permitting low-level behaviour whilst supporting high level abstractions (and as simple or as complex as you like).
Windows actually provides a stronger gurantee here. But if any of your dependencies are not OOM safe then neither are you. And much of windows userland is not OOM safe.
This is a situation where C++ really shines: you can use C++ with templates and not only have much cleaner code, but also avoid forcing the compiler to inline every single call to array_push_back. And you don't even need to use anything more than structs and functions, you don't need to go "full OO".
I wish C programmers would be more open to C++ but it seems like they're for the most part pretty closed. That's probably the C++ community's fault, but I'm not sure how to make amends.