首页 > 解决方案 > 递归类定义产生c ++中的无效指针

问题描述

我正在尝试创建一个具有递归结构的类,例如 Matrioska 娃娃,以便更清晰。像这个 A 类包含 8 个 A 类型的子级。我正在使用指针,正如这个问题中指出的那样: C++ 类可以将自己包含为成员吗?

我在每个类实例上分配一个索引值,以跟踪该类相对于父类的下降程度。

这是我的代码:

测试者.h

#pragma once

const unsigned int MAX_RECURSION = 3;

struct Tester
{

    Tester* Children[2][2][2];
    unsigned int Index;

    Tester(unsigned int index) : Index(index), Children()
    {
        std::cout << std::string(index, '-') << "(" << index << ")" << std::endl;
        if (index < MAX_RECURSION) {
            for (int x = 0; x < 2; x++) {
                for (int y = 0; y < 2; y++) {
                    for (int z = 0; z < 2; z++) {

                        this->Children[x][y][z] = &Tester(index + 1);;
                    }
                }
            }
        }

    }

    void Walk(int& tr)
    {

        if (this->Index < MAX_RECURSION) {
            for (int x = 0; x < 2; x++) {
                for (int y = 0; y < 2; y++) {
                    for (int z = 0; z < 2; z++) {
                        tr++;
                        (this->Children[x][y][z])->Walk(tr);
                    }
                }
            }

        }
    }

};

主要.cpp:

#include "Tester.h"
#include <iostream>
int main() {

    int c = 0;

    Tester t(1);
    t.Walk(c);

    return 0;
}

值 c 保存函数Tester::Walk运行的次数。

通过运行此代码,可以正确创建所有类,如您在输出日志中所见:

-(1)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
--(2)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)
---(3)

索引为 3 的类确实被创建了 8*8 次,所以 64。(每次递归 8 次)。

但是当我尝试Walk降低 的Children变量时t,walk 函数只运行了 8 次。通过调试,我后来发现只有前 8 个 Tester 类(main.cpp 中 t 类中的第一个)具有正确的索引,因为其他的具有3435973836. 我最初认为递归创建有问题,但日志显示索引正常工作(打印了 1 次、2 8 次和 3 64 次)。

是什么导致了这种行为?

标签: c++recursion

解决方案


您不应存储指向临时对象的指针。

他们的寿命太短了。如果没有通过编译器错误直接阻止它,编译器可能会警告您。例如。g++9.3 发出

..\src\test.cpp:18:68: error: taking address of rvalue [-fpermissive]
   18 |                         this->Children[x][y][z] = &Tester(index + 1);;
      |                                                                    ^

this->Children[x][y][z] = &Tester(index + 1);;

Tester(index + 1)创建一个未命名的临时对象,该对象仅存在到行尾。在您有机会使用指向该对象的指针之前,它已经超出范围并被销毁。

这个问题最直接的解决办法就是手动动态分配

this->Children[x][y][z] = new Tester(index + 1);

但是您现在必须手动管理所有已分配Testers 的生命周期,这可能比看起来要复杂得多。

推荐的解决方案是使用智能指针来管理Testers 的生命周期。我将使用astd::unique_ptr因为它是最简单和最严格的智能指针:

#include <iostream>
#include <memory> // unique_ptr, make_unique
const unsigned int MAX_RECURSION = 3;

struct Tester
{

    std::unique_ptr<Tester> Children[2][2][2]; 
        // array of Testers with scope-managed lifetime. When the array goes out of 
        // scope, all of the Testers will be automatically destroyed.
    unsigned int Index;

    Tester(unsigned int index) :  Children(), Index(index)
    {
        std::cout << std::string(index, '-') << "(" << index << ")" << std::endl;
        if (index < MAX_RECURSION) {
            for (int x = 0; x < 2; x++) {
                for (int y = 0; y < 2; y++) {
                    for (int z = 0; z < 2; z++) {

                        this->Children[x][y][z] = std::make_unique<Tester>(index + 1);
                            // makes a unique_ptr referencing a brand-new Tester
                    }
                }
            }
        }

    }

    void Walk(int& tr)
    {

        if (this->Index < MAX_RECURSION) {
            for (int x = 0; x < 2; x++) {
                for (int y = 0; y < 2; y++) {
                    for (int z = 0; z < 2; z++) {
                        tr++;
                        (this->Children[x][y][z])->Walk(tr);
                    }
                }
            }

        }
    }
};

推荐阅读