2

我们在共享库的修订版 1 中有一个结构,我们需要为它维护 ABI:

struct Person
{
    std::string first_name;
    std::string last_name;
}

在修订版 2 中,我们将 Person 更改为:

class Person
{
public:
    Person(const std::string &f, const std::string &l);

    std::string first_name;
    std::string last_name;
}

为了保持源代码兼容性,我们想修改 Person 的 reversion 1,以便针对较新的头文件编译的代码将运行,而未重新编译的代码将运行。

我们是否可以使用两个新的非内联构造函数来执行以下操作:

class Person
{
public:
    Person();
    Person(const std::string &f, const std::string &l);

    std::string first_name;
    std::string last_name;
}

我们正在使用 g++ 完成这一切。在使用 nm 查看生成的共享库时,我没有看到普通结构的构造函数或析构函数,所以我猜测未重新编译的代码只会像以前一样在调用站点构造 Person,这很好。任何重新编译的代码都将使用无参数构造函数。

我看到的唯一问题是,如果我们需要回滚到没有构造函数的旧版本的共享库,那么针对它编译的任何代码都会中断,但我并不担心这种情况。

4

5 回答 5

3

下面的呢?

class NewPerson : public Person
{
public:
    NewPerson(const std::string &f, const std::string &l)
    {
      first_name = f;
      last_name = l;
    }
}
于 2010-08-05T22:01:24.007 回答
2

它可能“有效”,但您将违反单一定义规则,并且就 C++ 标准而言,您将处于未定义行为领域,这不是一个好地方。

于 2010-08-05T21:43:41.490 回答
1

我认为它应该可以工作,假设您的显式默认 ctor 与之前使用的隐式 ctor 执行相同的操作。在这个简单的例子中。然而,恕我直言,很难预测或知道编译器会做什么/改变。我自己不会相信它,如果我是你,我宁愿重新编译库用户。

于 2010-08-05T21:40:45.297 回答
0

在不破坏二进制兼容性的情况下,向类或结构添加新的非虚拟函数应该没有问题。这是因为一个类函数是作为一个普通函数来实现的this,它的第一个参数是隐式。

但是,如果添加新的虚函数,则可能会破坏兼容性,因为新函数将强制修改 vtable,从而可能破坏兼容性。

因此,添加额外的构造函数(永远不能是虚拟的)不会破坏兼容性。如果你要添加一个虚拟析构函数,你很可能会破坏兼容性。

于 2010-08-05T22:32:21.043 回答
0

这种事情可能是有风险的,知道什么时候可以安全地做出这样的改变是很困难的。该标准对您没有帮助,它只是将任何此类更改称为“未定义行为”。

g++ 确实有一个定义明确的 ABI,但是这个 ABI 非常复杂,并且有一些你可能不知道的极端情况。

一个特别令人担忧的是,POD 和非 POD 类型的处理方式通常完全不同。将构造函数添加到以前是 POD 的类型可以使其成为非 POD,这可以显着改变它作为参数传递的方式以及它作为基类合并的方式。在您的特定情况下,由于字符串字段,您的类型已经是非 POD,所以我认为在这种特殊情况下这不是问题,但在类似情况下您肯定会遇到陷阱。

还有一个有趣的问题是,当某事物的行为在 ABI 中定义良好,但在 C++ 标准中未定义时,会发生什么。如果 lto 被禁用,这没有问题,但如果 lto 被启用,编译器就有可能检测到未定义的行为并将其视为优化机会。我不知道是否真的有。

于 2020-08-11T15:04:07.153 回答