代码之家  ›  专栏  ›  技术社区  ›  KeyC0de

奇怪的循环模板模式-不能创建多个派生类吗?

  •  0
  • KeyC0de  · 技术社区  · 7 年前

    #include <iostream>
    
    static int nodes = 0;
    
    class TreeNode {
    private:
        int m_id;
    public:
        TreeNode() : 
            m_id(++nodes)
        {}
        TreeNode(int id) :
            m_id(id)
        {
            ++nodes;
        }
        TreeNode* left;
        TreeNode* right;
    
        int getId() const {
            return m_id;
        }
    };
    
    
    template<typename T>
    //typename std::enable_if<std::is_base_of<TreeParser, T>::value>::type
    class TreeParser {
    protected:
        TreeParser() {
            ++parsers;
        }
    public:
        static uint32_t parsers;
        void preorderTraversal(TreeNode* node) {
            if (node != nullptr) {
                processNode(node);
                preorderTraversal(node->left);
                preorderTraversal(node->right);
            }
        }
        virtual ~TreeParser() = default;
    
        void processNode(TreeNode* node) {              // 2, 3. the generic algorithm is customized by derived classes
            static_cast<T*>(this)->processNode(node);   // depending on the client's demand - the right function will be called
        }
    };
    
    template<class T>
    uint32_t TreeParser<T>::parsers = 0;
    
    class SpecializedTreeParser1 : public TreeParser<SpecializedTreeParser1> // 1. is-a relationship
    {
    public:
        explicit SpecializedTreeParser1() : 
            TreeParser()
        {}
        void processNode(TreeNode* node) {
            std::cout << "Customized (derived - SpecializedTreeParser1) processNode(node) - "
                "id=" << node->getId() << '\n';
        }
    };
    
    class SpecializedTreeParser2 : public TreeParser<SpecializedTreeParser2> // 1. is-a relationship
    {
    public:
        explicit SpecializedTreeParser2() : 
            TreeParser()
        {}
        void processNode(TreeNode* node) {
            std::cout << "Customized (derived - SpecializedTreeParser2) processNode(node) - "
                "id=" << node->getId() << '\n';
        }
    };
    
    
    int main() 
    {
        TreeNode root;
        TreeNode leftChild;
        TreeNode rightChild;
    
        root.left = &leftChild;
        root.right = &rightChild;
    
        std::cout << "Root id: " << root.getId() << '\n';
        std::cout << "Left child id: " << leftChild.getId() << '\n';
        std::cout << "Right child id: " << rightChild.getId() << '\n';
    
        SpecializedTreeParser1 _1;
        _1.preorderTraversal(&root);
    
        SpecializedTreeParser2 _2;
        _2.preorderTraversal(&root);
    }
    

    输出为:

    Root id: 1
    Left child id: 2
    Right child id: 3
    Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=1
    Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=2
    Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=1963060099 // what is that?
    

    2 回复  |  直到 7 年前
        1
  •  5
  •   Michael Kenzel    7 年前

    left right 指针 leftChild rightChild preorderTraversal() 到了那里,你的程序就崩溃了。这也是为什么你会觉得奇怪 id 最后:它是从某个随机记忆位置读取的

    要解决此问题,请确保 左边 正确的 组织的成员 TreeNode 总是初始化为 nullptr 正如代码的其余部分所期望的:

    class TreeNode {
        …
        TreeNode* left = nullptr;
        TreeNode* right = nullptr;
        …
    };
    
        2
  •  3
  •   bolov    7 年前

    问题是 SpecializedTreeParser2 _2; TreeParser leftChild rightChild 很好,他们的 id 已打印。检查输出

    不, 右孩子 初始化不正确,问题不存在 SpecializedTreeParser2 _2

    TreeNode::left; TreeNode* right; 美丽 不确定的行为是什么 整个计划 具有未定义的行为,而不仅仅是使用未初始化的变量。这意味着尽管 SpecializedTreeParser1 _1 _1 似乎在工作 _2 不是。这是未定义的行为。修正它,不要试图理解为什么会有这种特殊的行为。


    int main()
    {
        int a = 24;
        std::cout << a << std::endl;
    
        int b; // uninitialized
        std::cout << b << std::endl; // <-- this line makes the WHOLE program to have UB
    }
    

    那么上述程序可能的行为是什么呢?

    print "24"
    print <garbage>
    

    是可能的,也是合理的

    print "24"
    print "0"
    

    也有可能

    print "24"
    <crash>
    

    也不奇怪

    print <garbage>
    print "24"
    

    也有可能。。。等等什么?对!!程序的未定义性质不必在行中显示 std::cout << b << std::endl . 程序的整个执行过程是未定义的。 有什么事吗 可能发生!

    所有这些都是可能的:

    <crash>
    
    print "0"
    print "0"
    
    print "24"
    print "24"
    
    print "24"
    print "0"
    <crash>
    
    print "Here be dragons"