拆分维护类接口的长方法

Splitting long method maintaining class interface

本文关键字:方法 接口 维护 拆分      更新时间:2023-10-16

在我的库中有这样一个类:

class Foo {
public:
    void doSomething();
};

现在,doSomething()的实现已经增长了很多,我想把它分成两种方法:

class Foo {
public:
    void doSomething();
private:
    void doSomething1();
    void doSomething2();
};

doSomething()的实现是这样的:

void Foo::doSomething() {
    this->doSomething1();
    this->doSomething2();
}

但是现在类接口发生了变化。如果我编译这个库,所有使用这个库的现有应用程序都不能工作,外部链接被改变。

如何避免破坏二进制兼容性?

我想内联解决了这个问题。对吗?它是便携的吗?如果编译器优化未内联这些方法会发生什么?

class Foo {
public:
    void doSomething();
private:
    inline void doSomething1();
    inline void doSomething2();
};
void Foo::doSomething1() {
    /* some code here */
}
void Foo::doSomething2() {
    /* some code here */
}
void Foo::doSomething() {
    this->doSomething1();
    this->doSomething2();
}
编辑:

我在方法分裂之前和之后测试了这段代码,它似乎保持了二进制兼容性。但我不确定这是否适用于每个操作系统和每个编译器以及更复杂的类(虚拟方法,继承…)。有时,在添加这样的私有方法后,我的二进制兼容性会中断,但现在我不记得是在哪种特殊情况下了。也许是由于按索引查找的符号表(如Steve Jessop在他的回答中注释)。

严格地说,更改类定义(以您所显示的任何一种方式)都违反了单一定义规则,并导致未定义的行为。

在实践中,向类中添加非虚成员函数可以在每种实现中保持二进制兼容性,因为如果不这样做,就会失去动态库的大部分好处。但是c++标准并没有对动态库或二进制兼容性说太多(任何?),所以它不能保证你能做什么改变。

因此,在实践中,只要动态链接器按名称查找符号表中的项,更改符号表并不重要。符号表中的条目比以前多了,但这没关系,因为所有旧的条目仍然具有相同的混乱名称。可能在你的实现中,私有和/或内联函数(或你指定的任何函数)没有全部导出,但你不需要依赖于它。

我使用过一个系统(Symbian),符号表中的条目不是按名称查找的,而是按索引查找的。在该系统上,当您向动态库添加任何内容时,您必须确保将任何新函数添加到符号表的末尾,这可以通过在特殊配置文件中列出所需的顺序来实现。您可以确保二进制兼容性没有被破坏,但这相当乏味。

所以,你可以检查你的c++ ABI或编译器/链接器文档来绝对确定,或者只是相信我的话,然后继续。

这里没有问题。

Foo::doSomething()的name mangling总是相同的,无论它的实现。

我认为如果添加非虚方法,类的ABI不会改变,因为非虚方法不存储在类对象中,而是作为具有混乱名称的函数。只要不添加类成员,就可以添加任意多的函数。