[C ++从多重继承进行转换;源不是多态的

问题描述 投票:0回答:1

我是C ++的新手。我有两个纯抽象类(如接口),我从这两个纯抽象类派生了一个类。

在一种情况下,我需要将派生类的指针上移到基本抽象类之一。首先,对此有任何限制。

class IBase1
{
  virtual ~IBase() = default;
}

class IBase2
{
  virtual ~IBase2() = default;
}

class Derived : public IBase, public IBase2
{
}

Derived d;
IBase1* basePtr = dynamic_cast<IBase1*>(&d);

这里,如果我使用任何其他强制类型转换,我不太确定,但是我从强制类型转换中获得了无效的指针,所以我需要使用dynamic_cast从多个继承进行向上转换,对吗?

当我这样做时,出现错误“源类型不是多态的”

我的基类是纯抽象类,因此它们至少具有一个虚拟方法,应该可以,对,但是为什么我会收到此错误?关于多重继承吗?

编辑:这里还有一层。我的派生类需要包含两种不同类型的实例,但是我的实例确实是一个巨大的变量,因此作为C开发人员:),我计划使用Union来减少内存使用量。联合仅具有从纯抽象类派生的两个类的实例。因此,我以为联合实例地址也应指向类实例的偏移量,但是C ++可能无法知道写成员方法的地址。

class IFile
{
public:
  virtual ~IFile() = default;
};

class IDirectory
{
public:
  virtual ~IDirectory() = default;
};

class FileSystem1 : public IFile, public IDirectory
{
public:
  FileSystem1() { }

  virtual ~FileSystem1() final override = default;
private:
   Native1APIInstance file;
};

class FileSystem2 : public IFile, public IDirectory
{
public:
  FileSystem2() { }

  virtual ~FileSystem2() final override = default;
private:
   Native2APIInstance file;
};

union FileSystemInstance
{
  FileSystem1 fs1;
  FileSystem2 fs2;
  FileSystemInstance(string path)
  {
    if (path[0] == '1') // initialise fs1
    else if (path[0] == '2') // initialise fs2
  }
};

FileSystem fs("<PATH to File System>");
IFile* file = reinterpret_cast<IFile*>(&fs);

这里,我不想对初始化哪个实例感兴趣。我只想使用基类接口。我想知道联盟是否有可能?

谢谢。

c++ multiple-inheritance dynamic-cast upcasting
1个回答
0
投票
class FileSystem1 : public IFile, public IDirectory

让我们坐下来,想一会儿。这断言了FileSystem1 is(或更正式地,在任何可能的情况下,都可以代替IFileIDirectory。)>

至少在大多数人使用这些术语时,根本不是这样。正如大多数人使用的术语一样,文件系统contains

某些东西,每个东西都可以是文件或目录:
class FS_node {
    virtual std::string name() const { return name_; }
    // ...
    virtual ~FS_node = default;
};

class File : public FS_node {
    // ...
};

class Directory : public FS_node { 
    // ...
};

class FileSystem { 
    std::vector<FS_node *> nodes;
public:
    // ...
};

现在,从事态发展来看,您必须处理两个完全独立的文件系统。有很多方法可以做到这一点。一种可能性是拥有一个FileSystem基类,该基类定义了文件系统的接口,然后具有两个派生类,这些派生类根据操作系统级别的两个独立API来实现该接口。

另一种可能性是实现类似的功能,但是在实例化FileSystem对象时,您可以将API接口类指定为模板参数,而不是使用继承:

template <class Api>
class FileSystem {
    Api api;
public:
    FileSystem(Api const &api) : api(api) {}

    // FS functions for finding files and such go here,
    // each implemented via the `Api` passed as a parameter
};

关于使用模板和继承之间的区别:与通常的用法几乎相同:模板是静态的,因此,例如,如果您想要在编译时可以指定是针对Windows还是Linux进行编译的代码,则模板应做得好。另一方面,如果要处理的是单个文件系统集合,并且该集合可能包含对象的混合物,则每个对象代表一个不同的文件系统,并且您希望能够透明地处理所有这些对象在运行时,那么您可能需要使用继承层次结构。

但是至少对我来说,您从FileSystemFile类派生的Directory原始设计似乎很可能是一个非常严重的错误。我们可能需要更多地了解您在做什么,以确保哪种方法真正是最佳的,但是事实并非如此。

暂时忽略所有有关设计的内容,并考虑如何从指针到派生转换为指向基的问题,我们实际上只有两种情况非常重要。如果您使用公共继承(如问题所示),则该转换完全不需要强制转换:

FileSystem1 foo1;

IFile *fileBase = &foo1;      // No problem.
IDirectory *dirBase = &foo1;  // Likewise

如果使用私有派生,那么您刚刚发现了一种极为罕见的情况:您实际上需要使用C样式强制转换正确地进行转换:

class Base1 {};
class Base2 {};

// Note: private derivation: 
class Derived : Base1, Base2 {};

Derived d;
Base1 *b1 = (Base1 *)&d;
Base2 *b2 = (Base2 *)&d;

对于这种特定情况(将派生类转换为不可访问的基类),任何“新” C ++强制类型转换都不能胜任—您必须

我们使用C风格的强制转换。
© www.soinside.com 2019 - 2024. All rights reserved.