Adding correct calls to
delete[] arr;in the right places (remember exceptions, assignments and so on!), which is rather bug-prone.Also,
new T[size]default-constructssizeobjects of typeTin the contiguous memory. This requiresTto be default constructible and might be expensive, depending onTandsize.Use a
std::unique_ptr<T[]> arrinstead. This will automatically clean up memory, but you still need to keep track of size, capacity etc.Still has the default-construction issue from above.
Use a
std::unique_ptr<std::aligned_storage<sizeof(T), alignof(T)>[]>. Similar to above, but without the default-construction issue. Requires placement new, though.Use a
std::vector<T>(or similar container) and let it handle all that memory management stuff. EasiestThe easiest solution, and hard to screw up. It also makes adding additional features easier, like growing capacity.
At the very lowest level, one could extract the comparison operation and the actual container and write the heap logic independently of those. This would allow to reuse the same implementation for many different orderings (min-heap, max-heap, ...) and different underlying storage.
There are many uses for heaps (or as the standard library calls them: Priority queues), and more often than not they don't operate on pure numbers. Most likely you'll have some objects ordered by a
prioritydata member or by some timestamp. Relying onstd::numeric_limitsthus isn't a good idea.For deleting a random element, it would be better to swap it with the last element, and then bubble that one up or down as needed.
If I understand the question correctly, you seem to want to ask "How can I fail gracefully if I have to return a value?". The simple answer is: You can't. If you need to return a value, that value needs to come from somewhere, and special values like
INT_MIN,INT_MAX,0,-1orT{}just aren't expressive enough since they introduce ambiguities and require special checks by the caller.In some cases, if "no result" is an actually expected return value, you can use
std::optional<T>for that, which makes those checks more explicit.In other cases, there is no need for returning a value. For example,
delete_mindoesn't need to return a value (the same value could have been retrieved by callingget_minbeforehand). Ifdelete_mins return type got changed tovoid, it could fail gracefully (not throwing an exception) if the heap were empty.Generally, try to not return values from state modifiers (e.g. insertion or deletinondeletion operations), unless those values are an indirect result of those state modifiers themselves (e.g. an iterator pointing to the newly inserted element/to the element after the removed element).
See above. -