приведение void * к классам с множественным наследованием - PullRequest
0 голосов
/ 15 января 2019

У меня проблема, подобная следующей:

Когда статическое приведение безопасно при использовании множественного наследования?

множественное наследование: неожиданный результат после приведения из void * во 2-й базовый класс

но я действительно не понимаю, почему это не работает, так как я делаю то, что предлагается (приведение к исходному типу).

Также участвуют шаблоны.

У меня есть общий контейнер шаблонных объектов, который я реализую как карту с std::string идентификаторами и void*:

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

Объекты выглядят следующим образом:

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

В основном коде у меня есть что-то вроде этого, чтобы добавить свои объекты на карту и вызвать соответствующий метод в зависимости от типа:

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

Строка однозначно определяет, какой класс используется в качестве шаблона, так что обратный вызов может привести к правильному типу.

Все работало очень хорошо, пока я перешел к addClass объектам, таким как B, то есть с единственным наследованием от чисто виртуального шаблонного класса A.

Как только я передаю объект типа C, то есть с множественным наследованием от A и D, callback, где я выполняю приведение, выдает SEGFAULT, даже если у меня нет ошибок компиляции.

UPDATE . По-видимому, проблема была в использовании std::shared_ptr, а не в самом приведении. Вот 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");

}

Если я заменю общий указатель на фактические объекты, все в порядке:

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

Но в моем исходном коде мне нужны общие указатели, потому что во время выполнения существуют некоторые условные выражения для их построения или нет ...

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 Ответ

0 голосов
/ 15 января 2019

Проблема с вашим кодом состоит в том, что ваши общие указатели являются нулевыми, и, следовательно, поведение косвенных указателей через них не определено.

Вы можете создавать общие объекты, используя std::make_shared.

Имейте в виду, однако, что общие объекты уничтожаются, как только уничтожается последний общий указатель, и в этот момент любые указатели на объекты на вашей карте (если таковые имеются) остаются висящими.

...