c++中的向上和向下转换

Upcast and Downcast in C++

本文关键字:转换 c++      更新时间:2023-10-16
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;条目丢失


还有,downcast是如何工作的?

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

dp = dynamic_cast<dp*> (fp);

我将尽量简单地解释,但是您仍然应该阅读更多关于这方面的文档。

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

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

directory* dp = new directory;
file* fp ;
fp = dynamic_cast<file*>(dp); 

首先,动态强制转换在这里不起作用,因为这些不是多态类型。如果有,这些就只是指针。由于目录is-a文件,您可以使用文件指针指向目录。对象在相应的位置。但是,您不能通过文件指针访问独占目录成员,除非进行反类型转换。

dp = dynamic_cast<dp*> (fp);

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

下面是关于什么是上型和下型的解释:questionscompiled.com

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

向下类型转换:向下转换是将基类类型的指针或引用转换为其派生类的指针或引用类型,在继承树中向下。这是在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类型的对象或其子类。

继承捕获了"is a"的关系。考虑到你的问题的上下文,下面的话不太明智:

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);

仅仅分别创建一个引用和指针,通过它们,您可以通过在目录和文件对象的基类中指定的接口与它们进行交互。像这样的"向上"(称为"向上",因为您正在向根爬上类层次结构)是相对安全的……而不是你做错了什么。

另一方面,"Downcasting"可能是危险的。我们知道每个directoryfile都可以被视为thing_that_has_a_name…然而,如果你有一个指向thing_that_has_a_name的指针或引用,那么任意代码都不能确定它是directory还是file。有一些方法可以插入条件代码来检查和确定,但这通常被认为是你在设计中做错了的标志:

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

其他人已经提供了更多的细节,所以我就到此为止,但只是把它放在那里,如果你发现一些有用的