C ++中的Upcast和Downcast

时间:2011-11-22 07:28:18

标签: c++ inheritance pointers casting

class file {
    std::string name;
};

class directory : public file {
    std::vector<file*> entries;
};

directory d;
file f;

f = d; // Only d.name is copied to f; entries are lost

如果我们这样做:

directory* dp = new directory;
file* fp ;

fp = dynamic_cast<file*>(dp); 

将条目和标识保留为目录对象,或仅将dp->name复制到f;条目丢失。


另外,垂头丧气有什么作用?

如果我们这样做会发生什么:

dp = dynamic_cast<dp*> (fp);

3 个答案:

答案 0 :(得分:3)

以下是关于从链接获取的向上铸造和向下铸造的说明: questionscompiled.com

向上转型: Upcast是将派生类类型的指针或引用转换为基类类型的指针或引用,在继承树中上升。 Upcast有助于在C ++中实现接口概念。当在基类类型指针或引用(指向或引用其某些派生类)上调用函数时,将调用该派生类的正确成员函数。

向下转换: Downcast是将基类类型的指针或引用转换为其派生类的指针或引用类型,在继承树中向下转换。这是在dynamic_cast运算符的帮助下实现的,该运算符在运行时安全地向下转换。

以下是一个例子:

class A
{public:
  int i;
  virtual void show(){}
};

class B:public A
{
public:
  int j;
  virtual void show()  { cout<<"B";  }
};

int main()
{
  A* ptr=new B; // UPCASTING
  B* ptrb;
  ptrb=dynamic_cast<B*>(ptr); //DOWNCASTING to derived object
  ptrb->show(); // upcasting helped in implementing interface
}

参考上面的例子,dynamic_cast确保ptr指向B类或其子类的对象。

答案 1 :(得分:2)

我会尝试简单解释一下,但您仍应阅读更多相关文档。

directory d;
file f;

f = d; // Only d.name is copied to f; entries are lost

这是因为目录和文件的类型和大小不同。文件的大小为sizeof(file),目录为sizeof(directory),大于sizeof(file)。即使是原始内存副本也只会将目录的文件部分复制到文件对象,或者如果您打算复制更多内容,则会给出未定义的行为。

directory* dp = new directory;
file* fp ;

fp = dynamic_cast<file*>(dp); 

首先,动态强制转换在这里不起作用,因为它们不是多态类型。如果是这样,这些只是指针。从目录is-a文件开始,您可以使用文件指针指向目录。对象位于相应位置。但是,除非您退回,否则您无法通过文件指针访问独占目录成员。

dp = dynamic_cast<dp*> (fp);

您正在重新获得目录成员对原始指针的访问权限,如上所述。

答案 2 :(得分:1)

继承捕获关系“是一个”。鉴于你的问题的背景,以下是不明智的:

class directory : public file

仅因为目录和文件都有名称并不意味着directory "is a" file。说file "is a" directory

也是不恰当的

你可以在这里进行许多其他的推理。例如,您可以争辩说文件和目录都是thing_that_has_a_name

的情况
class thing_that_has_a_name {
    std::string name;
    /* ... */
};

class file : public thing_that_has_a_name {
    /* ... */
};

class directory : public thing_that_has_a_name {
    std::vector<file*> entries;
    /* ... */
};

(这是实现它的一种方式,但继承是一种相当严厉的机制。如果你正在为“有名字”的抽象概念之类的东西创建基类,那么你很快就会发现自己需要从多个基类继承。)

要重新考虑到您所关注的技术方面,您必须记住的是,创建新对象之间存在很大差异现有文件或目录对象为thing_that_has_a_name 。如果您曾做过如下声明:

directory d;
file f;

这些是实例化。构造函数运行。无论sizeof目录是什么,这就是d只获得了多少内存。无论sizeof文件是什么,这都是f刚才得到的。但是:

thing_that_has_a_name& tthan_ref_to_d (d);
thing_that_has_a_name* tthan_ptr_to_f (&f);

这些只是分别创建一个引用和指针,通过它们可以通过它们的基类中指定的接口与目录和文件对象进行交互。像这样的“向上传播”(称为“向上”,因为你将类层次结构推向根)是相对安全的......而不是表明你做错了。

另一方面,“向下倾斜”可能很危险。我们知道每个directoryfile都可以视为thing_that_has_a_name ...但如果您有指向thing_that_has_a_name的指针或引用,那么任意代码都不能确保它是directoryfile。有一些方法可以插入条件代码进行检查并确定,但这通常被认为是您在设计中出错的一个迹象:

http://en.wikipedia.org/wiki/Run-time_type_information

其他人已经提供了更多细节,所以我会停在这里,但是如果你发现有用的东西,就把它扔掉。