【问题标题】:Accessing a private virtual function from outside the class从类外部访问私有虚函数
【发布时间】:2014-12-29 01:23:31
【问题描述】:

我知道,就良好的编程实践而言,这是一个坏主意,但我一直在尝试解决一个相对困难的问题,并希望获得一些见解。本质上,我试图输出一个类的所有成员,如下所示:

class protoCalc
{
    private:
        int x; 
        int y;
        virtual int basicAddition()
        {
            return x + y;
        }
        virtual int basicMultiplication()
        {
           return x*y;
        }
public:
    protoCalc(){
        x = 14;
        y = 120;
    }
};

事实证明,访问 x 和 y 很容易;为此,我编写了一个函数(包括我对它如何工作的想法,无论它们是否正确):

int private_member_Print(void* proto)
{
    protoCalc* medium = (protoCalc*)proto;
    protoCalc halfway = *medium;
    int* ptr = ((int *)(&halfway));
return ptr[1];

}

上面会返回x的值,如果使用ptr[2]会返回y的值。

现在我有两个问题,第一个是 ptr[0] 指向什么?那部分内存不应该被私有成员 x 占用,因为它是类 protoCalc 的第一个成员吗?如果不是,那么这个地址是由什么占用的呢?

其次,如何访问虚拟功能?我的第一个直觉是地址 ptr[3] 将被 basicAddition() 虚函数和 ptr[4] basicMultiplication() 函数占用,但事实并非如此。当这不是真的时,我的下一个想法是 ptr[0] 包含指向虚拟成员表位置的指针,该表包含我正在寻找的两个函数。然而,这也被证明是错误的。

那么当我访问了私有成员 x 和 y 时,如何在类之外访问这些虚函数呢?显然我可以改变环境以使其更容易,但这会破坏问题的目的。

【问题讨论】:

  • 这都是未定义的行为。如果您想这样做,请提供用于访问私有成员的公共函数。
  • 一看到 void 指针和演员表,我就停止阅读。接下来的任何事情对我的大脑都没有好处。
  • 您不必担心,但您的函数可以设为 const。如果一个类有虚函数,它可能应该有一个虚析构函数。
  • 我一定不是特别清楚问题的设置。我可以修改的唯一代码在我的“private_member_Print”函数中。为了解决方案的目的,类中的所有内容都必须保持原样
  • 在实践中ptr[0] 可能指向虚函数表指针,但你不能依赖它。 stackoverflow.com/questions/10925115/…

标签: c++ private-members


【解决方案1】:

您当前正在做的是未定义的行为。在大多数情况下,内存中对象的布局由编译器决定,您不知道编译器会将每个成员放在内存中的什么位置。

话虽如此,有一种方法可以实现这一点,那就是你严重滥用模板。我强烈建议不要这样做,而是提供成员函数来访问所需的数据。

这是一个实际的例子。

#include <iostream>

class Priv {
private:
    int i;
    void print( ) {
        std::cout << i << std::endl;
    }

public:
    Priv( ) : i( 100 ) {}

    void print_pub( ) {
        std::cout << "Public" << std::endl;
        this->print( );
    }
};

template<typename Tag>
struct result {
  /* export it ... */
  typedef typename Tag::type type;
  static type ptr;
};

template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
  /* fill it ... */
  struct filler {
    filler() { result<Tag>::ptr = p; }
  };
  static filler filler_obj;
};

template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

struct Priv_f { typedef void ( Priv::*type )(); };
template class rob< Priv_f, &Priv::print >;

struct Priv_i { typedef int Priv::*type; };
template class rob< Priv_i, &Priv::i >;

int main( ) {
    Priv p;

    (p.*result<Priv_i>::ptr) = 1;
    (p.*result<Priv_f>::ptr)(); 

}

可以在here 找到关于 rob/result 模板类的说明。它应该可以跨编译器工作。

【讨论】:

  • 我认为问题更集中在如何实现虚函数。您试图涵盖这一点,但并不完全正确:它不是实现定义的。实现不需要记录它们如何实现虚函数,也不需要记录它们如何布置类。其余的可能与 OP 的要求有关,但我认为与 OP 的兴趣完全无关。
  • 事实上,OP 的评论“我可以修改的唯一代码是在我的“private_member_Print”函数中。”非常明确地放弃了这种方法。
  • 是的,我知道有很多更简单的方法可以实现我想要完成的工作,这就是我在主要问题中提到这一点的原因:D。我使用 Visual Studio,通过反复试验发现 ptr[0] 确实包含指向 vtable 的指针,但是我现在正在努力解决的问题是试图发现如何从 vtable 访问信息
  • @hvd 我的印象是实现定义意味着由实现决定事物在内存中的布局方式。并不是说它需要记录在案。不过我可能错了。
  • @Smith_61 实现定义意味着实现需要记录它。您所说的可能是该术语的合理含义,它恰好不是 C++ 标准中使用的含义。当实现需要做出选择,但不需要记录该选择时,这称为“未指定”。
猜你喜欢
  • 2011-12-29
  • 2016-08-04
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-19
相关资源
最近更新 更多