C++;类排序 我开始玩C++,来自C和Objic C(还有一点java)。我认为开始培养技能的一个好地方是从头开始编写一个简单的哈希表,使用链表进行冲突。所以我开始写每节课的大纲 class HashTable { public: ... private: ... }; class LinkedList { public: ... private: Node *root; }; class Node { public: Node *next; string key; int value; Node() { ... } }; 这是一个奇怪的事情,这对C++用户来说可能不会有什么奇怪的,这个代码不能工作。我会得到如下错误: error: expected type-specifier before ‘Node’
关于LinkedList类中的根节点 当我简单地对类重新排序,使其成为C++;类排序 我开始玩C++,来自C和Objic C(还有一点java)。我认为开始培养技能的一个好地方是从头开始编写一个简单的哈希表,使用链表进行冲突。所以我开始写每节课的大纲 class HashTable { public: ... private: ... }; class LinkedList { public: ... private: Node *root; }; class Node { public: Node *next; string key; int value; Node() { ... } }; 这是一个奇怪的事情,这对C++用户来说可能不会有什么奇怪的,这个代码不能工作。我会得到如下错误: error: expected type-specifier before ‘Node’,c++,class,syntax,C++,Class,Syntax,关于LinkedList类中的根节点 当我简单地对类重新排序,使其成为Node{…};链接列表{…};哈希表{…}一切都像一辆加油良好的冰淇淋车 现在,我不是一个质疑C++设计的人,但是有什么理由限制这个吗?如果我没记错的话,Obj。C的类基本上被转换成表,并在运行中查找。那么这种行为的原因是什么 编译器抛出以下错误 error: expected type-specifier before ‘Node’ 因为它(编译器)还不知道 什么是节点。(因为节点是稍后定义的。) 两种可能的解决办法:
Node{…};链接列表{…};哈希表{…}代码>一切都像一辆加油良好的冰淇淋车
<>现在,我不是一个质疑C++设计的人,但是有什么理由限制这个吗?如果我没记错的话,Obj。C的类基本上被转换成表,并在运行中查找。那么这种行为的原因是什么 编译器抛出以下错误
error: expected type-specifier before ‘Node’
因为它(编译器)还不知道
什么是节点
。(因为节点
是稍后定义的。)
两种可能的解决办法:
- 将
节点
类的定义放在链接列表
类之前(您已经知道这一点)
- 在class
LinkedList
之前,通过放置此行向前声明class节点
类节点
这告诉编译器存在一个类节点
在阅读了他的评论之后,你似乎在质疑这种行为的理由。我不是一个编译器人员,但我认为这种行为使解析变得容易。对我来说,这类似于在使用之前有一个可用的函数声明
PS:Nitpick,对于LinkedList
,变量名head
可能比root
更合适,因为您已经声明了LinkedList
类有一种节点
,但编译器不知道节点是什么,因为它尚未声明
只需在LinkedList
之前声明节点
,此行为的原因是历史原因。文件按顺序处理。当它遇到对标识符的第一个引用时,该标识符需要已经声明
编译器不会首先处理整个文件
不必对类定义重新排序,您通常可以通过前向声明来逃脱
class Node;
class List
{
public:
//...
private:
Node *root;
//...
};
//...
这种风格意味着解析器可以更少地运行代码。如果必须标识每个声明的类型,然后再次运行代码,则需要花费额外的时间来解析第二次运行的文件
当然,正如许多人所指出的,你可以使用远期声明。换个角度思考;如果可以接受文件中任何地方声明的类为OK,那么为什么不接受在另一个尚未遇到的文件中声明的类呢
如果您走得太远,那么在您尝试链接程序之前,您将无法给出错误,这可能离问题实际发生的位置很远。这种限制没有任何技术原因-编译器在单个类的上下文中做了您显然期望的事情,这一事实证明了这一点。尽管如此,删除“限制”确实会使编译器进一步复杂化,降低它们的速度,增加它们的内存使用率,而且至关重要的是,它不会向后兼容(因为在更本地化的范围(名称空间)中的匹配可能会比前面看到的其他符号更容易选择)
依我看,这也使代码更难阅读和理解。能够自上而下地阅读和理解代码是非常有用的,并且可以鼓励对问题解决方案进行更为周到和结构化的表达。对此类声明的要求来自两种力量。首先,它简化了编译器设计。由于类型和变量具有相同的标识符结构,编译器必须知道在解析标识符时遇到的是什么。有两种方法可以做到这一点。一种方法是要求在其他定义中使用每个标识符之前声明它。这意味着代码在给出定义之前必须向前声明它打算使用的任何名称。这是一种非常简单的方法,可以编写一个语法不明确的编译器
另一种方法是在多个过程中处理它。任何时候遇到未声明的标识符时,都会跳过该标识符,编译器在解析整个文件后会尝试解析该标识符。事实证明,C++的语法使得这很难正确地进行。编译器编写人员不想遇到这种麻烦,所以我们有了转发声明
另一个原因是,您实际上可能希望有前向声明,以便递归结构作为语言的固有属性是确定性的。这有点微妙。假设您编写了一个相互递归的类网络:
class Bar; // forward declaration
class Foo {
Bar myBar;
};
class Bar {
int occupySpace;
Foo myFoo;
};
这显然是不可能的,因为occupySpace
成员将以无限嵌套的递归形式出现。要求定义中所有成员的预先声明为此提供特定数量的信息。特别是,它允许编译器提供足够的信息来形成对类的引用,但不能实例化该类(因为它的大小未知)。forward声明使这成为语言语法的一个特征,很像左值如何作为语言语法的一个特征而不是更微妙的语义或运行时需求进行赋值 我不知道。如果我有一个关于C++的抱怨,就是这个。其他人抱怨糟糕的语法,我不明白,我碰巧觉得语法很优雅。或者他们抱怨它没有高级功能。在我看来,这完全是胡说八道,这就是图书馆
class Bar; // forward declaration
class Foo {
Bar myBar;
};
class Bar {
int occupySpace;
Foo myFoo;
};