You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何std::list<Directory>无法保留添加的对象?C++递归遍历问题

问题描述

我是资深C#开发者,正在学习C++,编写了一个递归处理文件夹结构以构建Directory对象层级的程序。我知道recursive_directory_iterator,但认为它无法满足我的控制需求。为什么我的child_dirs(std::list<Directory>)无法保留添加的对象?我确定是作用域问题,但不理解——顶层Directory对象在作用域内,它的成员也应该在作用域内,不是吗?我考虑过如果在包含大量子项的路径(如C:\)上运行时,可能需要切换到动态存储,但目前想尽量简化……难道不使用动态存储就无法实现我的需求?

Directory.h

#pragma once
#include <list>
#include <filesystem>
#include <iostream>
#include "FileEntry.h"
class Directory
{
public:
    std::string debug;
    std::string path;
    std::list<FileEntry> child_files {};
    std::list<Directory> child_dirs {};

    Directory();
    Directory(std::filesystem::directory_entry in_path);
    Directory(std::string in_path);
};

Directory.cpp

#include <list>
#include <filesystem>
#include "Directory.h"

//Constructors
Directory::Directory(){
}

Directory::Directory(std::filesystem::directory_entry in_path) {
    path = in_path.path().string();
}
Directory::Directory(std::string in_path) {
    path = in_path;
}

FileEntry.h

#pragma once
#include <string>
#include <filesystem>
class FileEntry
{
public:
    std::string FileName = "";
    uintmax_t SizeInBytes = 0;

    FileEntry(std::filesystem::directory_entry entry) 
    {
        FileName = entry.path().string();
        SizeInBytes = entry.file_size();
    }
};

Main.cpp

void process_dir(Directory& path);
void print_dir(Directory& dir);

int main()
{
    Directory start_dir("C:\\Users\\steve\\source\\repos\\topdir");
    std::cout << "--- Starting Dir: " << start_dir.path << " ---" << std::endl;

    process_dir(start_dir);

    std::cout << "Done." << std::endl;
    print_dir(start_dir);
}

void process_dir(Directory& dir)
{
    std::cout << "Dir: " << dir.path << " child_dirs.size: " << dir.child_dirs.size() << std::endl;
    for (std::filesystem::directory_entry entry : std::filesystem::directory_iterator(dir.path))
    {
        if (entry.is_directory()) 
        {
            Directory new_dir(entry);
            dir.child_dirs.push_back(new_dir);
            process_dir(new_dir);
        }
        if (entry.is_regular_file() || entry.is_block_file() || entry.is_character_file()) {
            dir.child_files.push_back(entry);
            std::cout << "   File: " << entry.path() << " child_files.size: " << dir.child_files.size() << std::endl;
        }
    }
}

void print_dir(Directory& dir)
{
    std::cout << "DIR: " << dir.path << " child_files.size: " << dir.child_files.size() << std::endl;
    for (FileEntry file : dir.child_files)
    {
        std::cout << " File: " << file.FileName << std::endl;
    }
    for (Directory child_dir : dir.child_dirs)
    {
        print_dir(child_dir);
    }
}

程序输出

--- Starting Dir: C:\Users\steve\source\repos\topdir ---
Dir: C:\Users\steve\source\repos\topdir child_dirs.size: 0
Dir: C:\Users\steve\source\repos\topdir\middir1 child_dirs.size: 0
Dir: C:\Users\steve\source\repos\topdir\middir1\lowdir1 child_dirs.size: 0
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir1\\lowdir1filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir1\\lowdir1fileb.txt" child_files.size: 2
Dir: C:\Users\steve\source\repos\topdir\middir1\lowdir2 child_dirs.size: 0
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir2\\lowdir2filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir2\\lowdir2fileb.txt" child_files.size: 2
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\middir1filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\middir1fileb.txt" child_files.size: 2
Dir: C:\Users\steve\source\repos\topdir\middir2 child_dirs.size: 0
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir2\\middir2filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir2\\middir2fileb.txt" child_files.size: 2
   File: "C:\\Users\\steve\\source\\repos\\topdir\\topdirfilea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\topdirfileb.txt" child_files.size: 2
Done.
DIR: C:\Users\steve\source\repos\topdir child_files.size: 2
 File: C:\Users\steve\source\repos\topdir\topdirfilea.txt
 File: C:\Users\steve\source\repos\topdir\topdirfileb.txt
DIR: C:\Users\steve\source\repos\topdir\middir1 child_files.size: 0
DIR: C:\Users\steve\source\repos\topdir\middir2 child_files.size: 0
问题分析与解决

核心问题有两个,都是C++值语义和引用特性导致的,和动态存储无关:

1. 递归处理的是临时对象,而非列表中存储的对象

在process_dir函数中,你创建栈上临时对象new_dir,调用push_back时会把这个对象拷贝到dir.child_dirs列表里,但随后递归调用process_dir(new_dir)处理的是栈上的临时对象,不是列表里的拷贝。当递归返回后,栈上的new_dir被销毁,列表里的拷贝完全没被修改——这就是为什么子目录的child_files为空。

修复方法:直接处理列表中刚添加的对象引用,替换原有的目录创建与递归代码:

if (entry.is_directory()) 
{
    // 直接在列表中构造对象,避免拷贝
    dir.child_dirs.emplace_back(entry);
    // 获取列表最后一个元素的引用,递归处理它
    process_dir(dir.child_dirs.back());
}

或者用push_back后取引用:

if (entry.is_directory()) 
{
    Directory new_dir(entry);
    dir.child_dirs.push_back(new_dir);
    // 改为处理列表中的对象引用
    process_dir(dir.child_dirs.back());
}

2. 打印时拷贝子目录对象(非必需但建议修复)

print_dir中的循环for (Directory child_dir : dir.child_dirs)会创建子目录的拷贝,虽然不影响结果,但会造成不必要的性能开销,建议改为引用:

for (Directory& child_dir : dir.child_dirs)
{
    print_dir(child_dir);
}

修改后的输出效果

修复后,打印时会正确显示所有子目录中的文件,比如middir1的child_files会显示为2,同时它的子目录lowdir1、lowdir2的文件也会被打印出来。

内容的提问来源于stack exchange,提问作者stevoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 06:32:04