【问题标题】:Slice off overridden method by casting通过强制转换切掉覆盖的方法
【发布时间】:2015-02-16 09:02:53
【问题描述】:

我有一个从 A 公开继承的 B 类:

class A {
    private:
    virtual void method();
}

class B : public A {
    private:
    void method();
}

现在,我需要以某种方式在 B::method() 中调用原始的 A::method(),而不调用 A 的复制构造函数。
A 在我试图扩展的库中定义,因此我无法更改此代码(例如,使方法受保护)。是否有可能以某种方式在B::method() 中投射this ptr 并切掉覆盖的method

我正在使用调用A::method() 的外部接口。此接口正确调用了我重写的B::method(),但我似乎无法使B::method() 内的接口调用不产生堆栈溢出。

【问题讨论】:

  • 另见stackoverflow.com/questions/2170688/private-virtual-method-in-c,这表明A 类的作者正试图防止这种情况发生。
  • 我认为您所提出的方法只会遇到问题。 1. 可能不是虚拟的 2. 可能没有虚拟析构函数 3. 开发人员不打算在其实现之外重写或调用此方法(因此是私有的且不受保护)。看起来您正在尝试做一些他们不打算发生的事情。
  • @cdhowie:它们可以是private,它们仍然可以被覆盖。如果不是因为很少需要调用基本版本来对其进行一些操作,那将是一个合理的习惯用法(有一段时间我主张让所有 virtual 函数 private 但我意识到这是错误:他们应该是protected)。
  • @DietmarKühl 是的,受保护的虚拟是我一直为不应该公开的虚拟方法所做的。尽管如此,很高兴知道私有虚拟选项存在(尽管我可能不会充分使用它)。
  • 这是一个标志 method() 不应该被子类显式调用;这样做可能会破坏对象的状态(这很糟糕)!如果您认为 method() 实际上应该受到保护,我会联系图书馆供应商和/或自己更改源,如果您可以使用的话。

标签: c++ casting virtual base derived-class


【解决方案1】:

没有符合的方法来调用派生类中的私有函数。

至少如果没有friend 或成员模板,您可以专门使用自己的私有类型来颠覆意图,同时遵守法律条文。

  1. 不过,#define private public-hack 可能对您的实施有用。
    这是未定义的行为,所以不要抱怨它在任何地方都不起作用。可能的故障模式是链接错误,尽管理论上它可能会变得任意离奇。

  2. 更可靠的是添加一个非虚拟保护的内联转发器,尽管这意味着实际编辑标题。

  3. 另外,gcc 有 -fno-access-control 来禁用所有访问检查:https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/C_002b_002b-Dialect-Options.html#C_002b_002b-Dialect-Options

    关闭所有访问检查。此开关主要用于解决访问控制代码中的错误

【讨论】:

    【解决方案2】:

    由于private 方法不能被称为合格并且覆盖不能被撤消,你将不能在没有另一个对象的情况下调用private 方法。转换对象不会有任何效果,因为处理虚函数的方式是实际对象的一部分。

    过去我曾主张制作 all virtual 函数(除了析构函数)private 但实际上需要调用基类版本并不少见。因此,virtual 函数应该private,而是protected。当然,如果一个接口真的实现了它的virtual函数private,那么这个接口的用户就无法撤消这个设计错误。

    看到提倡可怕的黑客攻击 (#define private protected) 的答案得到了支持,我建议宁愿依赖添加的非virtual 成员函数而不更改对象布局并编辑头文件以添加合适的函数:

    class A {
    private:
            virtual void method();
    protected:
        void call_method() { this->A::method(); }
    };
    

    此更改具有更本地化的效果,并且也无法移植。但是,它仅依赖于不通过添加非virtual (inline) 方法和访问说明符来更改对象布局。语言级别的警告不会受到影响。

    【讨论】:

    • 谢天谢地,至少界面的作者可以稍后以保留 ABI 的方式纠正该错误(至少我不知道任何 ABI 不会保留公共库(并受保护) ) 接口)。
    • @Deduplicator: ...而且您的不合格黑客严重依赖于 ABI 在使用访问说明符时不会以任何方式发生变化。如果我们谈论的是不符合标准的黑客,我实际上只是编辑标题并添加一个非virtualprotectedinline 函数,该函数使用合适的限定符调用该函数......
    • 好吧,为那个建议 +1。
    • @DietmarKühl 我很想知道为什么这会不符合标准。规范的哪一部分是这样说的?
    • @cdhowie:是的。对定义的任何更改都将违反 ODR。它们不太可能影响对象的布局并导致实际问题,但未定义的行为可以完全按照您希望它的行为表现,然后在软件正确运行非常重要时选择完全不同的行为(例如,当销售人员在完成数百万笔交易之前正在制作所谓的最终演示:解释“好吧,你知道,我们的程序只是有一些未定义的行为”是不合适的)。
    猜你喜欢
    • 1970-01-01
    • 2011-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-18
    • 2016-07-02
    相关资源
    最近更新 更多