【问题标题】:C++ casting a derived subclass into another derived subclassC ++将派生子类转换为另一个派生子类
【发布时间】:2021-04-01 16:45:04
【问题描述】:

所以我在这里想要实现的是将派生的子类转换为从同一子类派生的另一个子类。到目前为止,它看起来似乎不可能真正做到,但我仍然相信。

我的示例代码是:

#include <iostream>
#include <vector>

class BaseClass
{
public:
    virtual void printFunction() = 0;
};

class printOne : public BaseClass
{
public:
    void printFunction() override
    {
        std::cout << "One\n";
    }
};

class printTwo : public BaseClass
{
public:
    void printFunction() override
    {
        std::cout << "Two\n";
    }
};

int main()
{
    std::vector<BaseClass *> baseClassVector;

    printOne * one = new printOne;
    baseClassVector.push_back(one);

    printTwo * two = new printTwo;
    baseClassVector.push_back(two);

}

所以我想用这个向量在这里实际做的是我想将索引零上的“一”对象更改为“二”对象。现在这可以通过代码来完成

delete baseClassVector[0];

printTwo * two = new printTwo;
baseClassVector[0] = two;

但据我所知,这是非常昂贵的,尤其是如果它必须在运行时完成。我想知道是否有其他方法可以做到这一点,或者与其他替代方案相比,成本是否值得。

提前致谢!

【问题讨论】:

  • 如果您的类层次结构负担得起,您可以使用CRTP,也称为静态多态性。不会有任何运行时开销,但会限制您构建类层次结构的方式,如前所述。 “但是据我所知,这是非常昂贵的......” 也许比你想象的要少。测量,尝试消除完成动态转换的瓶颈,然后再次测量。
  • struct base {} 会派上用场
  • 请注意,要使delete baseClassVector[0] 正常工作,BaseClass 必须具有虚拟析构函数。如果通过指向基类型的指针删除派生类型的对象,并且该基类型没有虚拟析构函数,则行为未定义。你的例子它可能“工作”得很好。这是未定义行为的一个隐蔽属性:您的代码有时不会显示任何症状,因此您不会注意到问题。然后你做了一个小的改变,事情就开始爆炸了。
  • 在技术上可以转换指针。这种方法的“成本”可能是不可接受的 - 对象的后续使用(由指针指向)会产生未定义的行为。
  • @πάνταῥεῖ 指向基类型的指针向量表明,这里需要真正的动态多态性。

标签: c++ polymorphism derived-class


【解决方案1】:

在技术上可以将对象“重建”为不同的类型,但以下内容应仅视为概念证明,而不是设计或实践的建议。首先要付出的代价是放弃 new/delete 的便利性,以便与 malloc/free 一起使用新的显式析构函数来手动管理放置。

const size_t sz = max(sizeof(printOne), sizeof(printTwo));

BaseClass *either = (BaseClass *)malloc(sz);       // allocate memory for objects

new(either) printOne();                            // construct printOne object
printOne *one = dynamic_cast<printOne *>(either);  // ... use printOne object
one->~printOne();                                  // destruct printOne object

new(either) printTwo();                            // construct printTwo object
printTwo *two = dynamic_cast<printTwo *>(either);  // ... use printTwo object
two->~printTwo();                                  // destruct printTwo object

free(either);                                      // free memory used by objects

【讨论】:

  • 这大概就是std::variant 在场景之外为你所做的事情,至少作为一个开始。
  • @aschepler 在某种程度上,是的,尽管在 OP 的用例中,这与他们提出并要求替代方案的代码并没有太大的不同——我理解这意味着替代方案可以避免(重新)分配时类型改变。
【解决方案2】:

对于问题中的简化示例,使用更简单的std::variant,并且完全避免使用基类:

class printOne
{
public:
    void printFunction() const
    {
        std::cout << "One\n";
    }
};

class printTwo
{
public:
    void printFunction() const
    {
        std::cout << "Two\n";
    }
};

using printEither = std::variant<printOne, printTwo>;

void printFunction(const printEither& e)
{
    std::visit([](auto& p) { p.printFunction(); }, e);
}

int main()
{
    std::vector<printEither> eitherVector;

    printOne one;
    eitherVector.push_back(one);

    printTwo two;
    eitherVector.push_back(two);

    eitherVector[0] = two;

    for (auto& e: eitherVector)
        printFunction(e);
}

在 C++ 中为有效不相关的类型重新使用分配很难正确编写。进行分配更容易也更可取。

【讨论】:

    猜你喜欢
    • 2016-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-08
    • 2013-11-17
    相关资源
    最近更新 更多