繁体   English   中英

是否可以自动转换为大多数派生类型?

[英]Is it possible to automatically cast to most derived type?

我编写了一个内存扫描应用程序,其中的“模式”由任意模板化的“单元”组成。 单元类也派生自非模板化父包装器类(因此我可以将单元指针存储在向量中)。

如何显示不在父类中的模板化单元成员而无需手动向下转换?

我想做类似auto unitT = most_derived_cast(unitW);

这是示例代码:

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

class Wrapper {
public:
    virtual void* get1() const = 0;
};

template<typename T>
class Derived : public Wrapper {

    T m_value;

public:
    Derived(T value) : m_value{ value } {}

    void* get1() const override {
        return new T(m_value);
    }

    T get2() const {
        return m_value;
    }
};


int main(int argc, char** argv) {

    vector<shared_ptr<Wrapper>> wrapped;

    for (int i = 0; i < 10; i++) {
        wrapped.emplace_back(new Derived<int>(i));
        wrapped.emplace_back(new Derived<float>(i));
    }

    for (const shared_ptr<Wrapper>& w : wrapped) {

        cout << *w->get1() << " ";                      // bummer; can't dereference from void*

        auto v = w->get2();                             // bummer; w is of type Wrapper

        if (dynamic_pointer_cast<Derived<int>>(w)) {    // bummer; have to manually cast
            cout << *(int*)w->get1() << " ";
            cout << dynamic_pointer_cast<Derived<int>>(w)->get2() << " ";
        }
        else if (dynamic_pointer_cast<Derived<float>>(w)) {
            cout << *(float*)w->get1() << " ";
            cout << dynamic_pointer_cast<Derived<float>>(w)->get2() << " ";
        }
        else {
            cout << "bummer "; // bummer; forgot to implement this...
        }
    }

    system("PAUSE");

    return 0;
}

C ++是否可以自动转换为大多数派生类型?

不可以。 通常,当人们认为自己需要此功能时,他们需要的是虚拟功能。

如何显示不在父类中的模板化单元成员而无需手动向下转换?

解决方案示例:

添加一个新的虚拟函数以插入流中:

class Wrapper {
public:
    virtual std::ostream&
    stream_insert(std::ostream& os) const = 0;
// ...
};

使Wrapper流式传输,并委派给虚函数:

std::ostream&
operator<<(std::ostream& os, const Wrapper& w) { 
    return w.stream_insert(os);
}

在派生类中实现该功能:

std::ostream&
stream_insert(std::ostream& os) const override {
    return os << m_value;
}

现在,您可以将Wrapper实例插入字符流:

for (const shared_ptr<Wrapper>& w : wrapped) {
        std::cout << *w;
}

是否可以自动转换为大多数派生类型?

正如eeonika的回答所提到的,不,不是。

一般而言,仅给出Wrapper的声明(或什至是Wrapper的定义),实现就没有有关从中派生出哪些类的信息。 单独的编译模型(允许源文件被独立编译并链接在一起)意味着编译器不了解在不同编译单元中定义的派生类。

但是,您的主题行实际上是一个错误的问题。 实际上,您已经提供了“ XY问题”的演示,其中您尝试执行“ X”,认为“ Y”将是一个解决方案,因此您已经询问了如何执行“ Y”-在您的情况是没有解决方案的问题,这会阻止人们为您提供帮助。

将来,在提出问题时,请尝试正确地描述您的REAL问题(“ X”),最好不要考虑您要考虑的解决方案的描述(“ Y”)。

幸运的是,很少有人问如何解决“ XY问题”,而实际上却不愿在问题的正文中包含对REAL问题的描述。 这意味着可以提供帮助,我现在将尝试。

如何显示不在父类中的模板化单元成员而无需手动向下转换?

再次如eeroniko所述,实际的解决方案是提供适当的虚拟功能。

就您而言,实际的问题是您的Wrapper

 class Wrapper { public: virtual void* get1() const = 0; }; 

提供了一个虚函数,但是当在main()使用该函数时,该函数没有提供足够的信息来使使用正常工作

 cout << *w->get1() << " "; // bummer; can't dereference from void* 

要解决此问题,需要执行一些步骤。

第一步,更改Wrapper以使虚拟函数不会返回void *

virtual Wrapper* get1() const = 0;

第二步,声明一个operator<<() ,该operator<<()允许将Wrapper输出到流而无需更改该对象。

std::ostream &operator<<(std::ostream &, const Wrapper &);

(可选)可以将此函数声明为Wrapper的朋友。 const代表正常的期望,即将对象输出到流不会改变它。 通过引用传递两个参数(每个参数上的& )很重要。

第三步,提供Wrapper另一个虚拟功能,可用于执行输出;

virtual void Output(std::ostream &) const;

第四步在模板类Derived ,覆盖两个virtual函数。

template<typename T> class Derived : public Wrapper
{
     T m_value;

     public:
        Derived(T value) : m_value{ value } {}

        Wrapper * get1() const override
        {
            return this;    //  note that this does not create a clone
        };

        virtual void Output(std::ostream &s) const override
        {
            s << m_value;
        };           

};

请注意,我已经更改了get1()因此它返回了当前对象的地址。 您的实现动态创建了一个克隆,这会导致内存泄漏(除非调用者采取特定步骤来释放动态分配的对象)。

第五步,定义先前声明的operator<<()以便调用虚函数

std::ostream &operator<<(std::ostream &s, const Wrapper &w)
{
     w.Output(s);
     return s;
}

w.Output(s)实际上是对虚函数的调用。 因此,如果w是对Derived<int>的引用,则w.Output(s)将正确调用Derived<int>::Output()而无需以某种方式将WrapperDerived<int>

将所有内容放在一起,您会发现该语句

 cout << *w->get1() << " ";  

现在有效。 机制是get1()返回对象的地址(即,它返回w )。 *获得对指向对象的引用。 然后, operator<<()调用虚拟函数,该函数解析(由于w指向Derived<int>Derived<float> )到Output()的正确重载-由于它是模板化的成员, Derived类,正确访问成员m_value

您可能希望考虑的其他优点

1)尽管我已经根据需要省略了模板化Derived<T> get2()成员,但是该函数也可以由Derived<T>::Output()调用(但不能由operator<<() Wrapper因为Wrapper具有没有这样的函数,并且operator<<()对从Wrapper派生出哪些类一无所知。

2)虽然你的陈述

 cout << *w->get1() << " ";

现在可以使用了,可以完全省略与get1()有关的所有内容,而只需

 cout << *w << " ";

正确输出对象,因为*w获得对w指向的对象的引用( Wrapper &类型)。 这意味着operator<<()将正确调用虚拟函数Output()的最派生形式。

最后一点,我再次强调,无需解决您提出的不可能的问题(如何自动转换为派生最多的类型)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM