void f(int n, int x) {
Gadget p = new Gadget(n);
// ...
if (x < 100) throw new Exception("Weird!); // no leak
if (x < 200) return;
// ...
}
Yes, good night's sleep tonight after writing that...There are two better options:
void f(int n, int x) {
Gadget p(n); // Stack allocated
// ...
if (x < 100) throw new Exception("Weird!); // no leak
if (x < 200) return;
// ...
}
Or, if it really has to be a pointer: void f(int n, int x) {
std::unique_ptr<Gadget> p = new Gadget(n); // Smart pointer
// ...
if (x < 100) throw new Exception("Weird!); // no leak
if (x < 200) return;
// ...
}
Both will be automatically freed as soon as the scope is exited.This is fine until I want to do this, at which point C++ becomes a memory management bastard:
void f(int n, int x) {
Gadget g = new Gadget(n);
// ...
if (x < 100) throw new GadgetException("Gadget broke", x);
if (x < 200) return;
// ...
} Gadget g = new Gadget(n);
That code won't compile unless Gadget has an assignment operator that accepts a pointer, which is, well, not a very common scenario.Note: I'm not trying to be nitpicky. I really don't understand what you mean when you write that you "want to do this".
Now, I don't know anything about you, but based off your comments in this thread I'm not sure you have a working knowledge of C++.
void f(int n, int x) {
Gadget g = new Gadget(n);
std::shared_ptr<void> defer(nullptr, [g](void *) {
delete g;
});
...
But honestly ... I'd just use a stack allocated object - or a smart pointer. In the last year I didn't write one delete. (I'm a full time C++ dev).In C++, you have various ways of referencing and passing objects, so you need to be aware of the lifetime and ownership of objects. It's arguably harder, and unfortunately, the C heritage makes it a lot harder than it needs to be :(
void f(int n, int x) {
Reader fr = new FileReader("foo.txt");
// ...
if (x < 100) throw new Exception("Weird!); // resource leak
if (x < 200) return; // resource leak
// ...
}
A garbage collector that gives a false sense of security is much worse than no garbage collector at all.Anyway, what if you need to share non-memory resources? Suddenly you cannot depend on the garbage collector, you cannot use try/finally, you cannot use using or try-with-resource - you need to handle the situation just like in C++, except you're given fewer tools to do it - and a poorer understanding of the situation if you've learned that you don't need to do manual resource management due to the garbage collector.
C++ makes it possible for the library writer to take care of freeing the resources automatically.
I've done a limited amount of C++ many many years ago before I even knew what garbage collection was and I keep thinking of revisiting it, but honestly in my line of work ("Enterprise") I don't need the mental overhead of dealing with things such as pointers and memory allocation.
Perhaps, my view is outdated, but I get the impression that everything in C/C++ is just a little thorny when compared to other slightly more high-level languages, such as namespaces, package management, list comprehensions, library compatibilities, type strictness etc.
I would like to be wrong about that though... I wish I had a little more motivation to spend some real time with C++ (or perhaps even C).
if (x < 100) throw new Exception("Weird!); // LEAK!
You shouldn't use `new` either when throwing exceptions. Just: if (x < 100) throw Exception("Weird!); // no leak.- partial construction: having to deal with already allocated pointers in case of errors during the construction - copy construction: who owns the resources? (Alternatively, you'll need to remember to disable the copy constructor explicitly.) - copy assignment: likewise
Doing the Right Thing is just so much easier when you wrap those member objects into e.g. `std::unique_ptr<T>`. That'll disable copy construction and assignment, which means that you'll need to think of that separately if it's needed.
Can you expand upon what you mean by "safe" here?
If you mean "unable to fail", I vehemently disagree. Constructors should validate what is passed into them and fail if that is invalid. The alternative is to construct a zombie object that can't actually be used. Objects like this subvert the type system and lead to lots of unnecessary "if object.is_valid()" checks all over code that uses them.
If you have a pointer in a class that is exactly what you want to happen in most cases. Then if you want copies you "have to" define your own copy constructor and assignment operator that copy the object pointed to rather than just copying the pointer.
Of course, you may prove me wrong.