我想了解为什么编译器没有在公共函数中找出私有结构。例如:
class List
{
public:
List();
List(const List&);
bool remove(int);
bool insert(int, ItemType );
Node *find(int);
void toString();
ItemType retrive(int);
int getSize();
private:
struct Node
{
ItemType item;
Node *next;
};
int size;
Node *head;
};
在这段代码中,它是Linked List头的一部分,gnu编译器给了我这个错误:
错误:未知类型名称'Node'Node * find(int);
我发现了一个与此问题相关的主题,但我不明白为什么会这样。 Setting a private struct as a return value。
虽然许多人将C++
与面向对象的编程语言联系起来;这只是语言核心的一小部分。这种范式可能会让一些人陷入困境,因为C++
的根源就像C
一样是一种过程式编程语言。
考虑这段代码:
#include <iostream>
#include <string>
int main() {
std::string hello("hello");
std::string world("world");
std::string helloWorld = getConcatString( hello, world );
std::cout << helloWorld << std::endl;
return 0;
}
std::string getConcatString( const std::string& str1, const std::string& str2 ) {
return str1 + std::string(" ") + str2;
}
以上将无法编译,当它到达此代码中的8th
行时将失败。这失败的原因与您班级中的原因相同。编译器遇到getConcatString(...)
但它没有被声明或定义,因为C ++是一个过程语言的核心。这就是为什么你需要在使用function
,struct
,class
等之前进行前瞻性声明。编译器需要知道要留出多少内存,并且需要知道它将分配哪种类型的内存,无论是本地,动态,静态,const等.C ++中的所有内容都按顺序发生。
要修复上面的代码,您有两个选择:
当涉及到类的结构时,我通常遵循以下模式:
class ClassName /*struct StructName*/ {
public:
// variables, constants, enums, structs, unions, typedefs here
protected:
// variables, enums, structs, unions, typedefs here if using inheritance
private:
// variables, enums, structs, unions, typedefs here to encapsulate
public:
// Constructors & Destructor if being declared here.
// public functions or methods, setters & getters here.
protected:
// Constructor - Copy Constructor if Abstract - Inheriting.
// protected methods for inheriting classes.
private:
// Constructor - If fully preventing from creating an instance of this class or if the class is fully static.
// private methods that are specific to this class.
}; // ClassName /*struct StructName*/
这样,如果类或结构中有任何内部结构或类,它有助于防止您遇到的问题。
编辑
我还想补充一点,如果一个类包含一个对象并通过指针管理它在堆上的动态内存的资源,那么最好在包含类的头文件中有一个该类的前向声明,然后包含资源的头文件在包含类的cpp文件中:
foo.h中
#ifndef FOO_H
#define FOO_H
class Bar; // forward declaration
class Foo {
private:
Bar* bar_; // For demonstration purposes I'm using a raw pointer...
// It is wiser to use smart pointers.
public:
Foo( /*properties to create an instance of Bar*/ );
~Foo(); // Destructor Implemented see Rule of 3, 4 or 5...
// Copy Construct & overloaded operator=() needed here
...
// other public functions
}; // Foo
#endif // FOO_H
Foo.cpp中
#include "Foo.h";
#include "Bar.h";
Foo::Foo( /* properties need for Foo & to properly create Bar on the heap*/ ) {
// Allocation of Bar Here
}
Foo::~Foo() {
// deallocation of Bar here.
}
另外明智的是,如果Foo使用bar作为本地内部实例,复制或引用是Foo的本地实例并且独立于Bar意味着Bar不包含Foo或者可以访问Foo的内部,那么只需将Bar的头部包含在内就是安全的。 Foo的标题是这样的:
foo.h中
#ifndef FOO_H
#define FOO_H
#include "Bar.h"
class Foo {
private:
Bar bar_; // local stack copy
public:
Foo(){} // Default - Empty Class
// Any of these constructors would be okay
explicit Foo( Bar bar );
explicit Foo( Bar& bar );
explicit Foo( const Bar bar );
explicit Foo( const Bar& bar );
}; // Foo
#endif // FOO_H
Foo.cpp中
#include "Foo.h"
// Do not need to include Bar.h
// Constructors here.
这涉及使用前瞻性声明,但它也与预防循环包含有关。
在C ++中,所有使用的名称,例如Node
,必须先前已声明,即在源代码中较早。成员函数的主体有一个例外。您可以将此异常视为在编译正确之前应用的文本转换,其中函数/ definitions /(如果有)将移动到类定义之后的某个点,并且只在类中留下声明。因此,在函数体中引用Node
就可以了(将其视为在假设转换之后)。但不是在返回值规范中。
这意味着您可以通过重新排序类定义来解决此问题:
class List
{
struct Node
{
ItemType item;
Node *next;
};
int size;
Node *head;
public:
List();
List(const List&);
bool remove(int);
bool insert(int, ItemType );
Node *find(int);
void toString();
ItemType retrive(int);
int getSize();
};
在C ++语言中,可以“向前看”并查看整个类定义的上下文仅限于:成员函数体,缺省参数,异常规范和类内非静态数据成员初始值设定项。
即由于上述原因,以下示例是合法的†
struct S
{
void foo(void *p = (N *) 0) throw(N)
{
N n;
}
unsigned s = sizeof(N);
struct N {};
};
即使它在其声明点之上(即之前)使用名称N
。
在您的示例中,您尝试在函数返回类型中使用尚未声明的名称Node
。函数返回类型不属于上面的列表。因此,在C ++中不允许这样做。在函数返回类型中使用它之前声明Node
。
†GCC拒绝编译异常规范throw(N)
,而Clang对此非常满意。也许它与弃用此语言功能有关。