Slice off overridden method by casting
我有一个公开继承自A的B类:
1 2 3 4 5 6 7 8 9
| class A {
private:
virtual void method();
}
class B : public A {
private:
void method();
} |
现在,我需要以某种方式调用B::method()中的原始A::method(),而不调用a的复制构造函数。a是在我试图扩展的库中定义的,因此我无法更改此代码(例如,使方法受保护)。有没有可能在B::method()中以某种方式抛出thisptr,然后切掉覆盖的method?
我正在使用一个调用A::method()的外部接口。这个接口正确地调用了我被重写的B::method(),但我似乎不能在B::method()内进行接口调用,而不会产生堆栈溢出。
- 另请参见stackoverflow.com/questions/2170688/private-virtual-method-i‌&8203;n-c,这表明类A的作者试图防止这种情况发生。
- 我认为你所提出的方法只会面临一些问题。1。可能不是虚拟的2。可能没有虚拟析构函数3。开发人员不打算在实现之外重写或调用此方法(因此是私有的,不受保护)。看来你在做他们不想做的事。
- @CDhowie:它们可以是private,而且它们仍然可以被覆盖。如果不是因为不经常需要调用基础版本来对其执行某些操作,那么这将是一个合理的习惯用法(有一段时间,我主张将所有virtual函数都设置为private),但我意识到这是错误的:它们应该是protected)。
- @Dietmark&252;hl是的,我一直在为不应该公开的虚拟方法做保护虚拟。不过,很高兴知道私有虚拟选项的存在(尽管我可能不会充分利用它)。
- 这是一个符号method()不打算由子类显式调用;这样做可能会破坏对象的状态(这很糟糕)!如果您认为method()实际上应该受到保护,我会联系图书馆供应商和/或自己更改来源(如果您可以使用)。
由于private方法不能被调用为Qualified,并且重写不能被撤消,因此在没有其他对象的情况下,您将无法调用private方法。强制转换对象没有任何效果,因为处理虚拟函数的方式是实际对象的一部分。
在过去,我曾主张使所有virtual函数(析构函数除外)private,但调用基类版本的需求实际上并不罕见。因此,virtual函数不应该是private函数,而应该是protected函数。当然,如果一个接口真的使它的virtual函数private的话,那么这个接口的用户就不能撤消设计错误。
鉴于支持恐怖黑客的答案(#define private protected获得了赞成票,我建议更依赖于添加的非virtual成员函数,而不是更改对象布局,并编辑头文件以添加合适的函数:
1 2 3 4 5 6
| class A {
private:
virtual void method();
protected:
void call_method() { this->A::method(); }
}; |
这种变化具有更大的局部效应,也不可移植。但是,它仅仅依赖于对象布局,而不是通过添加非virtual(inline方法和访问说明符来更改。语言级别的警告不会受到影响。
- 值得庆幸的是,至少该接口的作者可以稍后以ABI保留的方式更正该错误(至少我不知道任何ABI在哪里不会保留库的公共(和受保护)接口)。
- @除尘器:而且,当使用访问说明符时,您的不合规黑客严重依赖于ABI,而不是以任何方式进行更改。如果我们讨论的是不一致的黑客,我实际上只需要编辑头文件并添加一个非virtualprotectedinline函数,它调用带有适当限定符的函数…
- 好吧,这个建议是+1。
- @Dietmark&252;hl我感兴趣的是为什么这会不符合要求。规范的哪个部分这么说?
- @CDhowie:你是说,在上面添加成员?这违反了一个定义规则:在编译修改的头文件时,A的定义与为库编译时的定义不同。我现在懒得找章节。
- @Dietmark&252;hl对,好的。我假设向类中添加友元声明同样会破坏ODR。
- @凯豪:是的。对定义的任何更改都将违反ODR。它们不太可能影响对象的布局并导致实际问题,但未定义的行为可以完全按照您希望的方式进行,然后在软件正确工作非常重要时(例如,当销售人员在关闭多功能操作系统之前进行假定的最终演示时),选择完全不同的行为。-百万交易:解释"好吧,你知道,我们的程序只是有一点未定义的行为")是行不通的。
- @Dietmark&252;hl是的,我经常强调UB本身的危险——我只是想澄清什么构成违反ODR。
在派生类中没有一致的方法来调用私有函数。
至少如果没有friend或成员模板,您可以专门使用自己的私有类型,在遵守法律的同时破坏意图。
不过,还有#define private public黑客,可能对您的实现有效。但这是未定义的行为,所以不要抱怨它在任何地方都不起作用。可能的失效模式是连接误差,尽管理论上它可以变得任意奇异。
更可靠的方法是添加一个非虚拟保护的内联转发器,尽管这意味着实际编辑头。
此外,GCC还让-fno-access-control禁用所有访问检查:https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/c b_002b-dialect-options.html c b-dialect-options
Turn off all access checking. This switch is mainly useful for working around bugs in the access control code.