设计文件夹/文件系统
Designing folder/file system?
我正在尝试创建一个类似于大多数操作系统中使用的文件夹/文件系统。基本上我已经发现我应该使用三个类; File
、Folder
和公共基类。为了创造力,让我们称之为Common
。以下是我认为这三个标题应该是什么样子的:
普通.h
class Common {
string m_name; // all files and folders have a name
Folder* m_parent; // all files and folders can have a parent
public:
virtual void open() = 0; // executed when folder or file is opened by user
virtual void draw() const; // all files and folders can be printed
virtual void setParent(Folder* parent);
virtual Folder* getParent() const;
};
文件夹.h
class Folder : public Common {
vector<Common*> m_children; // folders can contain other files and folders
// whereas files cannot
public:
virtual void open(); // folder opens; basically shows the content
virtual void draw() const; // folder draws differently
};
文件.h
class File : public Common {
// not really "files", they just call a function when opened
funcptr m_openAction;
public:
virtual void open(); // calls m_openAction() when opened
};
如您所见,问题是我的基类Common
应该能够知道它是子类Folder
,这是不良行为,不应该这样做(至少根据我的老师的说法(。这使我无法像计划的那样对系统进行编码。
这样的系统应该如何设计?
您现有的设计不需要Common
"知道其子类Folder
"。它只需要 Common
标头来声明存在一些这样的类如Folder
:
class Folder; // Forward declaration
class Common {
string m_name; // all files and folders have a name
Folder* m_parent; // all files and folders can have a parent
public:
virtual ~Common(); // Don't forget virtual destructor!
virtual void open() = 0; // executed when folder or file is opened by user
virtual void draw() const; // all files and folders can be printed
virtual void setParent(Folder* parent);
virtual Folder* getParent() const;
};
这里没有依赖循环。
如果由于某种学术原因,您必须拥有一个甚至没有的基类提到任何子类,那么你可以像这样制作多态基类:
class Node {
string m_name; // all files and folders have a name
Node* m_parent; // all files and folders can have a parent
public:
virtual ~Node(); // Don't forget virtual destructor!
virtual void open() = 0; // executed when folder or file is opened by user
virtual void draw() const; // all files and folders can be printed
virtual void setParent(Node* parent);
virtual Node* getParent() const;
};
然而,通过这种设计,setParent(Node* parent)
方法将具有包括运行时检查Node *
参数parent
实际上是 Folder *
,例如使用
Folder *pf = dynamic_cast<Folder *>(parent);
在这种情况下,它还需要一个非 void 返回类型,通过该类型来指示成功或失败。这是曲折的,而不是仅仅做一个class Folder
声明 .
继续解决OP的后续问题。
在Common的
setParent()
中,我必须调用Folder的m_children;这会导致错误。即使我包含 folder.h 共同.cpp,我也无法访问文件夹的私有成员。有什么想法吗?:'
我前面的回答仅限于向您展示"是什么让你无法像你计划的那样编写系统代码"其实不然。
您现在看到的问题是将某些文件夹设置为某些文件夹f
某些文件夹的父文件夹节点n
不是节点(文件或文件夹(上的独立操作。 f
只能有效成为n
的父级,如果n
同时成为f
的孩子 .因此在n.setParent(parent)
,同时设置 n.mParent == parent
您希望将n
添加到parent->m_children
;但是节点n
无法访问m_children
。
这个问题对你的设计来说是一个沉重的提示。如果设置父项和添加子项必须始终一起发生,然后它们实际上是相同的操作 - 设置父添加子- 只是调用方式不同:来自父母或来自孩子。如果有Common
提供setParent(Folder *)
的理由同样好Folder
提供addChild(Common *)
的原因,他们都必须做同样的事情。
这是否表明,比如说,static void Common::link(Folder * parent, Common * child)
也许最好公开替换它们?也许是这样;但你一开始是 Common::setParent(Folder *)
,这是合理的;所以与Folder::addChild(Common *)
匹配也是合理的,然后我们可以通过相互调用来让他们做同样的事情。
那么,还要考虑一下,既然pCommon->setParent(pFolder)
是
相当于pFolder->addChild(pCommon)
,还需要手段从其父节点中删除节点;因为在您可以有效添加之前父节点的节点 必须将其从其现有父节点(如果有(中删除。和此操作很可能对客户端代码有利;所以 Folder::removeChild(Common *)
也是对 Folder
界面。
Folder::addChild(Common * pnode)
和Folder::removeChild(Common * pnode)
是您缺少用于管理私有成员的接口Folder::m_children
。
接下来,考虑这些方法中的每一个都必须确定pnode
是否实际上是文件夹的子文件夹:不得添加子项到已是子项的文件夹,并且无法删除不是子项的子项。所以Folder::find(Common * pnode)
也很有用 - 至少对实施(私有(,并且合理地也对客户端代码(公共(:您可以决定。
然后,考虑Folder::find(Common * pnode)
要求另一种方法:bool Common::operator==(Common const & other)
。这么说吧如果节点具有相同的名称,则它们是相等的。
Common::clearParent()
也浮现在脑海中,但我会把它放在一边。
这些想法足以用于以下实现,即不完整、次优和不切实际,但展示了如何连接我们拥有的点刚刚确定以便通过成员访问障碍,即还在阻止你。这是不切实际的,因为它忽略了假定的动态对象的所有权的整个问题通过其方法的Folder *
和Common *
参数来解决。您可以自己处理(您可能希望进行调查(标准::shared_ptr 和std::unique_ptr,即使这些是更多比您应该在此项目中使用的先进设施(。
普通.h
#ifndef COMMON_H
#define COMMON_H
#include <string>
#include <iostream>
class Folder;
class Common {
std::string m_name;
Folder* m_parent;
public:
explicit Common(std::string const & name)
: m_name(name),m_parent(nullptr){}
virtual ~Common(){};
virtual void open() { /*Whatever*/}
virtual void draw() const {/*Whatever*/}
virtual Folder* getParent() const { return m_parent; };
virtual void setParent(Folder* parent);
bool operator==(Common const & other) const {
return m_name == other.m_name;
}
bool operator!=(Common const & other) const {
return !(*this == other);
}
#if 1 // Testing
std::string const & name() const {
return m_name;
}
std::string parent() const;
virtual void list() const {
std::cout << name() << " (in " << parent() << ')' << std::endl ;
}
#endif
};
#endif // EOF
文件夹.h
#ifndef FOLDER_H
#define FOLDER_H
#include "common.h"
#include <vector>
class Folder : public Common {
std::vector<Common *> m_children;
std::vector<Common *>::iterator find(Common const * child) {
auto i = m_children.begin();
for ( ;i != m_children.end() && **i != *child; ++i) {}
return i;
}
public:
explicit Folder(std::string const & name)
: Common(name){}
virtual void open(){/*Whatever*/}
virtual void draw() const {/*Whatever*/}
void addChild(Common * child) {
auto par = child->getParent();
if (par && par != this) {
par->removeChild(child);
}
if (find(child) == m_children.end()) {
m_children.push_back(child);
m_children.back()->setParent(this);
}
}
void removeChild(Common const * child) {
auto where = find(child);
if (where != m_children.end()) {
m_children.erase(where);
}
}
#if 1 // Testing
void list() const {
std::cout << name() << " {" << std::endl;
for (Common const * child : m_children) {
child->list();
}
std::cout << '}' << std::endl;
}
#endif
};
#endif //EOF
文件.h
#ifndef FILE_H
#define FILE_H
#include "common.h"
class File : public Common {
// Whatever
public:
explicit File(std::string const & name)
: Common(name){}
virtual void open(){/*Whatever*/};
};
#endif // EOF
常见.cpp
#include "common.h"
#include "folder.h"
void Common::setParent(Folder* parent) {
auto par = getParent();
if (par && par != parent) {
par->removeChild(this);
}
m_parent = parent;
m_parent->addChild(this);
}
#if 1 // Testing
std::string Common::parent() const {
return m_parent ? m_parent->name() : "<null>";
}
#endif
测试程序:
#include "common.h"
#include "folder.h"
#include "file.h"
int main()
{
Folder *fo0 = new Folder("folder0");
File * fi0 = new File("file0");
File * fi1 = new File("file1");
fo0->addChild(fi0);
fi1->setParent(fo0);
fo0->addChild(fi0); // Duplicate
fi1->setParent(fo0); // Duplicate
// There are now 2 files in folder fo0
fo0->list();
Folder *fo1 = new Folder("folder1");
fo1->addChild(fi1);
fi0->setParent(fo1);
fo1->addChild(fi1); // Duplicate
fi0->setParent(fo1); // Duplicate
// There are now 0 files in folder fo0
// There are now 2 files in folder fo1
fo0->list();
fo1->list();
delete fo0;
delete fo1;
delete fi0;
delete fi1;
return 0;
}
您不能将文件夹放在 common 中并在文件夹中继承 common;这是一个循环冗余错误。
这个系统只能设计为两个类:文件和文件夹,但如果你想进行泛化,从common中删除文件夹。
- 文件系统:复制功能的速度秘诀是什么
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 如何传递多个 std::文件系统选项?
- 遍历顺序由 std::文件系统directory_iterator给出
- libstdc++ 文件系统中未初始化的用法?
- boost::文件系统::recursive_directory_iterator多线程安全
- C++17 文件系统::remove_all 带有通配符路径
- 使用 Powershell 命令将 cpp 文件的文件夹编译为 GNU 的 g++
- 实验性文件系统库不完整C++?
- 使用文件系统时仍然需要链接到带有 C++20 的 stdc++fs?
- 无法链接文件系统库C++
- 如何识别项目是 QT 中的文件还是文件夹
- 使用Boost文件系统C++将具有特定扩展名的文件的名称保存在特定文件夹中
- 正在筛选Boost文件系统中的文件夹
- 卸载程序不会删除复制到VS2010安装程序项目中的系统文件夹。
- C++/CLI:文件系统观察器移动或复制非空文件夹
- 在 C++ 中使用 Linux 系统调用以及文件和文件夹管理
- 如何使用C/C++以编程方式检查窗口中的系统文件夹
- 设计文件夹/文件系统
- 如何使用c++获得文件夹/目录名称,但不是一个文件的路径?尤其是提高:文件系统;