class definition and class declaration

11
我正在阅读《使用C++编程原理与实践》(Bjarne Stroustrup)第312页的第9.4.4节“定义成员函数”,其中提到:
1.
在类定义内部编写成员函数的定义会有两个影响...
2.
除非您知道...,否则不要将成员函数体放在类声明中...
作者是否写错了?它们讨论的是同一件事情,为什么第一句是“类定义”,而第二句是“类声明”?
答:作者并没有写错。第一句话中,“class definition”指的是类的定义,包括类的成员变量和成员函数的声明和定义;而第二句话中,“class declaration”指的是只有类的成员变量和成员函数的声明,缺少定义的那一部分。因此,这两个概念是不同的,并且需要明确区分。

作者在定义类成员函数时提到了哪些缺陷?需要注意的是,对于类模板来说,这样做是必要的。 - Praetorian
是的...抛开术语上的怪癖,这听起来像是一个错误的建议。在class {}块内定义函数没有任何问题;这通常是实现inline函数最清晰的方式。对于类模板的非模板友元(不常见),你必须在该作用域中定义它。 - Potatoswatter
@Praetorian - 在类模板中定义成员函数并不是必须的。这是一种常见的风格,使代码看起来更像Java。但通常情况下,没有什么阻止我们使用正确限定的名称在模板定义之外定义成员函数。 - Pete Becker
@PeteBecker 是的,但我指的是在源文件和头文件中分离定义和声明,这在类模板的情况下是不可能的(除非您还#include源文件)。 - Praetorian
3个回答

15

作者写错了吗?

不算错,但不够精确。一个类的 声明 可以是前置声明,例如:

class X;

这只是让编译器知道该类的存在,但并不指定该类拥有哪些成员、基类等(这是类的定义要做的事情)。

然而,声明也可以是一个定义,例如:

class X
{
    // ...
};

从某种意义上说,因为定义本身也是一种声明,所以这句话并不是错误的。

如果我们假定“declaration(声明)”指的是“非定义的声明”,那么这句话就是错误的;但在处理此类术语时,最好牢记它们的正式定义。


但是类定义也可以是类声明,对吗?例如[class.name]/2“仅由class-key identifier;组成的声明要么是当前作用域中名称的重新声明,要么是标识符作为类名的前向声明。”因此,如果它包含class-specifier/类定义,您可以在类声明中放置函数体。 - dyp
4
这是严格错误的。你的例子是“前向”声明。一般情况下,声明可以是前向声明或定义。引用的作者并不完全错误,只是不应该不够准确。 - Potatoswatter
@DyP:是的,在严格的术语上,你是正确的。我基于常识假设“声明”指的是“不同时也是定义的声明”,但实际上这并不一定是正确的假设。我编辑了答案,谢谢。 - Andy Prowl
@Potatoswatter:你说得对。正如我在上面的评论中所写的,我做出了一个常识性的假设,这在处理正式术语时并不完全适用。我编辑了答案,谢谢。 - Andy Prowl

0

类声明是你声明类的地方。或者说,根据C++标准第3.1节:

声明将名称引入翻译单元或重新声明先前声明引入的名称。声明指定这些名称的解释和属性。

本质上,声明告诉编译器某个具有特定名称的对象、变量等存在。在编译单元中可以有多个声明。

因此,在某种程度上,声明就是告诉编译器某个名称所代表的实体的存在。

class X; // is declaration also known as forward declaration.

这只是一个声明。 然而,每个对象/变量必须始终只有一个定义。 定义基本上是编译器需要创建该类/对象/结构体/变量实例所需的所有内容。

在C++中,许多时候类声明也可以是定义或部分定义。

//declare a class and declare it's members.
class X { //declares X and starts to define it
   void test ();  //declare test method
   int b;         // declare b member
}

完整的定义可以放在一个.cpp文件中,就像这样:

void X::test() {
  //test code
}

或者在类声明中这样写:

   class X {
      void test () {
         //test code
      }

这里有一个关于定义和声明的广泛讨论:

什么是定义和声明之间的区别?

我认为作者建议您不要使用第二种类型的声明,其中您在类定义内部声明和定义方法,而是在单独的C++文件中定义。这是因为C++只定义一次规则,您不希望从多个文件中包含相同的.h文件并且对于同一类有多个定义。

这里还有另一个关于为什么应该分离的广泛讨论:

将C++类的定义放入头文件中是一种好习惯吗?


我没有点踩,但成员函数的定义不是类定义的一部分。函数体可以出现在类定义内部,但这并不意味着它是定义类所必需的一部分。 - dyp
我同意,声明某个东西意味着你拥有创建它所需的所有信息。因此,实质上你不需要方法的声明来创建类实例。然而,很多时候当把它们放在同一个地方而不是分成头文件和cpp文件时,你会遇到问题 :) - Dory Zidon
不,class X;X 的完全正常的声明。在我看来,声明是引入一个名称及其所有必要信息以理解该名称所指的内容(它是变量、类型、函数?模板?等等)。类的前向声明是一个纯声明(或重新声明),类的定义既是(重新)声明又是定义。在创建实例之前,必须对类进行定义(因此也要进行声明)。 - dyp
void X::test() { //test code } 不是实现而是定义吗? - Post Self

0

从面向对象编程语言的不同角度来看,接口意味着声明具有其方法的类。在C++中,您可以将此放入某个头文件中。类的实现意味着实现此类的方法。例如,在Java中,您可以使用抽象类,其中一些方法已实现,而其他方法尚未实现。但是,抽象类或接口无法实例化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接