Search code examples
c++treedynamic-memory-allocationstatic-memory-allocation

Why accessing variable declared locally from outside is working?


In tree, while taking input (inside takeInput function), tree node was made using dynamic allocation, but I tried doing it statically, but as tree node were declared inside a function locally it should have not worked because its a local variable (I was expecting a error). But Why am I able print it even after that:

NOTE: this code takes input recursively (and may not be the best way)

#include<bits/stdc++.h>
using namespace std;
template <typename T>
class treeNode{
    public:
    T data;
    vector <treeNode<T>> children;
    treeNode(T data){
        this->data=data;
    } 
};
treeNode<int> takeInput(){
    int rootdata;
    cout<<"Enter Node"<<endl;
    cin>>rootdata;
    // treeNode<int>* root= new treeNode<int>(rootdata);

    treeNode<int> root(rootdata);   //Static Allocation

    cout<< "Enter Number of children of "<<rootdata<<endl;
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        treeNode<int> child = takeInput();
        root.children.push_back(child);
    }
    return root;
}
void printTree(treeNode<int> root){
    cout<<root.data<<": ";
    for(int i=0;i<root.children.size();i++){
        cout<<root.children[i].data<<",";
    }
    cout<<endl;
    for(int i=0; i<root.children.size();i++){
        printTree(root.children[i]);
    }
}
int main(){
    treeNode<int> root= takeInput();
    printTree(root);
    return 0;
}

Following code is using dynamic allocation:

#include<bits/stdc++.h>
using namespace std;

template <typename T>
class TreeNode{
    public:
    T data;
    vector <TreeNode<T>*> children;
    TreeNode(T data){
        this->data=data;
    }
};
TreeNode<int>* takeInput(){
    int rootdata;
    cout<<"Enter node"<<endl;
    cin>>rootdata;
    TreeNode<int>* root=new TreeNode<int>(rootdata);
    cout<<"Enter number of children of "<<rootdata<<endl;
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        TreeNode<int>* child=takeInput();
        root->children.push_back(child);
    }
    return root;
}
void printTree(TreeNode<int>* root){
    if (root == NULL){
        return;
    }
    cout<< root->data<<" :";
    for(int i=0;i<root->children.size(); i++){
        cout<<root->children[i]->data<<",";
    }
    cout<<endl;
    for(int i=0;i<(*root).children.size();i++){
        printTree(root->children[i]);
    }
}
int main(){
    TreeNode<int>* root = takeInput();
    printTree(root);
    return 0;
}

Solution

  • Your code is equivalent to

    A foo() {
        A a;
        a = bar();
        return a;
    }
    

    a is just copied into the return value (That copy might be avoided too). Replace A with treeNode<int> and the semantics remain the same.

    Why then the dynamic code?

    I'm guessing the code version using dynamic allocation was probably coded up thinking that something like

    struct A {
        std::vector<A> vecA;
    };
    

    is a recursive definition for A since when vecA is declared A is an incomplete type. But that's not the case anymore and this is officially into C++17 (though it worked for some compilers in earlier versions too) where some STL containers can do with incomplete type. Hence it used the form

    vector <TreeNode<T>*> children;
    

    storing pointers to the children and hence that code, which is similar to the familiar LinkedList Node data structure definition

    struct Node {
        int data;
        Node* next; // The TreeNode stores a vector of pointers instead.
    };
    

    Conclusion

    Stack allocation is usually preferred when possible since it's faster than the heap route. Also, that code with dynamic allocation brings in the headache of memory management unless smart pointers are being used. It's just not needed for your code. Go with the stack allocation route for your example and let std::vector take care of maintaining the dynamic array.