为什么C++中可以实例化一个没有实现成员函数的类?

Ame*_*lie 27 c++ incomplete-type

例如:

class B;

class A {
public:
    B f();
};

int main(int, char**) {
    A a; // no exception and error

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

该类A可能是不完整的类型。

为什么A这个例子中可以实例化类呢?

f我知道当代码中调用成员函数时,程序将无法编译。

use*_*522 45

在 C++ 中,实体不需要在使用它们的同一翻译单元中定义(有例外)。因此,不在一个翻译单元中定义它并不意味着您不会在另一翻译单元中定义并显式实例化该成员函数。

而且,根本没有必要在 C++ 中定义不被 ODR 使用(例如调用)的实体。

这对于自由函数来说是完全相同的。如果您实际 ODR 使用(例如调用)自由函数,并且该定义只需位于一个翻译单元中,则您也只需定义该自由函数。

A 类可能是不完整的类型。

A定义完成后class A { /*...*/ };。是否定义成员函数并不影响完整性。

我知道当代码中调用成员函数f时,程序将无法编译。

但只有在所有翻译单元链接在一起时,它才会在链接器步骤中失败,因为在此之前编译器无法知道该函数未在其他地方定义和实例化。如果该函数不是 ODR 使用的(例如,调用的),那么链接器无论如何都没有理由寻找它的定义。


请注意,B不完整也不是问题。返回类型只需要在函数的定义中完成,而不是在其声明中,并且可以B在另一个翻译单元中定义成员函数之前完成。

B调用函数时也需要完整,但对于其他 ODR 使用而言通常不需要。这适用于所有返回和参数类型。尽管如此,B无需定义函数即可完成。在另一个翻译单元中仍然可能发生这种情况。


上面的ODR意思是“一个定义规则”。这是一个规则,即整个程序中给定实体只能有一个定义。该规则并不完全适用于所有实体,特别是模板化实体可以在每个翻译中定义一次,只要定义相同(确切的规则更复杂)。

“ODR 使用”是指对实体的使用,该实体将触发一个定义规则,要求(至少)该实体的一个定义存在于程序中的某处。例如,这些是调用函数或获取函数的地址。

有关详细信息,请参阅https://en.cppreference.com/w/cpp/language/definition

  • @G.Sliepen 是的,调用函数时返回类型和参数类型需要完整,但对于其他 ODR 使用来说通常不需要。例如,即使“B”不完整,获取地址也可以。 (2认同)