【问题标题】:Is it possible to creat a derived object from a base object through downcast?是否可以通过向下转换从基础对象创建派生对象?
【发布时间】:2015-10-31 19:42:23
【问题描述】:

基类

class Base
{
public:
    Base()=default;
    virtual void f(){cout << "base class\n";}
    virtual ~Base(){}
};

派生类

class Derive : public Base
{
public:
    Derive()=default;
    void f() override {cout << "derived class\n";}
};

主要功能

int main()
{
    Base* bsd = new Base;
    Derive* dru = new Derive;

    if(Derive* drd=dynamic_cast<Derive*>(bsd)){
        drd->f();
        cout << "downcast successful\n";
    }else{
        cout << "downcast failed\n";
    }

    if(Base* bsu=dynamic_cast<Base*>(dru)){
        bsu->f();
        cout << "upcast successful\n";
    }else{
        cout << "upcast failed\n";
    }

    delete bsd;
    delete dru;
}

事实证明向上转换工作正常,而向下转换失败。这样看来似乎有道理。如果派生类包含未在基类中声明的成员对象并且没有默认构造函数,那么在向下转换期间会发生什么?

此外,通过dynamic_cast创建的目标指针*drd(*bsd)指向与要转换的指针*bsu(*dru)相同的对象。所以删除一次就足够了。我们不会有一个悬空指针,对吧?

【问题讨论】:

  • 静态转换驱动类而不是动态

标签: c++ dynamic-cast


【解决方案1】:

铸造不会以任何方式创建任何新内容或更改对象。强制转换改变了对现有对象的解释,因此如果一个对象不是Derive,则无法通过强制转换使其成为Derive

请注意,Derive 也是 Base,因为继承会创建“是”关系。这就是向上转换起作用的原因:它所做的只是告诉编译器它应该将指向的对象视为Base,即使该对象实际上是Derive

这对你的程序没有影响,所以这里是演员做什么的说明:

class Derive : public Base
{
public:
    Derive()=default;
    void f() override {cout << "derived class\n";}
    void added() {cout << "hello" << endl; }
};

dru->added(); // Works
Base* bsu=dynamic_cast<Base*>(dru);
bsu->added(); // Does not compile

本质上,强制转换“隐藏”了编译器添加的接口,命令它将Derive 对象视为Base。当然,覆盖会继续正确调用,因为覆盖的成员函数是Base 接口的一部分。

【讨论】:

  • @das 从这个意义上说,它有点像切片。但是,如果两个指针指向同一个对象,作为一个指针没有类型,为什么编译器无法识别bsu-&gt;added()
  • @das 更重要的是,如果我将dynamic_cast 更改为static_cast,upcast 部分中的bsu-&gt;added() 仍然无法编译,而在downcast 过程中drd-&gt;added() 工作正常!就像在此过程中创建了一个新对象。这是为什么呢?
  • @MiloLu 指针确实有一个类型——这决定了编译器是否允许执行某些操作。 Upcast/downcast 是一种表达方式。它产生另一个指针,而不改变原来的指针。强制转换 drd 不会改变您对 drd 的操作,因为它的类型和值都不会改变。
【解决方案2】:

我不确定你的问题是否正确,但是,如果析构函数是 virtual,你可以使用基指针安全地删除派生类。

“悬空指针”有所不同:删除bsddru 后,您不能再使用这些指针,它们已成为悬空指针,因为它们指向已删除的不属于您的内存没有了。

【讨论】:

  • @alain 悬空指针超出范围会自动清理,对吧?
  • 是的,指针的生命周期在它们超出范围时结束,它们指向的对象的生命周期以delete结束。
【解决方案3】:

dynamic_cast 检查运行时类型检查并提供安全转换。如果可以进行有意义的转换,那么您将在向下转换后得到一个有效的对象。正如您所指出的,在您的示例中,基础对象是“基类”。运行时类型检查失败。但是,如果基础对象属于“派生类”,那么 dynamic_cast 就会成功。例如:

class CBase
{
public:
    CBase(void);
    virtual ~CBase(void);

    virtual void identify() 
    {
        std::cout << "base class" << std::endl;
    }
};

class CDerieved : public CBase
{
public:
    CDerieved(void);
    virtual ~CDerieved(void);
    virtual void identify()
    {
        std::cout << "Derieved class" << std::endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    CDerieved* pderieved = new CDerieved;
    pderieved->identify();

    CBase* pb = static_cast<CBase*>(pderieved);
    pb->identify();

    CDerieved* pd1 = dynamic_cast<CDerieved*>(pb);
    pd1->identify();

    return 0;
}

上面的代码会成功。

但请记住,如果您发现需要低调,则需要修改设计。

【讨论】:

    猜你喜欢
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-05
    • 1970-01-01
    • 2015-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多