मेरे पास सी ++ में एक पेड़ है और पेड़ में एक विधि है जो पेड़ में उस पत्ते को जोड़ने पर एक नए पत्ते के लिए साझा_ptr संदर्भ देता है। जब मैं ट्री क्लास के अन्य तरीकों के अंदर इसका उपयोग करता हूं तो यह ठीक काम करता है, लेकिन जब मैं इसे मुख्य से कॉल करने का प्रयास करता हूं, तो यह कोड के समान ही काम करने के बावजूद क्रैश हो जाता है।

यहाँ नोड और वृक्ष वर्ग है:

#include <iostream>
#include <vector>
#include <memory>

class Node
{
public:
    int value;
    std::vector<std::shared_ptr<Node> > children; 
    Node(int value): value{value} {}
};

class Tree
{
public:
    std::shared_ptr<Node> root;
    Tree(): root {nullptr} {}

    std::shared_ptr<Node> CreateLeaf(int value)
    {
        return std::make_shared<Node>(value);
    }

    std::shared_ptr<Node>& AddLeaf(int value, std::shared_ptr<Node>& ptr)
    {
        if(ptr == nullptr)
        {
            ptr = std::move(CreateLeaf(value));
            return ptr;
        }
        else
        {
            std::shared_ptr<Node> newLeaf = CreateLeaf(value);
            ptr->children.push_back(std::move(newLeaf));
            return ptr->children.back();
        }
    }

    void otherMethod()
    {
        AddLeaf(1, root);
        std::shared_ptr<Node>& temporary = AddLeaf(2, root);
        std::cout << "temporary->value: " << temporary->value << std::endl;
    }

};

यदि मुख्य कार्य है:

int main()
{
    Tree t;
    t.otherMethod();
}

फिर कार्यक्रम सही ढंग से चलता है।

हालाँकि, यदि मुख्य कार्य है:

int main()
{
    Tree t;
    t.AddLeaf(1, t.root);
    std::shared_ptr<Node>& b = t.AddLeaf(2, t.root);
    std::cout << "b->value = " << b->value << std::endl; 

}

प्रोग्राम क्रैश हो जाता है, इसके बावजूद यह एक ही काम करता है। ऐसा लगता है कि AddLeaf केवल b में nullptr को संग्रहीत कर रहा है, इसके बावजूद यह एक सतत वस्तु का संदर्भ है, t.root->child[0]। ऐसा क्यों कर रहा है?

0
Paradox 15 नवम्बर 2018, 16:21

1 उत्तर

सबसे बढ़िया उत्तर

वेक्टर, या किसी भी स्वयं-आकार बदलने वाले कंटेनर में तत्वों का संदर्भ होना खतरनाक है। जब मैंने सी ++ में अपना पहला गेम बनाया तो मैंने इससे निपटाया।

अनिवार्य रूप से क्या होता है:

एक नया साझा_प्टर जोड़ते समय आपका वेक्टर आकार बदल जाता है, जिससे एक ऑपरेशन अधिक मेमोरी आवंटित कर सकता है। उस प्रक्रिया के दौरान वर्तमान में मौजूद वेक्टर को नष्ट कर दिया जाता है और संभवतः स्मृति में एक अलग स्थान पर आवंटित किया जाता है।
जिसका अर्थ है कि वर्तमान में मौजूद सभी पॉइंटर्स या वेक्टर में तत्वों के संदर्भ अमान्य हो जाते हैं और आपके प्रोग्राम को क्रैश कर सकते हैं। किन्हीं बिंदुओं पर।

एक वेक्टर में shared_ptr के संदर्भों को पास करना मूल रूप से अपरिभाषित व्यवहार को बढ़ावा देता है। एक बेहतर कदम यह होगा कि एक नया शेयर्ड_प्टर लौटाया जाए और संदर्भ काउंटर को केवल इंक्रीमेंट किया जाए।
इस तरह, जब वेक्टर का आकार बदलता है, तो आपके प्रोग्राम में कोई भी संदर्भ अमान्य नहीं हो जाता है।

संदर्भ को हटाने से मदद मिलनी चाहिए:

std::shared_ptr<Node> AddLeaf(int value, std::shared_ptr<Node>& ptr)
{
    if(ptr == nullptr)
    {
        ptr = std::move(CreateLeaf(value));
        return ptr;
    }
    else
    {
        std::shared_ptr<Node> newLeaf = CreateLeaf(value);
        ptr->children.push_back(std::move(newLeaf));
        return ptr->children.back();
    }
}

वैसे भी, जैसा कि मैं देख रहा हूं कि आप एक पेड़ बना रहे हैं, शायद आप उस कोड पर एक नज़र डालना चाहेंगे जो मैंने एक (छोड़े गए) प्रोजेक्ट में लिखा है: https://github.com/wvanbreukelen/LexMe/blob/feature-tree-node-vector-specialization/LexMe /TreeNode.h
यह एक सामान्य पेड़ के लिए पूरी तरह से कार्यान्वयन है जिसमें चाल शब्दार्थ और पुनरावृत्तियों के प्रति मित्रता है।

1
Julian vD 15 नवम्बर 2018, 13:59