Your existing construct does not require Common "know your subclass Folder ". It just requires the Common header to declare that there is a class like 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; };
There is no dependency loop.
If for some academic reason you should have a base class that doesn't even specify any subclass, then you can make a polymorphic base class as follows:
class Node { string m_name;
With this design, however, the setParent(Node* parent) method will have to enable runtime checking that the Node * parent argument is actually Folder * , using, for example,
Folder *pf = dynamic_cast<Folder *>(parent);
and in this case, it will also require a non-empty type of return, indicating success or failure. This is a tortuous, not just a declaration of class Folder .
CONTINUED to answer the question about the next steps.
Inside Common setParent() I need to call Folder m_children; leading to an error. Even if I include folder.h in common.cpp, I canโt access private private folders. Any ideas?:
My previous answer was limited to showing that โmaking the system code impossible for you as you plannedโ does not actually.
The problem that you see now is that setting some folder f as the parent of some node n not an independent operation in node (file or folder). f can actually become the parent of n if n simultaneously becomes one of the children of f . So in n.setParent(parent) , while setting n.mParent == parent you want to add n to parent->m_children ; but m_children not available for node n .
This problem is a heavy hint of your design. If the parent-and-extra-child parameter should always meet together, then they actually perform the same operation - set-parent-add-child - is simply called in different ways: from the parent or from the child. If there is a reason for Common provide setParent(Folder *) , then there is equally good reason for Folder provide addChild(Common *) , and they should do the same.
Does this mean that, say, static void Common::link(Folder * parent, Common * child) could better publicly replace both of them? May be so; but you started with Common::setParent(Folder *) , which was reasonable; therefore matching with Folder::addChild(Common *) also reasonable, and then we can get them to do the same, calling each other.
We also believe that since pCommon->setParent(pFolder) is equivalent to pFolder->addChild(pCommon) , you also need means to remove the node from its parent; because before you can a node parents you must remove it from the existing parent, if any. And also this operation is likely to be a boon to the client code; so Folder::removeChild(Common *) also a natural complement to Folder .
Folder::addChild(Common * pnode) and Folder::removeChild(Common * pnode) are the interfaces that you lack to manage the private member of Folder::m_children .
Then, consider that each of these methods should determine if pnode is pnode a child of a folder: you should not add a child to a folder where it is already a child, and you cannot delete a child that is not one of them. Thus, Folder::find(Common * pnode) will also be useful - at least for implementation (private), and probably also client code (public): you can decide.
Then consider that Folder::find(Common * pnode) calls another method: bool Common::operator==(Common const & other) . Let me just say that nodes are equal if they have the same name.
Common::clearParent() also comes to mind, but I will put it aside.
These ideas are sufficient for the next implementation, which is incomplete, suboptimal and impractical, but shows how you can combine the points that we have just identified to go through the barrier to access the member that is still stopping you. This is impractical because it ignores the whole issue of ownership of dynamic objects, which are supposed to be addressed by the arguments of the Folder * and Common * methods. You can handle it yourself (and you can investigate std :: shared_ptr and std :: unique_ptr , even if it is more that you should use in this project).
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 {
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 program:
#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);