【问题标题】:Upcast and Downcast in C++C++ 中的向上和向下转换
【发布时间】:2011-11-22 07:28:18
【问题描述】:
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-&gt;name 复制到 f;条目丢失。


另外,downcast 是如何工作的?

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

dp = dynamic_cast<dp*> (fp);

【问题讨论】:

  • 一个小旁注:如果您打算稍后分配给该指针,则不应分配带有new 的指针,这是内存泄漏。
  • 无赋值,dynamic_cast&lt;&gt; 都不起作用,因为您使用的是私有继承。另外,你有什么问题?
  • @Jaochim 你对delete不是很熟悉吗?

标签: c++ inheritance pointers casting


【解决方案1】:

我会尽量简单地解释一下,但您仍然应该阅读更多关于此的文档。

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】:

    以下是从链接中获取的关于什么是向上转换和向下转换的解释: questionscompiled.com

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

    【讨论】:

      【解决方案3】:

      继承捕获关系“是一个”。鉴于您的问题的上下文,以下内容不是很明智:

      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

      其他人已经提供了更多细节,所以我会在这里停下来,但只要把它扔在那里,以防你发现有用的东西。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-16
        • 2019-03-28
        • 1970-01-01
        • 2015-04-13
        • 1970-01-01
        相关资源
        最近更新 更多