13 ms·
I've started getting back into C++ after many years away and it's all coming back to me. Now of course it's C++11, which does have some nice features, but real
by cletus 12y ago
I've started getting back into C++ after many years away and it's all coming back to me.
Now of course it's C++11, which does have some nice features, but really I think we've reached the point where we need to start again (downvote away).
Let me give you an example: I recently came across some code that was written years ago that has two size types: one 32/64 bit signed and the other 32 bit unsigned. This creates a bunch of issues when compiled on 32 and 64 bit architectures and there is a substantial amount of effort to clean it up.
I point out things like this to colleagues who are very pro-C++ and I inevitably get the same response: "well that's just bad API design".
Thing is, if you look at the history of this example it's a series of incremental changes, all well-meaning and reasoned, some of which are done by people who I could only call luminaries, and even they make significant and far-reaching mistakes.
So what hope do the rest of us have?
But my biggest problem with the C-dialects is pointers. Namely if you return or receive a pointer, it's not necessarily clear who owns it. The way this is handled is comments like "DO NOT delete this" or "you MUST delete this".
I like that a language like Rust is trying to formalize the concept of object ownership. I'd really like to see that idea mature and take hold.
Until now there hasn't really been a competitive alternative to C/C++. It's not Go (as much I love Go). Maybe it's Rust. We can but hope.
My other big problem (and this applies to Java too) is directly dealing with low-level multithreading primitives like threads, thread groups and mutexes. I really like that Go has taken a different approach here.
What I find with particularly young programmers is they don't have the appropriate fear of writing multithreaded code. It's really, really hard to write correct multithreaded code with low-level primitives. It's why (excellent) books like Java Concurrency in Practice exist.
As for the feature list of C++14 [1], I wonder what all these "auto" declarations will do to the significant work required for static analysis tools, that are an essential part of modern, large-scale C++ codebases.
The literal types (like "s" for std::string or seconds) are cute but at some point the STL was optional. I'm a little leery of embedding it directly in the language but hey I'm no expert.
[1]: http://en.wikipedia.org/wiki/C%2B%2B14 http://en.wikipedia.org/wiki/C%2B%2B14
- Nursie 12y agoOn pointers - Like a lot of things with C, you need to handle ownership by convention. I generally try to make the actor that created the heap pointer the owner of it and responsible for its destruction - i.e. instead of allocating buffers and handing them back up the chain full of data, pass them down from the requester where possible. If the length is not known at that point, create another routine to calculate it. Using techniques like this, ownership can become easier. You can do anything with it, but that's half the problem! And personally I like threads :)
- jonstewart 12y agoThe convention I use in my own code is that passing a raw pointer around, or returning one, confers ownership. Mostly I do not schlep raw pointers around between functions, though. I use shared_ptr or scoped_ptr or unique_ptr or a reference. When I have to use raw pointers with some given library, then I immediately wrap their usage in my own facade types. Sticking to these oft-repeated best practices means that crashes are rare and memory leaks are rarer still.
- jeorgun 12y agoThe literal types are optional— they're user-defined literals, which can be written like (say): QString operator""_qs(const char* c_str, size_t len) { return c_str; } void foo() { auto iama_non_stl_type_ama = "foo"_qs; }
- deathanatos 12y ago> code that was written years ago that has two size types: one 32/64 bit signed and the other 32 bit unsigned. This creates a bunch of issues when compiled on 32 and 64 bit architectures and there is a substantial amount of effort to clean it up. Such things happen whenever an invalid assumption that is commonly true becomes not so commonly true, such as the 32-bit to 64-bit migration. That said, C/C++ provides you the tools: size_t is an unsigned type meant for storing array indexes and the size of objects in memory. If you want a real integer, then (unsigned) int. If you need integers of a definite fixed size, u?int(8|16|32|64)_t. Granted, I've encountered that not enough people take the time to understand the tool they're using. Honestly, I wish the default integer type was not fixed size, and especially not platform dependent fixed size, like Python; for non-[indexes/sizes], I feel like this is usually what you want. > "well that's just bad API design". Well… > But my biggest problem with the C-dialects is pointers. Namely if you return or receive a pointer, it's not necessarily clear who owns it. The way this is handled is comments like "DO NOT delete this" or "you MUST delete this". This is true; in C++, your use of pointers should be minimal, though this can be a problem with references just as easily. In the (special) case of std::shared_ptr, ownership is clear. That said, this is a problem not unique to C++: it exists in Java, Javascript, Python, and many others as well. For example, some_list = [1, 2, 3] some_obj = MyObject(some_list) If some_obj expects to own that list, and it's constructor is: def __init__(self, some_list): self._some_list = some_list which I see a lot, you've got the same problem. (I'm also interested to see how well Rust tackles this, as I agree it's a problem.) > My other big problem (and this applies to Java too) is directly dealing with low-level multithreading primitives like threads, thread groups and mutexes. I really like that Go has taken a different approach here. Honestly, I've never thought multithreading was "hard". There's a set of rules you have to hold yourself too, otherwise, yes, you can make your life very hard. If you limit shared data as much as possible, and what data is shared has a well defined locking order (preferably behind code that enforces it) — often this is just a single mutex — then I don't see the problem. Thread-safe queues ("channels", I think Go calls them) are also useful to establish "service" like threads.
- steveklabnik 12y ago> I'm also interested to see how well Rust tackles this, as I agree it's a problem. Is this a "see how well it actually works" or a "I don't know how this works and I'm interested?" I'm curious because this is possibly Rust's most important, interesting feature, and I want to make sure we've got some reasonable messaging here.
- daemin 12y agoIn C++ parlance ownership of pointers is dictated by how they are stored. Raw pointers are only views of the object passed around, these confer no ownership. std::unique_ptr owns a single unique instance of the memory. std::shared_ptr (and std::weak_ptr) confer ownership of the memory amongst several other entities.