【问题标题】:Why class static member are considered declaration while instance member are not? [duplicate]为什么类静态成员被认为是声明而实例成员不是? [复制]
【发布时间】:2021-09-18 04:06:34
【问题描述】:

将以下代码视为 C++ Primer 5th Ed。第 7.6 节。

class Bar {
public:
    // ...
private:
    static Bar mem1; // ok: static member can have incomplete type --> Tag 1
    Bar *mem2;       // ok: pointer member can have incomplete type --> Tag 2
    Bar mem3;        // error: data members must have complete type --> Tag 3
    int x=4; --> Tag 4
}
  1. 上面的类定义被认为是一个定义。这个调用定义没有像创建的对象那样分配存储。
  2. 标签 1 和 2 有效,因为 Bar 被视为声明。我对此很满意,因为没有创建类 Bar 的对象。这些是类定义中的声明。
  3. 标签 3 是我遇到问题的地方。对我来说,标签 3 被认为是声明,因为尚未创建类 Bar 的实例。
  4. 标签 4 - 在这个阶段我也会认为这是一个声明,因为没有创建类 Bar 的实例。

Point 是那些像标签 3 和 4 这样的数据成员,只有在类 Bar 的实例是在类定义之外创建时,我才会将它们视为定义。

Bar bar; --> this would then initialize tag 3 & 4 --> Tag 5

为什么标签 3 需要完整,因为在这一行没有分配存储,因为它仍然是一个声明?标记 3 还不是这一行的实例。当我们在类定义之外创建类 Bar 时,标签 3 仅成为一个定义并分配存储空间,就像在标签 5 中一样。

【问题讨论】:

  • Tag 3 需要完整,因为编译器需要知道它的大小,以便知道如何分配一个Bar
  • 为什么?尚未创建类 Bar 的实例。那么为什么它需要标签 3 中的尺寸呢?请记住,尚未在该行创建类 Bar 的对象。类 Bar 的对象仅在类 Bar 定义之外创建,如标记 5 所示
  • 与头文件中声明的全局变量完全相同的原因。静态类成员,如全局变量,最终需要在编译单元(.cpp 文件)中定义一个可以链接到的地址。
  • @yapkm01 Bar mem3; 只是写 Bar mem3 = Bar(); 的简写。您可以通过查看here 来查看。 (Bar 类的默认构造函数被调用,但在您的情况下,由于尚未定义 Bar,因此会弹出错误。)
  • 该语言只要求明确定义类型的大小。如果一个类型包含它自己的一个实例,它的大小是无限的,并且编译器阻止你这样做,甚至在该类型的对象被创建之前。我可以问为什么这是一个问题吗?你有什么特别的理由想要编译器接受的类型吗?

标签: c++ c++11


【解决方案1】:

请记住,c++ 类定义定义了内存布局。与大多数高级语言不同,变量的修饰中没有隐含的引用,它表示为该数据的一个小块内存,该数据在该类或结构的连续内存块中排列。

将 Bar 类型的实例成员变量声明为 Bar 的一部分会创建一个不可解析的递归定义,因为 Bar 的内存作为一部分包含 Bar 的所有内存以及其他内容。

静态变量之所以有效,是因为它不必在 Bar 的每个实例中分配,只需 1 以供所有 Bar 实例共享,它更多的是与类的命名空间相关联的全局变量,而不是真正的成员传统意义上的。

指针之所以起作用,是因为指针只是引用另一块应该包含 Bar 的内存,因此在构建内存布局时不会遇到相同的递归扩展问题。

【讨论】:

  • 标签 4 怎么样?为什么那里分配了存储空间?尚不存在 Bar 类的实例。 (假设标签 5 不存在)。这就像没有母亲的孩子..哈哈
  • 这使得变量在每个实例中都被初始化为那个值。仅允许这样做,因为这是一个常量表达式。编译器将简单地将初始化烘焙到每个构造函数中,因此它在严格意义上并不真正存在
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-15
  • 1970-01-01
  • 2012-01-21
  • 2016-12-16
  • 1970-01-01
相关资源
最近更新 更多