c++ - 调用另一个相关对象的 protected 虚函数(用于代理)

标签 c++ gcc clang cl

所以有一个任务:我们有一个第三方库,有一个类(称之为Base)。该库提供了一个隐藏的实现,称为 Impl。 我需要写一个代理。不幸的是 Base 有一个 protected 虚拟函数 fn。

所以问题是,从 C++ 的角度来看,下面的代码在多大程度上是正确的?它目前在 Visual Studio 中完美运行,但在 Mac 上的 clang/gcc 中不起作用(但编译时没有任何警告)。我很清楚那里发生的机制,所以如果删除类问题,一切都可以在两个平台上运行。我想知道我是否应该向 clang 报告错误,或者它是 C++ 标准的未定义/未指定行为。

代码的预期结果是正常调用 Impl::fn()

class Base
{
protected:
    virtual void fn(){}
};

class Impl : public Base
{
public:
    Impl() : mZ(54){}
protected:

    virtual void fn()
    {
        int a = 10; ++a;
    }

    int mZ;
};

class Problem
{
public:
    virtual ~Problem(){}
    int mA;
};

class Proxy :  public Problem, public Base
{
public:
    virtual void fn()
    {
        Base * impl = new Impl;

        typedef void (Base::*fn_t)();
        fn_t f = static_cast<fn_t>(&Proxy::fn);
        (impl->*f)();

        delete impl;
    }
};

int main()
{
    Proxy p;
    p.fn();
}

最佳答案

它恰好在这一行崩溃:

    (impl->*f)();

尝试访问已分配 block 后面的内存。这通常表明没有正确设置this,事实上,交换继承顺序可以解决问题,从而证实了这一理论。

    Base * impl = new Impl;

    typedef void (Base::*fn_t)();
    fn_t f = static_cast<fn_t>(&Proxy::fn);
    (impl->*f)();

所以问题实际上是 fn_t 指向的位置(当然不是这里的 Base::fn 的 vtable 条目)。

现在我们真正看到了这个问题。您尝试调用另一个对象的 protected 函数,尝试使用 &Base::fn 因为这是不可能的,尝试使用指向 Proxy::fn 的指针实际上是一个不同的函数,具有不同的 vtable 索引,这不会存在于基地中。

现在这之所以有效,只是因为 MSVC 使用不同的内存布局,而 Proxy::fn 和 Base::fn 恰好具有相同的 vtable 索引。尝试在 MSVC 构建中交换继承顺序,它可能会崩溃。或者尝试在某处添加另一个函数或成员,我猜迟早它也会与 MSVC 一起崩溃。

关于基本思想:我们在这里试图完成的是调用不同对象的 protected 函数。引用this列表,本质上是相同的 here

Class members declared as protected can be used only by the following:

  1. Member functions of the class that originally declared these members.
  2. Friends of the class that originally declared these members.
  3. Classes derived with public or protected access from the class that originally declared these members.
  4. Direct privately derived classes that also have private access to protected members.
  1. 事实并非如此
  2. 没有宣布好友
  3. 尝试调用不同对象的方法,而不是this
  4. 事实并非如此

所以我认为这是不合法的,会导致未定义的行为,对任何巧妙的转换都漠不关心等。

关于c++ - 调用另一个相关对象的 protected 虚函数(用于代理),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26256083/

相关文章:

linux - 如果函数是虚拟的,dlclose() 静态析构函数在不同时间运行

linux - 如何用 gcc 编译这个微小的可加载内核模块?

C++ MSVC/GCC/Clang 编译器错误

assembly - LLVM 4.0 的内联汇编问题

objective-c - 运行时编程动态方法时使用 ARC 编译时出错

c++ - () 运算符的返回值

c++ - 使用类型转换运算符

c++ - 这是使用访问功能的好习惯

c++ - 成员模板函数不能是虚拟的 - 解决方法?

c++ - 为什么这个构造函数不带参数调用另一个构造函数?