【发布时间】:2017-07-20 20:31:53
【问题描述】:
我知道已经提出了类似的问题,并且有一些答案描述了在下面描述的情况下,前向声明是不够的。但是,这些答案并不能说明为什么还不够。因此,我不认为这是一个重复的问题。
我很难理解为什么编译器必须在以下情况下查看数据类型的定义(前向声明是不够的):
假设我们有一个简单的“main.cpp”,如下所示,它确实 除了包含头文件“MyClass.hpp”
#include <MyClass.hpp>
int main(int argc, char **argv)
{
return 0;
}
现在,让我们考虑“MyClass.hpp”的内容如下:
#include <vector>
class Data1;
class MyClass
{
public:
MyClass(); // default constructor
MyClass(MyClass const & p_other); // copy constructor
MyClass(MyClass && p_other); // move constructor
~MyClass(); // destructor
MyClass & operator=(MyClass const & p_rhs); // copy assignment
MyClass & operator=(MyClass && p_rhs); // move assignment
private:
Data1 * m_a; // forward decl. always OK
Data1 m_b; // forward decl. always not OK
std::vector<Data1> m_c; // forward decl. OK if MyClass is not instantiated
};
MyClass 包含几个成员变量:
- “m_a”是指向“Data1”的指针,
- “m_b”是“Data1”的一个实例,
- “m_c”是具有“Data1”类型元素的向量。
“Data1”的定义既不包含在“MyClass.hpp”中,也不包含在“main.cpp”中。它只是前向声明的。
编译器对成员变量“m_a”从来没有问题,它不需要定义它来编译“main.cpp”——即使我们将实例化“MyClass”。
因为我们没有实例化“MyClass”,所以编译器对成员变量“m_c”也没有问题。在这种情况下,它也不需要定义“Data1”。
但是,编译器对成员变量“m_b”有问题:
Impl1.hpp:16:24:错误:字段“m_b”的类型“Data1”不完整
我的简单问题是:为什么?
请考虑我们有用户定义的默认构造函数,复制构造函数, 移动构造函数、用户定义的析构函数和用户定义的复制分配 运算符和移动赋值运算符,即编译器不必 为这些构造函数/方法中的任何一个生成代码。那么为了什么目的 编译器只编译“main.cpp”时需要查看“Data1”的定义吗?
有人知道答案吗?
【问题讨论】:
-
提示:想想
sizeof(MyClass):) -
我认为这不能回答我的问题。我已经阅读了现有的问题及其答案。为什么在这个例子中需要 sizeof(MyClass) ? main.cpp 完全是空的,什么都不做!
-
@A.Schorr:简而言之,
MyClass的布局必须在定义时而不是使用时已知。一旦你定义了它,即使它从未使用过,它的布局也必须是已知的。 -
main.cpp 不为空。您包括了 MyClass.hpp。这意味着 MyClass.hpp 的整个文本都包含在 main.cpp 中。它什么都不做,就像一个很长的算法什么都不返回,什么也不做。这并不意味着允许该算法使用未定义的内容。
-
它创建了你的类。没有对象,但有类本身。其中一部分是定义一组类型作为关联的数据持有者。它看到 Data1* 并认为“好的,我需要一个指针变量,所以我添加 64 位(假设是 64 位系统),没问题。”然后它查看 vector
并认为自己“嘿,让我们问一下 vector,它需要什么”,vector 回答并且本身没有问题,因为它管理指向 Data1 本身的指针。然后它看到 Data1 并想“好的,现在,我的捆绑包需要多少存储空间来存储 Data1?”,然后询问 Data1,但没有得到任何回答。
标签: c++