Everything below has to do with situations where a developer makes a custom C++ class (I have in mind something like OnlyKnowDemandAtRuntime
below)... and there can be no way of knowing how many instances/objects "the user" will need during runtime.
Question 1: As a sanity check, is it fair to say that in Case One below, RAII is being used to manage the "dynamic" usage of OnlyKnowDemandAtRuntime
?
Question 2: Is it correct to say that in case two, RAII isn't the first option that comes to mind simply because it is inconvenient (possibly a major understatement here) to hack up a way to wrap the nodes of a tree in an STL container? And, therefore, it is simpler to just use new and destructor/delete (or smart pointers) here, rather than scramble for a way to have the standard library manage memory for us. Note: Nothing here is a question about whether trees are often used in day to day work; rather, everything in this post is about the intuition behind the decision making one must do when using C++ to create objects at runtime. Note: Of course smart pointers are themselves part of the library, and of course they handle memory for us just as the library containers do... but for the purposes of this question I'm putting smart pointers and new
on the same footing: Because my question is about the limits on the abilities of the STL containers to have more and more instances of something like OnlyKnowDemandAtRuntime
inserted into them at runtime, while also being able to handle the relationships between said instances (without adding lots of logic to keep track of where things are in the container).
Question 3: If 1 and 2 are reasonable enough, then would a fair summary be this: [When a developer makes a custom class but doesn't know how many objects of it will be needed during runtime], either...
left_
and right_
of Case Two below).Quick reminder, this isn't about whether we need to build trees in day to day work with C++. Also, (and I suppose this is already clear to anyone who would answer this question) this is not about "use the heap when an object is too big for the stack or when an object needs a lifetime beyond the scope in which it was created".
Case One:
// This is the class "for" which an unknown of objects will be created during runtime
class OnlyKnowDemandAtRuntime {
public:
OnlyKnowDemandAtRuntime(int num) : number_(num) {};
private:
int number_;
};
// This is the class where an a priori unknown number of `OnlyKnowDemandAtRuntime` objects are created at runtime.
class SomeOtherClass {
public:
void NeedAnotherOnlyKnownAtRuntime(int num) {
v_only_know_demand_at_runtime_.emplace_back(num);
}
private:
std::vector<OnlyKnowDemandAtRuntime> v_only_know_demand_at_runtime_;
}
Case Two:
// This is the class "for" which an unknown of objects will be created during runtime
class Node{
public:
Node(int value) : value_(value), left_(nullptr), right_(nullptr) {};
private:
int value_;
Node *left_;
Node *right_;
friend class Tree;
};
// This is the class where an a priori unknown number of `Node` objects are created at runtime.
class Tree {
public:
~Tree() { // Traverse tree and `delete` every `Node *` //}
void Insert(int value) {
Node *new_node = new Node(value);
ThisMethodPlacesNewNodeInTheAppropriateLeafPosition(new_node);
}
private:
Node *root;
}
Not to your literal questions but you might find this useful.
std::unique_ptr
are most basic RAII classes.std::unique_ptr<Node>
specifically. With arbitrary graph that’d be more complicated ofc.Also,
makes a custom C++ class but doesn't know how many objects of it will be needed during runtime.
That’s highly unspecific. It is important that you have a container (be it SomeOtherClass
or Tree
or whatever) that manages these objects. Otherwise, things may become really really complicated.