【问题标题】:C++: Passing pointer to template class around in CC ++:在C中传递指向模板类的指针
【发布时间】:2012-03-26 17:34:38
【问题描述】:

我正在为我的 lib 编写一个 C 包装 API。

我通常将我的 C++ 对象作为 void* 在 C 中传递。每个对象的公共函数自然都有访问包装函数。 C 代码访问本机 C++ 类成员。

昨天,有人在 IRC 上提到,我不应该在 C 中将指向 C++ 模板类的指针传递为 void*,因为这很危险。这是真的?指向普通 C++ 类的指针与指向模板类的指针有何不同?

谢谢!

【问题讨论】:

  • 您不应将成员函数指针作为void* 传递。或者函数指针,我想。此外,请考虑使用强类型句柄(typedef struct foo foo; foo* create_foo(); void destroy_foo(foo*); 等)而不是 void*
  • 嗯,强类型句柄可能会更好,我会看看它如何适合我的包装代码......

标签: c++ c templates wrapper


【解决方案1】:

这是假的。模板没有普通类没有的特殊属性。确保您始终使用适当的演员表,您会没事的。

【讨论】:

    【解决方案2】:

    这与模板与普通类无关,但如果您的类具有多重继承,则在转换为 void* 之前应始终从相同类型开始,在转换回时也是如此。指针的地址会根据指针类型是哪个父类而改变。

    class ParentA
    {
        // ...
        int datumA;
    };
    
    class ParentB
    {
        // ...
        int datumB;
    };
    
    class Derived : public ParentA, public ParentB
    {
        // ...
    };
    
    int main()
    {
        Derived d;
        ParentA * ptrA = &d;
        ParentB * ptrB = &d;
        assert((void*)ptrA == (void*)ptrB); // asserts!
    }
    

    【讨论】:

    • @Nawaz,你试过了吗?我在 VS 2010 上做过。无论您使用 C 样式转换还是 reinterpret_cast 都没有区别。
    【解决方案3】:

    将一些指针 Foo* 强制转换为 void* 然后在同一类型 Foo* 上恢复总是安全的。但是,当使用继承时,应特别注意。不应该通过void* 指针进行向上转换/向下转换。考虑以下代码:

    #include <cassert>
    
    class Parent
    {
        int bar;
    };
    
    class Derived : public Parent
    {
        virtual void foo() { }
    };
    
    int main()
    {
        Derived d;
        Derived* ptr_derived = &d;
        void *ptr_derived_void = ptr_derived;
        Derived*    ptr_derived_from_void = (Derived*)ptr_derived_void;
    
        assert(ptr_derived_from_void == ptr_derived);   //that's OK
    
        Parent* ptr_parent = ptr_derived;   //upcast
        Parent* ptr_parent_from_void = (Parent*)ptr_derived_void;   //upcast?
    
        assert(ptr_parent_from_void == ptr_parent); //that's not OK
    
        return 0;
    }
    

    另外this 的帖子显示了通过void* 进行投射的一些问题。

    【讨论】:

      【解决方案4】:

      使用类AB 作为模板参数实例化的模板类涉及编译器创建两个单独的类来专门处理这些各自的类型。在许多方面,这就像一个智能的强类型预处理器宏。手动复制粘贴两个单独的“普通”类并没有什么不同,它们分别在AB 上运行。所以不行。

      唯一危险的方法是如果你尝试投射原来的东西:

      MyClass<A>
      

      作为

      MyClass<B>
      

      如果A 不是B,因为它们可能有不同的内存布局。

      【讨论】:

        【解决方案5】:

        指针就是:指向数据的指针。指针的“类型”对任何东西都没有影响(在编译时),它只是为了代码的可读性和可维护性。

        例如:

         Foo<a> *x = new Foo<a>();
         void *y = (void*)x;
         Foo<a> *z = (Foo<a>*)y;
        

        完全有效,不会引起任何问题。转换指针类型时的唯一问题是,当您忘记指针实际引用的底层数据类型是什么时,您可能会遇到解引用问题。

        如果您到处传递 void*,请注意保持类型完整性。

        不要这样做:

        Foo<a> *x = new Foo<a>();
        void *z = (void*)x;
        //pass around z for a while, then mistakenly...
        Foo<b> *y = (Foo<b>*)z;
        

        意外!

        【讨论】:

        • 这并非完全正确。当涉及继承时,转换指针可能涉及调整内存中的值以指向超类或子类数据。但这与模板无关;这只是意味着您需要小心使用适当的运算符进行转换。
        猜你喜欢
        • 1970-01-01
        • 2012-01-05
        • 1970-01-01
        • 2011-08-02
        • 2020-08-16
        • 2016-07-25
        • 2016-06-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多