【问题标题】:Accessing private virtual functions within same class访问同一类中的私有虚函数
【发布时间】:2015-04-26 02:08:00
【问题描述】:

有人问我,使用此代码并且仅在 extractMultAdd() 函数中编写,返回 variable.x variable.y variable.add()variable.multiply。我了解虚拟表的基本概念以及创建它们的时间,但尽管我尽了最大努力,但我不太确定如何利用它们来访问私有虚拟功能。任何帮助,将不胜感激。

#include <cstdio>
#include <iostream>
class MultAdd
{

private:
    int x;
    int y;
    virtual int add()   //virtual fuction so vtable created for Thing class
    {
            return x+y;
    }
    virtual int multiply()
    {
            return x*y;
    }
public:
    MultAdd(){
            x = 2;
            y = 10;
    }
};

int extractMultAdd(void* math)
{
    return 0;
}

int main()
{
    MultAdd variable;
    printf("%d\n", extractMultAdd(&variable));
    return 0;
}

【问题讨论】:

  • 那么你想要一个取决于实现的答案。如果没问题,我会给你一个适用于 MSVC 的。
  • 您要么将这些函数设为public,要么将extractMultAdd() 声明为friend 函数。
  • @πάνταῥεῖ OP说他/她只能修改extractMultAdd
  • @Lingxi 如果 MSVC 掌握了基本概念,那么我也许可以使用它
  • @ralphie9224 似乎要将this 指针正确传递给虚函数,您必须使用特定的寄存器ECX。不知道如何在没有内联汇编的情况下执行此操作。

标签: c++ pointers virtual-functions


【解决方案1】:

我给你两个版本供你选择。它们不遵循标准,也绝不是可移植的,但在我测试时它们都可以在 MSVC 2013、GCC 4.9 和 clang 3.5 上运行。第二个版本比第一个版本相对更安全、更便携。事实上,我希望它相当健壮。

版本 1:

#include <iostream>

class MultAdd {
private:
  int x;
  int y;
  virtual int add() { return x + y; }
  virtual int multiply() { return x * y; }
public:
  MultAdd() {
    x = 2;
    y = 10;
  }
};

struct MultAddCracker: MultAdd {
  int add();
  int multiply();
};

void extractMultAdd(MultAdd& v) {
  char* p = (char*)&v;
  std::cout << *(int*)(p + sizeof(void*)) << '\n';
  std::cout << *(int*)(p + sizeof(void*) + sizeof(int)) << '\n';
  MultAddCracker* pcracker = (MultAddCracker*)&v;
  std::cout << pcracker->add() << '\n';
  std::cout << pcracker->multiply() << '\n';
}

int main() {
  MultAdd v;
  extractMultAdd(v);
}

版本 2:

#include <iostream>

class MultAdd {
private:
  int x;
  int y;
  virtual int add() { return x + y; }
  virtual int multiply() { return x * y; }
public:
  MultAdd() {
    x = 2;
    y = 10;
  }
};

struct MultAddCracker {
  int x;
  int y;
  virtual int add();
  virtual int multiply();
};

void extractMultAdd(MultAdd& v) {
  MultAddCracker& w = (MultAddCracker&)v;
  std::cout << w.x << '\n';
  std::cout << w.y << '\n';
  std::cout << w.add() << '\n';
  std::cout << w.multiply() << '\n';
}

int main() {
  MultAdd v;
  extractMultAdd(v);
}

【讨论】:

  • 在第一个例子中,加和乘不应该是virtual吗? (啊,不,你是在复制vtable,对吧?
  • @MatsPetersson 通过派生自MultAdd,默认情况下它们是虚拟的。也许你在struct MultAddCracker: MultAdd 中忽略了MultAdd :-)
【解决方案2】:

好吧,如果您正在寻找一种可移植的方式,我可以提供以下内容,即 C++ 编译器 must support。进一步讨论here。简而言之,它利用了根据 14.7.2p8 在显式模板实例化中绕过访问检查规则的事实:

14.7.2p8 通常的访问检查规则不适用于用于指定显式实例化的名称。 [注意:特别是,函数声明器中使用的模板参数和名称(包括参数类型、返回类型和异常规范)可能是通常无法访问的私有类型或对象,并且模板可能是成员模板或成员函数通常无法访问。]

几乎做了你想做的事;唯一需要注意的是,根据 14.2,模板声明不能在函数范围内:

模板声明只能作为命名空间或类范围声明出现

这可能会破坏问题的精神,尽管它的措辞方式。 我不知道有什么方法可以将模板结构引入函数范围,但如果有类似的技巧,这可以 100% 完成你想要的。

////////////////////////////////////////////////////////////////////////////////////////
//// The template classes can unfortunately not be declared inside extractMultAdd ()
////////////////////////////////////////////////////////////////////////////////////////

#define ROB_PRIVATE_MEMBER_INST(CLASS, TYPE, MEMBER)    \
template<typename T>                                    \
struct CLASS##_##MEMBER##_rob_tag {                     \
  typedef T CLASS::*type;                               \
  friend type get(CLASS##_##MEMBER##_rob_tag);          \
};                                                      \
template<typename Tag, typename Tag::type M>            \
struct CLASS##_##MEMBER##_rob_private                   \
{                                                       \
    friend typename Tag::type get(Tag) { return M; }    \
};                                                      \
template struct CLASS##_##MEMBER##_rob_private<         \
CLASS##_##MEMBER##_rob_tag<TYPE> , &CLASS::MEMBER>;     \

#define ROB_PRIVATE_MEMBER_INST_FN(CLASS, TYPE, MEMBER) \
template<typename T>                                    \
struct CLASS##_##MEMBER##_rob_tag {                     \
  typedef T type;                                       \
  friend type get(CLASS##_##MEMBER##_rob_tag);          \
};                                                      \
template<typename Tag, typename Tag::type M>            \
struct CLASS##_##MEMBER##_rob_private                   \
{                                                       \
    friend typename Tag::type get(Tag) { return M; }    \
};                                                      \
template struct CLASS##_##MEMBER##_rob_private<         \
CLASS##_##MEMBER##_rob_tag<TYPE> , &CLASS::MEMBER>;     \

#define ROB_PRIVATE_MEMBER_ACCESS(CLASS, INSTANCE, TYPE, MEMBER) \
    (INSTANCE.*get(CLASS##_##MEMBER##_rob_tag<TYPE>()))          \

////////////////////////////////////////////////////////////////////////////////////////
//// Actually use the macros
////////////////////////////////////////////////////////////////////////////////////////

ROB_PRIVATE_MEMBER_INST(MultAdd, int, x);
ROB_PRIVATE_MEMBER_INST(MultAdd, int, y);
ROB_PRIVATE_MEMBER_INST_FN(MultAdd, int(MultAdd::*)(), add);
ROB_PRIVATE_MEMBER_INST_FN(MultAdd, int(MultAdd::*)(), multiply);

////////////////////////////////////////////////////////////////////////////////////////

//ROB_PRIVATE_MEMBER_INST_FN(MultAdd, int(__thiscall *)(), add);
int extractMultAdd(void* math)
{
    // No need to pass as void*
    MultAdd *pMA(reinterpret_cast<MultAdd*>(math));

    // ROB_PRIVATE_MEMBER_INST(MultAdd, int, x); // Note that unfortunately this isn't possible

    // The 4 values, retrieved in a portable way
    int robbed_x = ROB_PRIVATE_MEMBER_ACCESS(MultAdd, *pMA, int, x);
    int robbed_y = ROB_PRIVATE_MEMBER_ACCESS(MultAdd, *pMA, int, y);
    int robbed_add = ROB_PRIVATE_MEMBER_ACCESS(MultAdd, *pMA, int(MultAdd::*)(), add)();      // Note we're calling function with ()
    int robbed_mul = ROB_PRIVATE_MEMBER_ACCESS(MultAdd, *pMA, int(MultAdd::*)(), multiply)(); // Note we're calling function with ()


    return 0;
}

忽略朋友声明警告并滚动到输出:

Run example

当然,“只写在 extractMultAdd() 函数中”可能会为这种语义欺骗留下一些空间:

int extractMultAdd(void* math)
{
    return extractMultAdd_impl(math);
}

// structs

int extractMultAdd_impl(void* math)
{
    // original code ...
} // <== Original brace :)

【讨论】:

    猜你喜欢
    • 2014-12-29
    • 2019-06-19
    • 1970-01-01
    • 2021-10-31
    • 2018-07-13
    • 2023-03-04
    • 2013-05-24
    • 1970-01-01
    • 2012-01-04
    相关资源
    最近更新 更多