The abhorrent 🔥 rites of C
-
Have you ever seen C++11 code? I'm seriously starting to doubt that. All you need is provided by the STL.
The STL provides a standard object for "undo this arbitrary state change I just made, by calling the arbitrary method that undoes it correctly"? Your claims are becoming more and more ridiculous the further you pursue them.
-
Sure, but what does that have to do with being able to handle destroy-on-unlock by itself or not?
The mutex doesn't unlock itself in its destructor, because a mutex that's locked when it's about to be destroyed is an error in your program. (I.e., the mutex, which protects access to a shared resource, is being destroyed while the shared resource it is supposed to protect is being accessed).
-
Observation in passing about one key difference between try/catch with C++ RAII style and try/catch/finally:
If you need to run something to unknit some state that must be left knitted until after the catch blocks complete, you need either a classic finally block OR:
[code]
// stuff stuff stuff{
RaiiObject doodad(doodad_init_values);try { DifferentRaiiObject otherDoodad; // try things here } // otherDoodad is destructed here, BEFORE the relevant catch block runs. catch( This ) { // handle This } catch( That ) { // handle That } catch( TheOther ) { // handle TheOther }
} // Destructs doodad here.
[/code]Which does look a little clumsy.
-
The mutex doesn't unlock itself in its destructor, because a mutex that's locked when it's about to be destroyed is an error in your program. (I.e., the mutex, which protects access to a shared resource, is being destroyed while the shared resource it is supposed to protect is being accessed).
That makes even less sense. Why in the world would you destroy your mutex while it's still in use?
-
The STL provides a standard object for "undo this arbitrary state change I just made, by calling the arbitrary method that undoes it correctly"?
Yes, exactly that was proposed (and will most likely be standardized) for C++17:
(And you can easily write one yourself if you don't want to wait.)
Your claims are becoming more and more ridiculous the further you pursue them.
No, it's becoming more and more obvious that you don't know much about C++.
-
Sure, but what does that have to do with being able to handle destroy-on-unlock by itself or not?
It makes no sense for the mutex to unlock itself when it's destroyed, because once it's destroyed, it no longer exists. What do you care if a non-existent mutex is locked or not? And you can't lock a non-existent mutex either.What you're confused about is tying the existence of the mutex with the locked state of the shared object. Whether an object is locked or not is one piece of state that must always exist. If the mutex was constantly being created and destroyed, you'd have race conditions when two threads try to create a mutex for the same shared thing at the same time.
So instead, the first thread to call lock on the already existing mutex locks it. The mutex has no way of knowing when the first thread is done with it, so it can't call unlock on itself. The calling thread delegates the responsibility of calling lock and unlock on the unique_lock, so that it will always leave the mutex in the correct state no matter what happens.
With all these perfect solutions to avoid the problems, would someone like to tell me why malloc(), new and new[] are still things in C+11, since they got rid of other things (like the old meaning of auto)?
Because the solutions are library classes that are implemented in the language itself. new, malloc, and whatever other allocators you use are primitive operations, and the library composes them in different ways to solve different problems. You might as well ask why bricks still exist when we have brick walls.Now as far as I read, the "unique" part means there can only ever be one copy of that pointer so it's always clear who owns it and when it needs to get destroyed. If I'm getting that correctly, that doesn't work with hardware registers.
You're not getting that correctly. The unique means that there can only be one owner (as opposed to shared, where there are many owners). The owner is the one responsible for freeing the memory. You can get raw pointers out of a unique_ptr, and they behave just like you would expect. You're just not supposed to free them. You let the unique_ptr object do that.And you can "move" the ownership around, in which case the original unique_ptr loses ownership and another pointer takes over the responsibility.
-
It may be intentional, but it isn't necessarily a good idea. The problem isn't that it exists, but that it is syntactically quiet.
Not bothering with it still allows you to get a raw pointer, because you can still call
operator ->()
directly (it must, eventually, return a raw pointer to the target type), but the call sticks out like a sore thumb, and acts as a clue to code reviewers that you're doing something a little out of the ordinary.shared_ptr_variable->member()
is exactly the same as:
shared_ptr_variable.operator ->()->member()
-
That makes even less sense. Why in the world would you destroy your mutex while it's still in use?
Do you even know what a mutex is, and how it's used? You're the one suggesting the destructor should call unlock on it!
-
That makes even less sense. Why in the world would you destroy your mutex while it's still in use?
It doesn't make sense, and that's what people have been trying to tell you in regard to:
Missing the point. Why does std::mutex not unlock itself (as necessary) in its own destructor?
-
@cvi said:
The mutex doesn't unlock itself in its destructor, because a mutex that's locked when it's about to be destroyed is an error in your program. (I.e., the mutex, which protects access to a shared resource, is being destroyed while the shared resource it is supposed to protect is being accessed).
That makes even less sense. Why in the world would you destroy your mutex while it's still in use?
Read more carefully. That's exactly @cvi 's point. You wouldn't, unless there's a bug. A mutex object that is destroyed while locked should assert in debug builds, and explode in flames on release builds.
-
The problem isn't that it exists, but that it is syntactically quiet.
There is a lot of existing code that expects raw pointers and sometimes you simply cannot use a reference. If the standards committee had proposed a really ugly syntax for getting the raw pointer out of a shared_ptr, people simply might not have started using it.
-
Yes, exactly that was proposed (and will most likely be standardized) for C++17:
I look at that and I see 18 pages of highly technical gobbledygook that boils down to "here's a generic way to use templates to create a class that, when destroyed at the end of a scope, executes arbitrary code provided by the developer, in thetry/finally
block automagically generated by RAII." How can you not see that this is, by definition, much more complicated than just executing your arbitrary code in atry/finally
block without creating a class to do it for you and a bunch of compiler magic and template magic to do that for you?It makes no sense for the mutex to unlock itself when it's destroyed, because once it's destroyed, it no longer exists. What do you care if a non-existent mutex is locked or not? And you can't lock a non-existent mutex either.
Well, apparently someone cares enough to write that template that unlocks it upon destruction.Do you even know what a mutex is, and how it's used? You're the one suggesting the destructor should call unlock on it!
As opposed to having some other object wrap around it to unlock and destroy it?Read more carefully. That's exactly @cvi 's point. You wouldn't, unless there's a bug. A mutex object that is destroyed while locked should assert in debug builds, and explode in flames on release builds.
Then what is the point of that template?
-
How can you not see that this is, by definition, much more complicated than just executing your arbitrary code in a try/finally block without creating a class to do it for you and a bunch of compiler magic and template magic to do that for you?
Are you now arguing that complexity is bad even if it's hidden from the developer and handled in the compiler and/or standard library? Because I'm sure the code generation for try/finally isn't that simple either.
-
It may be intentional, but it isn't necessarily a good idea. The problem isn't that it exists, but that it is syntactically quiet.
I don't think it's a problem. Passing references to smart pointers is a bad idea. Smart pointers shouldn't be in the signature of a function unless that function needs ownership. Otherwise, pass a reference to the object itself, or a pointer if the pointer may be null. Aside from looking ugly, when you pass the reference to the smart pointer, accessing the object itself then requires two steps of indirection: the reference to the smart pointer, and then the jump to wherever the object lives. If you just pass the pointer or reference to the object, there's only one step of indirection.
-
@LaoC said:
No, not at all. That was just a caveat that what I know aboutwhen I last wrote C++, auto_ptr was all there was and it was crap
So your point is that C++98 was not much better than C. I agree. Take a look at modern C++ sometime.
unique_ptr
is from skimming some documentation and not actual experience so I might have gotten it wrong.@LaoC said:
I wasn't trying to argue that. But when you have to forego all the modern safety features of C++ to do what you need to do, there's little point in using it. Even more because you have to forego them both at the lowest level and at the highest, the border to userland. You really don't want the tight coupling and the headaches with the interface to other programming languages that come with a C++ ABI either.Say you have a network buffer pointed to by one of these. You enter the pointer into some DMA controller register after shoving it through an MMU translation to find the physical address. [...]
Well, if you do stuff like that, you have to use raw pointers, of course. Still doesn't mean C is any better than C++ for implementing something like that.
@Steve_The_Cynic said:
That's not much different from "I know that I have to `free` what I `malloc`ed".Both of them (the last time I looked) have a nice quiet method for accessing the raw pointer, T *std::shared_ptr<T>::get().
That's intentional and not a problem. If you use C++11 correctly, you know that a raw pointer means "I can use this object temporarily, but I do not own it".
-
Are nasal demons on strike?
-
Are you now arguing that complexity is bad even if it's hidden from the developer and handled in the compiler and/or standard library? Because I'm sure the code generation for try/finally isn't that simple either.
I'm arguing that creating an entire class to do something in its destructor inside a magictry/finally
block is needlessly complex versus using atry/finally
block without having to create any classes for what can be done with one single line of code.How is this difficult to understand? How many different times, in how many different ways, do I have to restate this before you finally (no pun intended) grasp the obvious?!?
-
Well, apparently someone cares enough to write that template that unlocks it upon destruction.
Not destruction of the mutex. Destruction of the object that represents ownership of the mutex. Once your thread gives up ownership of the mutex, the mutex still exists, and is available for other threads to take ownership.
-
But when you have to forego all the modern safety features of C++ to do what you need to do, there's little point in using it.
You don't necessarily have to do that (see @kian's post, I totally missed that you misunderstood the limitations of unique_ptr) and even if you do, you can still use the modern features in other parts of the code to make your life easier.
That's not much different from "I know that I have to free what I malloced"
Yes, there's a huge difference between remembering a basic rule (never use feature X) and having to inspect every single code path to make sure all resources are cleaned up correctly all the time.
-
How is this difficult to understand? How many different times, in how many different ways, do I have to restate this before you finally (no pun intended) grasp the obvious?!?
Apparently, you like typing the same thing over and over again. You wouldn't defend try-finally otherwise. Really, you should be thanking us.
-
Not destruction of the mutex. Destruction of the object that represents ownership of the mutex. Once your thread gives up ownership of the mutex, the mutex still exists, and is available for other threads to take ownership.
Oh! You mean holding the lock on the mutex. ("Ownership" has a completely different meaning, particularly when we're discussing the freeing of objects!) OK, that actually makes a little bit more sense conceptually.It's still stupid to create an entire class to do what you should be able to do in one line of code without having to create a class for it. (And even stupider to create a template to create that class!)
-
I'm arguing that creating an entire class to do something in its destructor inside a magic try/finally block is needlessly complex versus using a try/finally block without having to create any classes for what can be done with one single line of code.
How is this difficult to understand? How many different times, in how many different ways, do I have to restate this before you finally (no pun intended) grasp the obvious?!?
And how many times do I have to state that You don't have to do anything that complex? Why do you care if the internals of a feature are a bit complicated if the end result is that you don't have to worry about freeing resources anymore. At all. Which you do when you have to put finally blocks everywhere. A whole class of bugs has just been eliminated for you!
-
It's still stupid to create an entire class
"entire class"
Holy shit, what do you have against classes?
you should be able to do in one line of code
For the end user (developer), it IS one line of code. So what are you complaining about?
-
Why do you care if the internals of a feature are a bit complicated if the end result is that you don't have to worry about freeing resources anymore. At all. Which you do when you have to put finally blocks everywhere.
Because when your language advertises its high performance, internals matter. Details like this matter. Creating an entire class to do a single line of code's job is just plain ridiculous, period.Which you do when you have to put finally blocks everywhere.
See above, re: "try/finally
is not about freeing resources unless you're so brain-damaged by C++ that you literally can't see it in any other way; it's about exception-safe, guaranteed reversible state changes."A whole class of bugs has just been eliminated for you!
If by "eliminated" you mean "swept under the rug, hidden within an abstraction inversion that makes it more difficult to debug when something inevitably goes wrong," then yes, I agree entirely.what do you have against classes?
I have nothing against classes. I have plenty against using the wrong tool for the job. Creating an entire class that only exists for its destructor is a needlessly heavyweight solution when a single line of code, without gratuitous object construction and destruction, would work just as well.
-
Because when your language advertises its high performance, internals matter. Details like this matter.
Creating a template class like unique_lock or unique_ptr has exactly ZERO runtime overhead. Not a single CPU instruction is necessary for doing that. Maybe that's what you're not getting.
it's about exception-safe, guaranteed reversible state changes
That's what RAII is about as well.
If by "eliminated" you mean "swept under the rug, hidden within an abstraction inversion that makes it more difficult to debug when something inevitably goes wrong,"
You've never used C++11. If you had, you'd know that code using those RAII wrappers is mostly guaranteed to be correct when it compiles.
-
There is no construction and destruction in the compiled assembly. There's just the call to the function. See the example I used above of combining unique ptr with malloc. It's literally as fast as doing it manually, but not error prone.
-
Creating a template class like unique_lock or unique_ptr has exactly ZERO runtime overhead. Not a single CPU instruction is necessary for doing that. Maybe that's what you're not getting.
How do you call a function (such as a destructor) with no function call overhead? Does this template guarantee that the destructor will be inlined in every possible use case?You've never used C++11. If you had, you'd know that code using those RAII wrappers is mostly guaranteed to be correct when it compiles.
Yeah, mostly. The problem with mostly is that the more layers you bury your mostly under, the harder it is to get at or fix when it breaks down.
-
@LaoC said:
Now as far as I read, the "unique" part means there can only ever be one copy of that pointer so it's always clear who owns it and when it needs to get destroyed. If I'm getting that correctly, that doesn't work with hardware registers.
You're not getting that correctly. The unique means that there can only be one owner (as opposed to shared, where there are many owners). The owner is the one responsible for freeing the memory. You can get raw pointers out of a unique_ptr, and they behave just like you would expect. You're just not supposed to free them. You let the unique_ptr object do that.And you can "move" the ownership around, in which case the original unique_ptr loses ownership and another pointer takes over the responsibility.
I stand corrected. But … I do have to keep the object around somewhere, right? If I wanted to free buffers in an interrupt handler when DMA is done, I'd have to have a table to find the original object and manually trigger the destructor instead of justkfree
ing the pointer from hardware?
-
You do have to keep it around. How you do it is up to you. If you stored it in a table, you have to delete the entry in the table, same as before. You just don't need to remember to call the free function on it first.
I'm assuming the interrupt handler is written in C++, of course.
-
How do you call a function (such as a destructor) with no function call overhead?
Well, the destructor has to be called, of course. It contains what you have to do to clean up the resource and has to be executed whether you use RAII or not.
Does this template guarantee that the destructor will be inlined in every possible use case?
Guaranteed: Probably not, since the compiler is given a lot of freedom. But those templates are designed in a way that every sane compiler will inline what it can. For example, I'm pretty sure that every single C++11-capable compiler will inline the lambda passed to a scope_exit (see draft I linked).
The problem with mostly is that the more layers you bury your mostly under, the harder it is to get at or fix when it breaks down.
Uh, okay. Please show me an example of code using one of these templates which fails and is not easy to debug. Otherwise I won't believe your claim, since you obviously don't use C++11.
Those templates are simple enough that there's not much that can go wrong inside of them.
-
Well, the destructor has to be called, of course. It contains what you have to do to clean up the resource and has to be executed whether you use RAII or not.
Unless, of course, you're not using RAII and you can just write a single line of code inline (no need for templates, lambdas, or anything else,) and not have to call the destructor.Seriously, there's a reason why the phrase "explicit is better than implicit" exists.
-
@Mason_Wheeler: @asdf's point is that it's safer (and more economic in effort) to type:
class mutex_lock : noncopyable { mutex& m; public: mutex_lock(mutex& m) : m(m) { m.lock(); } ~muxex_lock() { m.unlock(); } }; void SomeFunction() { mutex_lock lk(myMutex); //Do stuff } void SomeOtherFunction() { mutex_lock lk(myMutex); //Do stuff } void SomeMoreFunction() { mutex_lock lk(myMutex); //Do stuff } void LetsAddAFewMoreFunctions() { mutex_lock lk(myMutex); //Do stuff } void AndAnother() { mutex_lock lk(myMutex); //Do stuff }
Than:
void SomeFunction() { myMutex.lock(); try { //Do stuff } finally { //Other clean up myMutex.unlock(); } } void SomeOtherFunction() { myMutex.lock(); try { //Do stuff } finally { //Other clean up myMutex.unlock(); } } void SomeMoreFunction() { myMutex.lock(); try { //Do stuff } finally { //Other clean up myMutex.unlock(); } } void LetsAddAFewMoreFunctions() { myMutex.lock(); try { //Do stuff } finally { //Other clean up //whoops! } } void AndAnother() { myMutex.lock(); try { //Do stuff } finally { //Other clean up myMutex.unlock(); } }
PS: You should program in C++/CLI, which offers both destructors and
finally
blocks (and I have to admit those are useful when your code is unique enough).PPS: Discourse, stop adding line feeds to everything I copy and paste!
-
Seriously, there's a reason why the phrase "explicit is better than implicit" exists.
Yes, in the Zen of Python. It's a joke. On the other hand, the DRY principle (Don't Repeat Yourself) is a well respected design principle.
-
Unless, of course, you're not using RAII and you can just write a single line of code inline
- try
- finally
- <actual code here>
That's at least three lines of code. Repeat for each scope. (-> code duplication for free!)
and not have to call the destructor.
I was talking about unique_ptr here, which destructs an actual object. The destructor of the "wrapper" itself will obviously be inlined.
> Seriously, there's a reason why the phrase "explicit is better than implicit" exists.
Yes, in the Zen of Python. It's a joke. On the other hand, the DRY principle (Don't Repeat Yourself) is a well respected design principle.
QFT
And even though "explicit is better than implicit" is true sometimes, this is not one of those cases. There's no "magic" happening behind the scenes, all your code is doing is the obvious thing, which it does anyway (local variables go out of scope -> object destruction).
-
I was also talking about this case:
std::unique_lock<std::mutex> foo() { // lock some mutex return lock; } std::unique_lock<std::mutex> bar() { // do stuff return foo(); } int main() { auto lock = bar(); // do stuff return 0; }
Really easy to handle with RAII, extremely unintuitive and error-prone if you don't have an object that represents the lock you're holding.
-
PS: You should program in C++/CLI, which offers both destructors and finally blocks (and I have to admit those are useful when your code is unique enough).
When I'm doing CLR programming, I generally use Boo, which has powerful metaprogramming built in. I would accomplish the example above by the use of thelock
macro, (which comes standard with the Boo language,) which expands to essentially the second example. Guaranteed, well-tested safety, no spurious object creation and destruction, and unlike C++, the compiler has a pipeline that will show you the final version of your program before codegen, after all macros have been expanded and all implicit conversions and transformations have been performed. You don't need it too often, but when you do, it makes debugging the tricky stuff soooo much easier.Yes, in the Zen of Python. It's a joke.
You are apparently not familiar with the concept of "ha ha but serious", which is prominent among computing jokes.That's at least three lines of code. Repeat for each scope. (-> code duplication for free!)
As opposed to a line for a{
to open a scope, a line for a}
to close the scope, and at least one line to set up your disposer object?And even though "explicit is better than implicit" is true sometimes, this is not one of those cases. There's no "magic" happening behind the scenes, all your code is doing is the obvious thing, which it does anyway (local variables go out of scope -> object destruction).
That's not obvious at all. What if you got that local from something else that's still holding a reference to it. (Oh, right! C++'s object model is utterly broken. It has objects-as-value-types, which should never exist in the first place. But that's a completely different discussion.)
-
What if you got that local from something else that's still holding a reference to it.
That's why classes like unique_ptr and unique_lock exist: They can only be moved, not copied, so there is no other reference (unless you're deliberately abusing C++).
So: Bzzzzzt, wrong, try again.
-
As opposed to a line for a { to open a scope, a line for a } to close the scope
Now you're just arguing in bad faith, you need two of each when you use try/finally!
-
You are apparently not familiar with the concept of "ha ha but serious", which is prominent among computing jokes.
Yes, I'm also familiar with the concept of people writing things that are serious but wrong. Like a language where whitespace has meaning. Hey, that's Python too!
Besides, do you use explicit type names where
var
(in C#) orauto
(in C++) would suffice?
-
Like a language where whitespace has meaning.
If you want to bash Python, I'll have to start disagreeing with you.
Besides, do you use explicit type names where var (in C#) or auto (in C++) would suffice?
:hand:
I do that quite often, whenever the return type of a function cannot be inferred from its name.
-
Its a special type of pointer. FILE is already a type. The FILE * semantic actually tells you that it is a FILE pointer. FILE is the type. * signifies that its a pointer to a file. What would actually change if you omit the pointer? Nothing. Virtually no one tries to use free() on a file pointer. It is not even an issue.
Yeah, because you remember you can't do it on a FILE. But why would you have to remember it? And what if you see Foo *? Can you call free on it?
Doing fclose() on a file is not an issue amongst C programmers
It's not an issue because it's something you must do. In C++ this isn't an issue because you don't have to do it.
Show me a large security hole that has resulted from it. Is not more complicated than doing .close() on a file object. You are arguing non issues.
So you really don't see any difference between needing to track every execution path through the lifetime of some data and manually adding the correct deallocation function everywhere, for doing literally nothing? Let me repeat that: literally nothing. Use a smart pointer and you don't have to call delete/close/free/whatever. And you are guaranteed there will bo no memory leak and no double free etc. You seriously want to argue there no difference between that and C?
You mean the one that is based on the Linux kernel, which in turn is written in C? ( https://en.wikipedia.org/wiki/Tizen ). Given how unenthusiastic you are about C, why haven't you reimplement the rest of the kernel in C++?
You were told about 10 times already. Do you homework and read it.
I though that was the meat of your argument, that you know what is it like to implement an OS from the ground up in C++.
No, the meat of the argument is that C is an unsafe and horrible language, and C programmers are mostly idiots who fail to see reason and would rather argue to death, defending their language, than to admit there are better alternatives. Oh wait...
But regardless, you must have some killer performance measurements of what writing those not-quite-userspace parts of the OS in C++11 are. Can you share them with us?
Nope, because I don't have them, nice try. But what you have is Google and tons of info where and why C++ outperforms C. Hell, I even linked a book on it.
C++ has worse spatial locality than C.
Bullshit. Saying something doesn't exactly make it true, you know, especially when it so absurd.
Compilers can do that too now. I was hesitant to mention sanitizer routines since I don't know if they exist in GCC. On clang -fsanitize=< memory/address/etc > would do exactly that.
But WHY on earth would you rather do it, when can NOT do it and be sure it works?
Exception handling has an overhead.
Especially those zero-cost exceptions. Man, are those a runtime penalty! And small note: you don't have to use them (but you actually should; topic for another time maybe).
You claim that you can get much better performance writing the entire OS in C++? Fine, so be prepared to backup your claims instead of restorting to angry outbursts against the C language or claiming that you are the only one in possesion of the truth.
For crying out loud - how many times do you have to be reminded why OSes are written in C mostly? And how many times can you NOT search why/where C++ outperforms C?
Yes, I read the link you posted.C++ can qsort() faster than C, but that doesn't prove that its faster for coding operating systems.
That means you totally missed the point. Read that book again till you do. Hint: it isn't dependent on kernel-user space, it's a language feature.
Generating a 10000 element list and asking for the first element is faster than both C and C++ because it computes fewer things. That doesn't make it faster for the general case and I wouldn't even want to see the code that generates.
That page explained exactly why it's faster in general case. Try again, this time by actually reading it.
The claim that C has to evolve and be replaced is a valid one. The claim that is a bad language is, at best a dubious one
Because all those CVEs we get are just by coincidence mostly in C, rather than C++, code.
The claim that C++ can replace it for ALL THE THINGS has to have some foundations
Given it's a superset (besides some minor quirks) of C, than yes - it can replace C.
-
How do people care so much about C. There's like 120 new posts this morning. Jesus.
-
Let me repeat that: literally nothing. Use a smart pointer and you don't have to call delete/close/free/whatever. And you are guaranteed there will bo no memory leak and no double free etc.
Then why are memory leaks so common in released software written in C++? Must not be as magically simple as you claim.Memory leaks are common in C++ software. Memory leaks are common in software written in other native languages, where memory must be managed manually. Memory leaks are common in managed code programs, where there's supposed to be a garbage collector that magically makes memory leaks not exist (hey, that claim looks familiar!) but someone left a reference assigned somewhere by mistake.
I've been programming my entire life, (well, not literally, but since third grade, so close enough,) and I have never seen any technology, in any language or framework, that magically removes the need to think about memory management and gets rid of memory leaks for you.
What I have seen, many times, is snake oil salesmen claiming such a thing exists, which removes the perception of the need to think about memory management, which in turn removes a lot of actual thinking about it, which then causes inattentive people to create memory leaks and not know how to track them down or fix them. I've seen that enough times to know that any such claim should be considered highly suspicious, if not a flat-out lie, by default.
-
Then why are memory leaks so common in released software written in C++? Must not be as magically simple as you claim.
- Because most programs are still mostly C++98
- Because most Tutorials still teach the "old" C++. C++11 is meant to be used differently
- Because people are idiots
What I have seen, many times, is snake oil salesmen claiming such a thing exists, which removes the perception of the need to think about memory management
Nobody ever claimed you don't need to think about memory management in C++. Our claim was that you don't have to think about proper object destruction and the various code paths.
If you use RAII correctly, managing memory in C++ becomes as easy as managing memory in a garbage-collected language. Making sure you don't keep references to objects you don't need is still your responsibility, like in any other language.
-
If you use RAII correctly, managing memory in C++ becomes as easy as managing memory in a garbage-collected language.
...which is also full of memory leaks, as I pointed out above.
-
> If you use RAII correctly, managing memory in C++ becomes as easy as managing memory in a garbage-collected language.
...which is also full of memory leaks, as I pointed out above.
Seriously, what's your point right now? That programming is hard? That people are idiots?
-
My point is what it has been this whole time: that RAII is a needlessly complicated abstraction inversion for every use case that does not involve resource management. (I'm not sure how we even got on the subject of memory leaks!)
-
My point is what it has been this whole time: that RAII is a needlessly complicated abstraction inversion for every use case that does not involve resource management.
I feel like we've already debunked that myth, so:
Please name 5 use cases for finally that do not involve resource management and are made unnecessarily complicated by the fact that C++ favors RAII.
-
I feel like we've already debunked that myth
Where? How? I posit that it's impossible to do in RAII what you can do withtry/finally
without doing something needlessly complicated involving creating an entire class, and what do I get? Examples of creating classes! Examples of creating templates that create classes! How exactly do you "debunk" my thesis by demonstrating the truth of it?
-
without doing something needlessly complicated involving creating an entire class
How many times do I have to repeat it?
Those classes are in the standard library.
You don't need to do anything, just use them.