从公共函数原型到达私有结构

时间:2017-12-13 08:41:25

标签: c++ struct header

我想理解为什么编译器没有在公共函数中找出私有结构。例如:

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;

};

在这段代码中,gnu编译器给了我这个错误:

  

错误:未知类型名称'Node'     Node * find(int);

我找到了与此问题相关的主题,但我不明白为什么会这样。 Setting a private struct as a return value

3 个答案:

答案 0 :(得分:2)

在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();
};

答案 1 :(得分:0)

尽管许多人将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 ++是一个过程语言的核心。这就是为什么在使用functionstructclass等之前需要转发声明的原因。编译器需要知道要留出多少内存,并且需要知道它将分配哪种类型的内存,无论是本地,动态,静态,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*/ 

这样,如果类或结构中有任何内部结构或类,它有助于防止您遇到的问题。

修改

我还想补充一点,如果一个类包含一个对象并通过指针管理它在堆上的动态内存的资源,那么最好在包含类中对该类进行前向声明&#39 ; s头文件,然后在包含类的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的内部有任何访问权限那么它是可以安全地在Foo的标题中包含Bar的标题:

<强> 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.

这涉及使用前瞻性声明,但它也与预防循环包含有关。

答案 2 :(得分:0)

在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对此非常满意。也许它与弃用此语言功能有关。