简体   繁体   中英

How to recover an interface through crosscast

First base class

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

First derived class

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};}
};

Second base class

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

Second derived classes

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";}
};

main functions

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;
}

Dynamic cast failed. It seems that the object created by unique_ptr<Io_obj> my_obj{get_obj(x)} is of type Io_obj , which has no member function draw() . How to make it work?

Code from Bjarne Stroustrup: the C++ programming language Ch22.2.4

To fix this, change:

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

to:

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

Access specifiers must be written separately for each base type. In your case, second derivation was private , as this is the default mode.

dynamic_cast<> converts Io<T> to T , if they are connected with is-a relation, which is created by public inheritation. In case of private, dynamic_cast<> will not work, because Io<T> is not T (private inheritance creates relation called "implemented in terms of" ).

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

Of course this code will also throw an std::runtime_error , as you have not registered "factory" for square type, but I guess you already know this :)

Some reference, perhaps ( 18.5.2 - dynamic_cast<T>(v) ):

Otherwise, a run-time check is applied to see if the object pointed or referred to by v can be converted to the type pointed or referred to by T .

The run-time check logically executes as follows:

If, in the most derived object pointed (referred) to by v , v points (refers) to a public base class subobject of a T object, and if only one object of type T is derived from the sub-object pointed (referred) to by v , the result is a pointer (an lvalue referring) to that T object.

Otherwise, if v points (refers) to a public base class sub-object of the most derived object, and the type of the most derived object has a base class, of type T , that is unambiguous and public , the result is a pointer (an lvalue referring) to the T sub-object of the most derived object.

Otherwise, the run-time check fails.

The value of a failed cast to pointer type is the null pointer value of the required result type. A failed cast to reference type throws bad_cast .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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