【问题标题】:How to cast member variable pointer to generic type in C++如何在 C++ 中将成员变量指针转换为泛型类型
【发布时间】:2010-09-30 01:28:30
【问题描述】:

我的应用程序中有类似的代码:

class A
{
  public: int b;
}

class C
{
  public: int d;
}

void DoThings (void *arg1, MYSTERYTYPE arg2);

A obj_a;
C obj_c;

DoThings(&obj_a, &A::b);
DoThings(&obj_c, &C::d);

问题是 - MYSTERYTYPE 应该是什么? void* 和 int 都不起作用,尽管如果通过 printf 输出值 &A::b 就可以正常打印。

澄清:是的,&A::b 是在 C++ 下定义的。是的,我正在尝试将偏移量分配给班级成员。是的,我很狡猾。

编辑:哦,我可以使用 offsetof()。还是谢谢。

【问题讨论】:

  • 哇,这很有趣。我记下了我的答案,j_random_hacker 得到了信任。
  • 已编辑,因此 DoThings 看起来可以做一些有意义的事情,因为你们非常热衷于有意义的事情。
  • zarawesome,你不能总是使用 offsetof。如果你有一个非 POD,这是不可能的(如果你有一个用户定义的构造函数或私有数据成员,它已经是非 Pod)。强烈推荐成员指针
  • @Jeff:谢谢,虽然很有趣,我以为我在拉拉的土地上很遥远......

标签: c++ class pointers member-pointers


【解决方案1】:

如果您按照 j_random_hacker 的建议使用模板,并且编译器在您调用函数时知道每个类的类型,那么您问题的字面答案是“template <typename CLASS> void DoThings (CLASS * object, int CLASS::*MEMBER)”。

以下是它如何适合您的示例:

#include <iostream>

class A {
public: 
    int b;
};

class C {
public: 
    int d;
};

template <typename CLASS>
void DoThings (CLASS * object, int CLASS::*MEMBER)
{
    std::cout << object->*MEMBER << std::endl;
}

A obj_a = { 2 };
C obj_c = { 4 };

int main (int argc, const char * argv[])
{
    DoThings(&obj_a, &A::b);
    DoThings(&obj_c, &C::d);
    return 0;
}

【讨论】:

    【解决方案2】:

    你有一个指向两个不相关类的数据成员指针。好吧,您找不到可以同时保存两个指针的通用类型。它仅在函数参数是指向派生成员的数据成员指针时才有效,因为如果基包含它,它也保证包含该成员:

    struct a { int c; }; struct b : a { }; int main() { int b::*d = &a::c; }
    

    更新:我想我应该写下为什么上面的内容从a::* 隐式转换为b::*。毕竟,我们通常有b*a* !考虑:

    struct a { };
    struct b : a { int c; };
    struct e : a { };
    int main() { int a::*d = &b::c; e e_; (e_.*d) = 10; /* oops! */ }
    

    如果以上是有效的,你真的会搞砸了。以上无效有效,因为从b::*a::* 的转换不是隐式的。如您所见,我们分配了一个指向 b::c 的指针,然后我们可以使用一个根本不包含它的类来取消引用它! (e)。编译器强制执行此顺序:

    int main() { int b::*d = &b::c; e e_; (e_.*d) = 10; /* bug! */ }
    

    现在编译失败,因为e 不是从b 派生的,成员指针指针所属的类。好的!但是,以下内容非常有效并且当然可以编译(更改了类ab):

    struct a { int c; };
    struct b : a { };
    struct e : a { };
    int main() { int e::*d = &a::c; e e_; (e_.*d) = 10; /* works! */ }
    

    要使其适用于您的情况,您必须将您的函数设为模板:

    template<typename Class>
    void DoThings (int Class::*arg) { /* do something with arg... */ }
    

    现在,编译器将自动推断给定成员指针所属的正确类。您必须在成员指针旁边传递实例才能实际使用它:

    template<typename Class>
    void DoThings (Class & t, int Class::*arg) { 
        /* do something with arg... */ 
        (t.*arg) = 10;
    }
    

    如果你只是想设置一些你在编写 DoThings 时就已经知道的成员,那么以下就足够了:

    template<typename Class>
    void DoThings (Class & t) {  
        t.c = 10;
    }
    

    【讨论】:

      【解决方案3】:

      &A::b 和 &C::d 是无意义的,没有关联的地址。您是否要获取成员的偏移量?

      您确定不想要以下内容吗?

      DoSomething(&obj_a,&obj_a.b);
      

      【讨论】:

      • sqlrob: "&A::b" 是有效的 C++ 代码,其效果是返回一个指向数据成员的指针类型,类型为 "int (A::*)"。跨度>
      • 我更习惯于指向成员函数,而不是数据。拥有指向成员数据的指针似乎没什么用,尤其是如果它必须是指向特定数据成员的指针,正如示例中的语法所暗示的那样。
      【解决方案4】:

      您是否只是尝试使用恰好存在于AC 对象中的整数地址调用函数?在这种情况下,Jeff McGlynn 的答案就是要走的路。

      否则,如果您真的想要做一些棘手的事情,需要 C++ 奇怪的指向成员的工具(而您几乎肯定不会):

      由于类 AC 是不相关的,您需要一个模板函数来处理这两者:

      template <typename T>
      void DoThings(int T::*x);
      

      如果 C 实际上是从 A 派生的,则以下方法可行:

      void DoThings(int A::*x);
      

      【讨论】:

      • 哇,我在发布我的编辑后才看到这个。我们如何发布相同的内容? :-)
      • random_hacker,恰恰相反。对于成员指针,关系是相反的:int C::*x 将是参数的常见类型
      • @litb: 如果 C 是从 A 派生的,并且您希望能够使用类型 A 对象或类型 C 对象调用 DoThings(),在任何一种情况下都对元素 b 进行操作,参数需要是“int A::*”类型,对吧?如果使用“int C::*”,则无法传递 A 类型的对象。
      • j_random_hacker,没有。您没有 A 或 B 对象/指针。你有一个 A/B 成员指针。假设 A 有“int d;”,B 有“float c”。并且您接受“float A::*”。并且你通过“&B::c”,它会工作,即使 A 没有成员 A::c !。
      • 所以,明确一点,你的函数看起来像: void fun(base * b, int derived::*d);
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-20
      • 1970-01-01
      相关资源
      最近更新 更多