cast void* to classes with multiple inheritance

末鹿安然 提交于 2019-12-11 16:56:49

问题


I have an issue similar to the following:

When is static cast safe when you are using multiple inheritance?

multiple inheritance: unexpected result after cast from void * to 2nd base class

but I really don't understand why it does not work, since I'm doing what is suggested (cast back to the original type).

Also, templates are involved.

I have a generic container of templated objects which I implement as a map of std::string IDs and void*:

std::map<std::string, void*> mymap;

The objects look like follows:

template <class T>
class A {
   virtual void f1(const T&) = 0;
};

class T1 {};
class T2 {};

class B : public A<T1> {
   void f1(const T1&) override;
} 

class D {
   virtual void f3 () {};
}

class C : public A<T2>,
          public D
{
   void f1(const T2&) override;
}

In the main code, I have something like this to add my objects to the map and call the appropriate method depending on the type:

template <class T>
void addClass(A<T>& a, std::string id){
    std::pair<std::string, void*> pair(id, (void*)&a);
    mymap.insert(pair);
}

template<class T>
void callback(A<T>&a, std::string typeID) {
    static_cast<A<T>*>(mymap[typeID])->f1();
}

The string unambiguously identifies what class is being used as a template, so that the callback can cast back to the correct type.

Everything worked just fine as long as I passed to addClass objects like B, i.e. with single inheritance from the pure virtual templated class A.

As soon as I pass an object like C, i.e. with multiple inheritance from A and D, the callback where I do the casting produces a SEGFAULT, even though I have no compilation errors.

UPDATE. Apparently, the problem was in the std::shared_ptr use rather than in the casting itself. Here is the MCVE.

classes.hpp:

#pragma once
#include <iostream>
#include <map>

template <class T>
class A {
public:
   virtual void f1(const T&) = 0;
};

class T1 {
public:
    double t1 = 10.0;
};
class T2 {
public:
    short int t2 = 8;
};

class B : public A<T1> {
public:
   void f1(const T1&) override;
};

class D {
public:
   virtual void f3();
};

class C : public A<T2>,
          public D
{
public:
   void f1(const T2&) override;
};

class MyContainer{
public:
    std::map<std::string, void*> mymap;

    template<class T>
    void addClass(A<T>& t, std::string id);

    template<class T>
    void callback(T& t, std::string id);
};

template<class T>
void MyContainer::addClass(A<T> &a, std::string id){
    std::pair<std::string, void*> pair(id, (void*)&a);
    mymap.insert(pair);
}

template<class T>
void MyContainer::callback(T& t, std::string id){
    static_cast<A<T>*>(mymap[id])->f1(t);
}

classes.cpp:

#include <classes.hpp>

void B::f1(const T1& t1){
    std::cout << "Hello from B using t1: " << t1.t1 << std::endl;
}

void C::f1(const T2 & t2){
    std::cout << "Hello from C using t2: " << t2.t2 << std::endl;
}

void D::f3() {
    std::cout << "Hello from D" << std::endl;
}

main.cpp:

#include <iostream>
#include <classes.hpp>
#include <memory>
using namespace std;

int main()
{
    std::shared_ptr<B> b(new B()); // inherits from A<T1>
    std::shared_ptr<C> c(new C()); // inherits from A<T2> and D


    MyContainer container;
    // no need to specify the template,
    // it is implict from the class being passed
    container.addClass(*b, "t1");
    container.addClass(*c, "t2");

    T1 t1;
    T2 t2;

    container.callback(t1, "t1");
    container.callback(t2, "t2");

}

If i replace the shared pointer with actual objects, everything is fine:

Hello from B using t1: 10
Hello from C using t2: 8

But in my original code I need the shared pointers because there are some conditionals at runtime to whether build them or not...

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
add_compile_options(-std=c++11)
project(casting_problem)
include_directories(include)
add_library(${PROJECT_NAME} SHARED classes.cpp)
add_executable(${PROJECT_NAME}_exe "main.cpp")
target_link_libraries(${PROJECT_NAME}_exe ${PROJECT_NAME})

回答1:


The problem with your code is that your shared pointers are null, and therefore behaviour of indirecting through them is undefined.

You can create shared objects using std::make_shared.

Keep in mind however, that the shared objects are destroyed as soon as last shared pointer is destroyed, at which point any pointers to the objects in your map (if any) are left dangling.



来源:https://stackoverflow.com/questions/54200669/cast-void-to-classes-with-multiple-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!