首页 > 解决方案 > C++ 前向声明和递归包含

问题描述

我有 8 个文件FileSystem.cppNode.cpp和它们Directory.cppFile.cpphpp文件。

这是内存文件系统的基本模型。类FileSystem负责创建Directory具有自己的节点(即文件或目录)的根。

该类NodeDirectory和的父类File。我需要包含Node到两个文件中以继承,但在 Node 类实现中我需要访问FileandDirectory来执行一些操作。如果我尝试进行前向声明,我将得到类成员的未定义引用。我读了一些关于这个的文章,但我无法解决我的标题问题。在 c++ 中解决此类问题的正确方法是什么。我如何/应该使用标题来解决这类问题?

请注意我的背景不同,所以我认为在 C++ 上遗漏了一些关于标头和递归声明的内容。

这里是这些文件的来源。

文件系统.cpp

#include <string>
#include <vector>
#include <iostream>

using namespace std;

#include "Node.hpp"
#include "Directory.hpp"

class FileSystem
{
private:
    // methods

    // attributes
    string name;
    Directory *root;

    friend class Directory;

public:
    int get_fresh_uid()
    {
        return 10;
    }

    FileSystem(string in_name)
    {
        name = in_name;
        root = new Directory(this, get_fresh_uid(), "root", nullptr);
    }

    ~FileSystem() {
        
    }

    // accessors
    string get_name()
    {
        return name;
    }

    Directory *get_root()
    {
        return root;
    }

    friend ostream &operator<<(ostream &output, FileSystem &fs)
    {
        return output;
    }
};

节点.cpp

#include <string>

#include "Directory.cpp"
#include "File.cpp"
class FileSystem;

using namespace std;

class Node
{
protected:
    FileSystem *fs;
    int uid;
    string name;
    Directory *parent;
    Node(FileSystem *fs_in, int uid_in, string name_in, Directory *parent_in)
    {
            fs = fs_in;
            name = name_in,
            uid = uid_in;
            parent = parent_in;
    }

    virtual void print_to(ostream os, int num) = 0;

public:
    ~Node() {
        
    }

    // accessors
    string get_name()
    {
        return name;
    }

    // methods
    virtual bool is_directory() = 0;
    virtual Directory *to_directory() = 0;
    virtual File *to_file() = 0;
    virtual int size() = 0;
};

文件.cpp

#include "Node.hpp"
#include "Directory.hpp"
#include "FileSystem.hpp"

class File : public Node
{
private:
    string content;
    ~File() {
        
    }

public:
    File(FileSystem *fs_in, int uid_in, string name_in, Directory *parent_in);

    // accessors
    void set_content(const string &value)
    {
        content = value;
    }
    // mutators
    string get_content()
    {
        return content;
    }
    // methods
    int size()
    {
        return content.size();
    }

    bool is_directory()
    {
        return false;
    }

    Directory *to_directory()
    {
        return nullptr;
    }

    File *to_file()
    {
        return this;
    }

    void print_to(ostream os, int num)
    {
        // os << "+ file: " << name << ", uid: " << uid << ", size: " << size << ", " << "content: " << content;
    }
};
 

目录.cpp

#include "File.hpp"
#include "FileSystem.hpp"
#include "Node.hpp"

class Directory : public Node
{
private:
    Directory(FileSystem *fs_in, int uid_in, string name_in, Directory *parent_in);
    ~Directory();

    // attributes
    vector<Node *> children;
    
    // methods
    bool child_exists(string name)
    {
        for (int i = 0; i < children.size(); i++)
        {
            return children[i]->get_name() == name;
        }
    }

    friend class FileSystem;
public:
    // accessors
    string get_name() {
        return name;
    }
    
    //methods
    int size()
    {
        int sum;

        for (int i = 0; i < children.size(); i++)
        {
            Node *child = children[i];
            sum += child->size();
        }

        return sum;
    }

    bool is_directory()
    {
        return true;
    }

    Directory *to_directory()
    {
        return this;
    }

    File *to_file()
    {
        return nullptr;
    }

    File *add_file(string filename)
    {
        // check whether same name child node exists
        if (child_exists(filename))
            return nullptr;

        // create file
        File *new_file = new File(fs, fs->get_fresh_uid(), filename, this);
        // add file to the children vector
        children.push_back(new_file);
        return new_file;
    }

    Directory *add_directory(string dirname)
    {
        // check whether same name child node exists
        if (child_exists(dirname))
            return nullptr;

        // create file
        Directory *new_dir = new Directory(fs, fs->get_fresh_uid(), dirname, this);
        // add dir to the children vector
        children.push_back(new_dir);
        return new_dir;
    }

    bool remove_node(string name)
    {
        for (int i = 0; i < children.size(); i++)
        {
            if (children[i]->get_name() == name)
            {
                children.erase(children.begin() + i);
                return true;
            }
        }

        return false;
    }

    Node *find_node(string name)
    {
        for (int i = 0; i < children.size(); i++)
        {
            if (children[i]->get_name() == name)
                return children[i];
        }

        return nullptr;
    }

    void print_to(ostream os, int num)
    {
        // os << "+ directory: " << name << ", uid: " << uid << ", size: " << size;
    }

    friend ostream &operator<<(ostream &output, Directory &dir)
    {
        return output;
    }
};

Directory::~Directory()
{
    for (int i = 0; i < children.size(); i++)
    {
        delete children[i];
    }
}

标签: c++g++forward-declaration

解决方案


#include "Directory.cpp"
#include "File.cpp"

我建议不要使用源文件作为标题。传统上,源文件是按惯例编译的,但如果其中一个包含的文件被编译并与“Node.cpp”文件链接,那么由于违反了一个定义规则,您的程序格式错误。

如果我尝试进行前向声明,我将得到类成员的未定义引用。

前向声明永远不应导致“类成员的未定义引用”。你可能误会了什么。

在 c++ 中解决此类问题的正确方法是什么。

简单地说,您需要按照依赖定义在依赖它们的定义之前的顺序对定义进行排序。当且仅当这种排序不存在时,依赖图中存在一个循环,并且无法通过重新排序定义来解决问题。在这种情况下,设计将不得不改变。

Node 类是 Directory 和 File 的父类。我需要将 Node 包含到两个文件中以继承,但在 Node 类实现中我需要访问文件和目录

这听起来像是糟糕的设计。有可能实现,但这并不意味着设计一定是好的。

也就是说,以下顺序有效:

  • 定义节点
  • 定义目录和文件
  • 定义使用 Directory 和 File 的函数

推荐阅读