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...
- Wrap the objects in an STL container when the structure between said objects is "trackable" with the STL container being used (or perhaps trackable with the STL container being used plus some reasonably simple extra logic), or
- Explicitly use the heap to build the objects with new and destructor/delete, or smart pointers, and manually build the structure "between" said objects (as in
left_
andright_
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;
}
CodePudding user response:
Not to your literal questions but you might find this useful.
- Smart pointers like
std::unique_ptr
are most basic RAII classes. - Using RAII is the only reasonably sane way to ensure exception safety.
- In your particular example, I’d use
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.