c++ - 递归类定义产生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 次)。
是什么导致了这种行为?
解决方案
您不应存储指向临时对象的指针。
他们的寿命太短了。如果没有通过编译器错误直接阻止它,编译器可能会警告您。例如。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);
但是您现在必须手动管理所有已分配Tester
s 的生命周期,这可能比看起来要复杂得多。
推荐的解决方案是使用智能指针来管理Tester
s 的生命周期。我将使用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);
}
}
}
}
}
};
推荐阅读
- reactjs - Material-UI - 单击菜单按钮打开所有可用的菜单项
- python - 与 Python 相关的查询 - 文件夹读取
- netlogo - 为什么 netlogo 给我 set [] of x 的错误
- ios - LinkedIn sdk iOS 错误,如果已经登录到 LinkedIn 应用程序
- javascript - 关闭弹出窗口后如何仅在父页面中刷新特定下拉列表
- dart - 如何在 Flutter 中测试/调试摇树?
- json - @RequestParam 从邮递员的表单数据中传递 Spring Boot 中的 pojo 字段
- c# - 使用 C# 访问 Google 电子表格(无 API)
- git - 如何减少 git grep 的输出
- r - 如何使用带条件的 rowSums 返回二进制值?