My issue with RAII is that it's fundamentally not what you expect to happen.
If I say:
int i;
I don't expect that to create an integer. I expect that to reserve memory in the program's space for an integer.
In the same way I don't expect this to call malloc:
int *i;
I just expect that to hold the space in the program data where and int pointer can be stored.
Furthermore I don't expect this to run any code (just as the other two).
string s;
I don't expect that to construct anything, that's just a place for me to put data. This way, we know not to use data before it's initialized.
In C++:
int i;
if (i > 10) printf("Greater then 10\n");
Is that going to always work how we expect it?
How about:
string s;
if (s.length() > 10) printf("Len is greater then 10\n");
That is going to always work how we expect it since it was constructed.
This is fundamentally different then uninitialized integers or primitives so I dislike it's use.
I strongly dislike the use of this term but it in my mind is an "Anti-pattern" as it promotes two separate ways of thinking about variables that are fundamentally completely different.
Now I'm no expert in education but this knowledge has taken me years to learn through my life and I don't think I'd have been able to do it if I didn't LOVE computers and programming.
I don't know how someone would think that this is good behavior because of it's inconsistencies between two outwardly similar cases.
When I see int i; without context I don't know anything about where memory is allocated.
It can either be the following.
1) Stack allocated
2) Heap allocated
3) Is in the read only data segment.
Just starting with a int, it can be any of the above three, two that reserve the memory on the heap/stack and another that is only a read only part of memory. The heap is the only place where I would need to call malloc/free.
Though for most common libraries when I see string s; They mostly have a pointer null terminated string in memory, and the primitive data types residing as the member's of string.
RAII helps in the situation where instead of having to do tedious code such as.
String s;
allocate_string(&s);
// do something with string.
free_string(&s);
It can all be done via a standard convention of calling the deconstructor or constructor to initialise and free itself.
For instance my professor when explaining to the other students why we use context-free grammars for parser he said "we always want a language to work how we tell it to. An if doesn't mean something different depending on where it is"
While I do agree that he is incorrect and that is a very close minded, and out dated, view of language design he is accidentally correct in another sense.
I'd like to ask "why" C++ chooses int i is different based on the location. I know the answer myself, but my peers don't and probably wont until years of pain in industry while parroting the same nonsense that C++ is the best language same as all of the other loons who think that there is a "best language".
Why is it ok for struct packing, for aligning data, and for initial memory state to decide how the program behaves?
In C++ it does, in C it's more sane but it sill happens.
> RAII helps in the situation where instead of having to do tedious code such as.
How is:
string s = "";
and
int i = 0;
Tedious? That seems like the sane alternative as it puts the behavior in the hand of the programmer. In nearly every language this is decided as the preferred way of allocating variables. Define AND assign to create something useful.
I don't think I understand how adding more case-specific behavior that doesn't apply to everything should be considered sane. It's more edge cases that aren't needed.
It's much simpler to say "we are just going to hold a slot in memory for any object, you need to initialize it" or to say "we are going to have default initialization for EVERY object even primitives" but it's horrible to say "we're going to do a combination of both and you have to figure it out"
I think part of your expectation may be coming from Java, where only fundamental data types are stack allocated and everything else has reference semantics to heap-allocated objects.
For the sake of syntactic consistency, one could argue that C++ should mandate "()" as a suffix to declarations to invoke the default constructor. However, what good is an allocated object that's _not_ constructed? For many classes, of course, the default constructor is trivial, but for others it isn't. The purpose of the constructor is to provide invariants that the code can rely on--it isn't obvious to me what common use case you could consider for an object that's allocated on the stack but not constructed, and it seems like it's asking for trouble.
Also, since C++ inherits C's syntax and semantics for fundamental types, "int i;" must be legal. So we cannot abolish the form.
Problem is, using destructors (and RAII) may have, in general, certain performance implications that C may not be willing to deal with. This is somewhat similar to demanding that C had a garbage collector built in. Note that, being lazy, memory management by means of garbage collection may turn out being more efficient than using the (eager) destructors.
Don't see how there could be any performance implications with having RAII within C. It would be a simple method call with the this pointer to the struct during complication. Though if your performance consideration is at the assembly code level why use C?
I've had some good/bad experiences with garbage collectors. They do help improve productivity and ROI within the enterprise space where memory isn't too much of of issue. Then when memory does become a issue within smaller memory footprint environments they can be a real pain in the ass.