C ++中的多态性。在基类上调用继承的方法

时间:2017-06-29 17:54:00

标签: c++ inheritance

我正在尝试实现一个简单的文件系统。我有一个基类条目。我有两个类继承自名为File和Directory的条目。目录包含Entry对象列表。我有一个名为changeDir的方法,用于目录和文件,但不是条目。我希望我可以在Entry对象上调用此方法,编译器将根据Entry是目录还是文件知道使用哪种方法。有什么方法可以实现这个目标吗?

class Entry{
  public:
    std::chrono::time_point<std::chrono::system_clock> dateCreated;
    std::string fileName;
    Entry(std::string name);
    ~Entry();
};

Directory* Directory::changeDir(Directory* dir, Directory* currentDir){
  currentDir = dir;
  return currentDir;
}

void File::changeDir(Directory* dir, Directory* currentDir){
  std::cout<<"You cannot change directories into a file\n";
}

unordered_set<Entry*> s;
s.add(File);
s.add(Directory);
for each in s: each.changeDir(); //pseudo code

不幸的是,它告诉我还没有为Entry类声明changeDir()方法。我通常只为Entry类创建一个方法,但它表示Directory未在此范围内声明(显然,因为Directory是Entry的派生类)。我觉得我以前在python中做过这种类型的多态。在C ++中没有办法吗?谢谢。

编辑:Set接受指向Entry对象的指针,而不是实际的Entry对象。

Edit2:虚拟类似乎是解决这个特定问题的方法。我相信更好更强大的解决方案是让每个目录包含一组文件和一组directorys,而不是一组两者。

4 个答案:

答案 0 :(得分:4)

您需要virtual methods

struct Entry
{
    virtual void changeDir( )
    {
        std::cout << "Entry" << std::endl;
    }
};

struct File : Entry
{
    virtual void changeDir( ) override
    {
        std::cout << "File" << std::endl;
    }
};

struct Directory : Entry
{
    virtual void changeDir( ) override
    {
        std::cout << "Directory" << std::endl;
    }
};

但是,如果您的收藏品是普通Entry个对象,slicing会破坏您所有的辛勤工作:

int main()
{
    std::vector< Entry > entries;

    entries.push_back( File { } );
    entries.push_back( Directory { } );

    entries[ 0 ].changeDir( ); // > Entry
    entries[ 1 ].changeDir( ); // > Entry

为了保存正确的对象,你必须动态分配它们并存储某种指针:

    std::vector< std::unique_ptr< Entry > > entryPointers;

    entryPointers.push_back( std::make_unique< File >( ) );
    entryPointers.push_back( std::make_unique< Directory >( ) );

    entryPointers[ 0 ]->changeDir( ); // > File 
    entryPointers[ 1 ]->changeDir( ); // > Directory
}

有些观点:

  • 覆盖时不要忘记override specifier - 这将确保您真正覆盖基类方法而不只是声明一个新方法;
  • 您可以使用原始指针而不是unique_ptr,但强烈建议不要这样做,因为您必须自己释放它们以避免内存泄漏;
  • 虽然这有效,但建议尽可能避免接口中的虚方法。例如,请参阅此基本Herb Sutter's article

答案 1 :(得分:1)

您可以使方法changedir成为纯虚函数,然后通过实例化基类对象的指针或引用(将指向文件或目录)然后您可以实现所需的行为调用函数,通过多态,它将被正确调用。 [对象切片]

答案 2 :(得分:1)

只要你只引用指针或对Directory的引用,你就可以直接声明它:

class Directory;
class Entry {
  ...
  virtual Directory* changeDir(Directory* dir, Directory* currentDir) = 0;
};

答案 3 :(得分:1)

我认为如果你想在整个集合上调用changeDir,你应该确保所有元素都是Directory的实例(因为它只对它们有意义),并将集合声明为包含目录,而不是条目。

另一方面,如果您的集合可能包含两种类型的条目,则不应该首先尝试在它们上调用可能未定义的方法。