我正在尝试创建一个类似于大多数操作系统中使用的文件夹/文件系统.基本上我已经发现我应该使用三个班级; 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;
};
Run Code Online (Sandbox Code Playgroud)
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
};
Run Code Online (Sandbox Code Playgroud)
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
};
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,问题是我的基类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;
};
Run Code Online (Sandbox Code Playgroud)
这里没有依赖循环.
如果由于某些学术原因你必须有一个甚至没有提到任何子类的基类, 那么你可以像这样制作多态基类:
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;
};
Run Code Online (Sandbox Code Playgroud)
然而,使用这种设计,该setParent(Node* parent)方法将必须包括运行时检查,该Node *参数parent实际上是a
Folder *,使用例如
Folder *pf = dynamic_cast<Folder *>(parent);
Run Code Online (Sandbox Code Playgroud)
在这种情况下,它还需要一个非空返回类型来表示成功或失败.这是曲折的,而不仅仅是宣言class Folder.
继续讨论OP的后续问题.
在Common的内部,
setParent()我必须调用Folder的m_children; 这会导致错误.即使我在common.cpp中包含folder.h,我也无法访问文件夹的私有成员.有任何想法吗?:"
我的上述答案仅限于向您展示"使您无法像您计划的那样对系统进行编码"实际上并非如此.
您现在看到的问题是将某个文件夹设置f为某个节点的父节点n不是节点(文件或文件夹)上的独立操作.如果同时成为其中一个孩子,f只能有效地成为父母.因此,在同一时间设置
要添加到; 但是节点无法访问.nnfn.setParent(parent)n.mParent == parentnparent->m_childrenm_childrenn
这个问题是您设计的重要提示.如果set-the-parent和add-the-child必须总是一起出现,那么它们实际上是相同的操作--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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
测试程序:
#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;
}
Run Code Online (Sandbox Code Playgroud)