我正在尝试创建类似于大多数操作系统中使用的文件夹/文件系统。
基本上我已经发现我应该使用三个班级; File
,Folder
和公共基类。我们称之为Common
是为了创造力。
以下是我认为这三者的标题应如下所示:
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;
};
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
};
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
,这是不好的行为,不应该这样做(至少根据我的老师的说法) )。
这使我无法像我原先计划的那样对系统进行编码。
如何设计这样的系统?
答案 0 :(得分:7)
您现有的设计不要求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
实际上是a的运行时检查
Folder *
,使用例如
Folder *pf = dynamic_cast<Folder *>(parent);
在这种情况下,它还需要一个非void返回类型
表示成功或失败。这是曲折的,而不仅仅是制造一个
声明class Folder
。
续以解决OP的后续问题。
Inside Common
setParent()
我必须调用Folder的m_children;这会导致错误。即使我在common.cpp中包含folder.h,我也无法访问文件夹的私有成员。有任何想法吗? :
我之前的回答仅限于向您展示“使您无法按照计划对系统进行编码” 实际上并没有。
您现在看到的问题是将某个文件夹f
设置为某些文件夹的父级
节点n
不是节点(文件或文件夹)上的独立操作。 f
只能
如果n
同时成为其中之一,则有效地成为n
的父级
f
的孩子。因此在n.setParent(parent)
中,与设置同时进行
n.mParent == parent
您希望将n
添加到parent->m_children
;
但节点m_children
无法访问n
。
此问题是您设计的重要提示。如果设置父母和添加孩子
必须始终一起出现然后它们实际上是相同的操作--set-parent-add-child
- 只是以不同方式调用:来自父级或来自子级。如果有的话
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 *
参数解决。
你可以自己处理(你可能希望调查
std::shared_ptr和
std::unique_ptr,即使
这些更多
比你应该在这个项目中使用的先进设施。)
<强> COMMON.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
<强> folder.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
<强> file.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
<强> common.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;
}
答案 1 :(得分:0)
您不能将文件夹放在公共文件夹中并在文件夹中进行公共继承;这是一个循环冗余错误。
此系统只能设计为两个类:文件和文件夹,但如果要进行概括,请从常用文件中删除文件夹。