【问题标题】:cast void* to classes with multiple inheritance将 void* 强制转换为具有多重继承的类
【发布时间】:2019-06-09 13:43:40
【问题描述】:

我有一个类似以下的问题:

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

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

但我真的不明白为什么它不起作用,因为我正在做建议的事情(转换回原始类型)。

此外,还涉及模板。

我有一个模板化对象的通用容器,我将其实现为std::string ID 和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 这样的对象,即从AD 进行多重继承,我进行转换的callback 会产生一个SEGFAULT,即使我没有编译错误。

更新。显然,问题在于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})

【问题讨论】:

  • 您是否在其他地方定义了T1,T2?如果不是,它们将是未定义的,对吧?当你写class T1 {}; class T2{};时会发生什么?
  • T1 和 T2 定义明确,是的。我试图简化示例问题,因为真正的代码太复杂而无法完整显示。
  • @MarcoCamurri 不会完全显示您的代码。而是创建一个 MCVE。
  • 看起来你正在重新发明std::any

标签: c++ templates inheritance casting void-pointers


【解决方案1】:

你的代码的问题是你的共享指针是空的,因此通过它们间接的行为是未定义的。

您可以使用std::make_shared 创建共享对象。

但是请记住,一旦最后一个共享指针被销毁,共享对象就会被销毁,此时指向映射中对象的任何指针(如果有的话)都会悬空。

【讨论】:

  • 哦...我忘记在示例中初始化它们...我现在已经更正了代码,但是它变成了...不再是 MCVE,因为它不再有段错误(但我原来的代码可以)。那么现在写的东西没有什么奇怪或不安全的地方吗?
  • @MarcoCamurri 初始化共享指针后,示例中不再有未定义的行为。整个想法在某种程度上仍然是不安全的,因为很容易使用与字符串参数不匹配的类型的参数意外调用callback,在这种情况下,行为将是未定义的。但只要类型匹配,它就可以工作。
  • 我发现了问题所在。这与选角无关。这正是这里发生的错误初始化列表顺序:geidav.wordpress.com/2013/05/09/…
猜你喜欢
  • 2011-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多