【问题标题】:How to recover an interface through crosscast如何通过交叉广播恢复接口
【发布时间】:2023-03-12 18:15:02
【问题描述】:

第一个基类

class Io_obj
{
public:
    virtual Io_obj* clone() const=0;
    virtual ~Io_obj(){}
};

第一个派生类

template<typename T>
class Io : public Io_obj,T
{
public:
    Io(){}
    Io(string& s){cout << s << '\n';}
    Io* clone() const override {return new Io{*this};}
    static Io* new_io(string& s){return new Io{s};}
};

第二个基类

class Shape
{
public:
    virtual void draw() const=0;
    virtual ~Shape(){}
};

第二个派生类

class Circle : public Shape
{
public:
    Circle(){}
    Circle(string& s){cout << s << '\n';}
    void draw() const override{cout << "draw circle\n";}
};

class Triangle : public Shape
{
public:
    Triangle(){}
    Triangle(string& s){cout << s << '\n';}
    void draw() const override {cout << "draw triangle";}
};

主要功能

using io_circle = Io<Circle>;
using io_triangle = Io<Triangle>;
using Pf = function<Io_obj*(string&)>;

map<string,Pf> io_map{{"circle",&io_circle::new_io},{"triangle",&io_triangle::new_io}};

Io_obj* get_obj(string& s){
    if(auto f=io_map[s]) return f(s);
    throw runtime_error{"error: wrong type"};
}

int main(){
    vector<string> vs{"circle","triangle","square"};
    for(auto x:vs){
        unique_ptr<Io_obj> my_obj{get_obj(x)};

        if(auto sp=dynamic_cast<Shape*>(my_obj.get())){
            sp->draw();
        }else{
            throw runtime_error{"error: bad cast"};
        }
    }
    return 0;
}

动态投射失败。看来unique_ptr&lt;Io_obj&gt; my_obj{get_obj(x)}创建的对象是Io_obj类型的,它没有draw()的成员函数。如何让它发挥作用?

来自 Bjarne Stroustrup 的代码:C++ 编程语言 Ch22.2.4

【问题讨论】:

    标签: c++ interface polymorphism dynamic-cast


    【解决方案1】:

    要解决此问题,请更改:

    template<typename T>
    class Io : public Io_obj, T
    

    到:

    template<typename T>
    class Io : public Io_obj, public T
    

    必须为每种基本类型分别编写访问说明符。在您的情况下,第二个推导是 private,因为这是默认模式。

    dynamic_cast&lt;&gt;Io&lt;T&gt; 转换为 T,如果它们与由公共继承创建的 is-a 关系连接。在私有的情况下,dynamic_cast&lt;&gt; 将不起作用,因为Io&lt;T&gt; 不是 T(私有继承创建的关系称为“根据”实现)。

    测试:http://coliru.stacked-crooked.com/a/130c7768fb5b501d

    当然,这段代码也会抛出std::runtime_error,因为你还没有为square类型注册“工厂”,但我想你已经知道了:)

    一些参考,也许(18.5.2 - dynamic_cast&lt;T&gt;(v)):

    否则,将应用运行时检查来查看对象是否指向或 v所引用的可以转换为指向或引用的类型 T.

    运行时检查的逻辑执行如下:

    如果,在v 指向(引用)的最衍生对象中,v 点 (指)T 对象的 public 基类子对象,如果 只有一个T 类型的对象派生自指向的子对象 (引用)v,结果是一个指针(一个左值引用) T 对象。

    否则,如果v 指向(引用)public 基类 最衍生对象的子对象,以及最 派生对象有一个T 类型的基类,它是明确且 public,结果是指向最派生对象的T 子对象的指针(左值引用)。

    否则,运行时检查失败。

    转换为指针类型失败的值是空指针值 所需的结果类型。对引用类型的强制转换失败 bad_cast.

    【讨论】:

    • dynamic_cast&lt;&gt;Io&lt;T&gt; 转换为T。如果我理解正确,在此过程中Circle 成为Io 的公共基础。因此Io继承了Circle的成员函数draw()。由于draw()Circle的公共基础Shape中被声明为virtual,所以sp-&gt;draw()的调用终于可以被编译器处理了。
    • @MiloLu 是的,就是这样。虽然它没有被编译器处理。虚函数(我不考虑任何优化)通常在运行时处理以确定对象的实际类型(以及应该调用哪个虚函数)。
    • 我尝试了一种不同的方法:将T 设置为成员而不是公共基础,然后再次发布问题。也许你可以给一些建议? stackoverflow.com/questions/33614524/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 2021-08-23
    • 2013-06-03
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    相关资源
    最近更新 更多