为什么复制构造函数有时会被明确地声明为非内联?

时间:2017-08-16 20:23:38

标签: c++ copy-constructor

我无法理解有关内联和客户二进制兼容性的句子。有人可以解释一下吗?

C ++ FAQ Cline,Lomow:

  

当编译器合成复制构造函数时,它会使它们内联。如果您的类暴露给您的客户(例如,如果您的客户#include您的头文件而不仅仅是使用从您的类构建的可执行文件),您的内联代码将被复制到您的客户可执行文件中。如果您的客户希望在头文件的版本之间保持二进制兼容性,则不得更改客户可见的内联函数。因此,您需要一个明确的非内联版本的复制构造函数,该版本将由客户直接使用。

5 个答案:

答案 0 :(得分:2)

动态库(.dll.so)的二进制兼容性通常很重要。

e.g。你不想在操作系统上重新编译一半的软件,因为你更新了一些低级库,一切都以不兼容的方式使用(并考虑安全更新的频率)。即使你想要,你甚至可能都没有所需的所有源代码。

要使动态库的更新兼容,并且实际上有效,您基本上无法更改公共头文件中的任何内容,因为那里的所有内容都直接编译到其他二进制文件中(即使在C代码中,这通常也是如此)包括结构大小和成员布局,显然你不能删除或更改任何函数声明。

除了C问题之外,C ++还引入了更多(虚函数的顺序,继承如何工作等),因此可以想象你可以做一些改变自动生成的C ++构造函数,复制,析构函数等的东西。否则保持兼容性。如果它们与类/结构一起定义为“内联”,而不是在源代码中明确定义,那么它们将直接包含在链接动态库并使用这些自动生成的函数的其他应用程序/库中,并且它们不会得到您的更改版本(您可能甚至没有意识到已经改变了!)。

答案 1 :(得分:1)

This answer说如果涉及的功能的签名没有改变,那么"重建"该程序意味着目标文件必须再次链接。你不应该再次编译它们。

This paper描述了 在GNU / Linux系统上用C ++实现的共享库的二进制兼容性 的概念。在撰写图书馆时,This link还可以帮助您了解 do's and don'ts

为什么我们没有 virtual constructors 的概念也与此有关。

您可能会对验证两个给定版本兼容性的工具感兴趣:abi-compliance-checker适用于Linux。

答案 2 :(得分:1)

它指的是库的二进制版本与该库中的标头更改之间可能出现的问题。某些更改是二进制兼容的,某些更改不是。对内联函数的更改(例如内联复制构造函数)不是二进制兼容的,并且需要重新编译使用者代码。

您始终可以在单个项目中看到这一点。如果您更改a.cpp,那么您不必重新编译包含a.hpp的所有文件。但是,如果更改标头中的接口,则通常需要重新编译该标头的任何使用者。这与使用共享库时的情况类似。

维护二进制兼容性对于想要在不更改其接口的情况下更改二进制库的实现时非常有用。这对于修复bug等很有用。

例如,假设程序使用liba作为共享库。如果liba包含它公开的类的方法中的错误,那么它可以更改内部实现并重新编译共享库,并且程序可以使用liba的新二进制版本而不会重新编译。但是,如果liba更改了公共协定,例如内联方法的实现,或者将内联方法移到外部声明,那么它会破坏应用程序二进制接口(ABI)并且必须重新编译使用程序使用liba的新二进制版本。

答案 3 :(得分:0)

考虑将以下代码编译到静态库中:

// lib.hpp
class
t_Something
{
     private: ::std::string foo;

     public: void
     Do_SomethingUseful(void);
};

// lib.cpp
void t_Something::
Do_SomethingUseful(void)
{
    ....
}

// user_project.cpp

int
main()
{
   t_Something something;
   something.Do_SomethingUseful();
   t_Something something_else = something;
}

现在当t_Something类字段以某种方式更改时,例如添加了一个新字段,我们遇到了必须重新编译所有用户代码的情况。基本上由编译器隐式生成的构造函数从我们的静态库“泄漏”到用户代码。

答案 4 :(得分:-1)

我想我理解这段经文意味着什么。但是,我绝不赞同这一点。

我相信,他们描述了您开发库并以头文件和预编译二进制库部件的形式提供给客户的场景。在客户完成初始构建之后,他们希望能够在不重新编译应用程序的情况下用更新的二进制部件替换二进制部件 - 只需要重新链接。实现这一目标的唯一方法是保证头文件是不可变的,即版本之间不会改变。

我想,这个概念的原因在于98个构建系统不够智能,无法检测头文件的变化并触发重新编译受影响的源文件。

现在,任何和所有这些都完全没有实际意义,事实上,再次出现问题 - 由于多种原因,大量的库实际上都在努力成为仅限标题的库。