C++类方法,返回向量<子类>;

发布于 2024-12-09 16:52:55 字数 628 浏览 0 评论 0原文

我尝试为课程编写的方法遇到了一些麻烦。我有班级符号和班级终端。类终端扩展了类符号,但类符号的方法之一需要返回一个向量。例如:

#ifndef SYMBOL_H
#define SYMBOL_H

#include "terminal.h"
#include <vector>

using namespace std;

class symbol {
   public:
        vector<terminal> first();
        virtual void polymorphable();
};

#endif

定义了类终端:

#ifndef TERMINAL_H
#define TERMINAL_H

#include "symbol.h"

using namespace std;

class terminal: public symbol {
    // ...
};

#endif

但是,在执行此操作时,我在构建时遇到两个错误,其中一个或另一个先出现:定义向量函数的行上的“'terminal':未声明的标识符”和“'symbol'” : 基类未定义”与终端类定义一致。

如何解决“a 需要 b”、“b 需要 a”问题?

I'm having a bit of trouble with a method I'm trying to write for a class. I have class symbol and class terminal. class terminal extends class symbol, but one of the methods of class symbol needs to return a vector. E.g.:

#ifndef SYMBOL_H
#define SYMBOL_H

#include "terminal.h"
#include <vector>

using namespace std;

class symbol {
   public:
        vector<terminal> first();
        virtual void polymorphable();
};

#endif

With class terminal defined:

#ifndef TERMINAL_H
#define TERMINAL_H

#include "symbol.h"

using namespace std;

class terminal: public symbol {
    // ...
};

#endif

However, when doing this, I get two errors when building, with one or the other coming first: "'terminal' : undeclared identifier" on the line that defines the vector function, and "'symbol' : base class undefined" on the line with the terminal class definition.

How do I solve this 'a requires b', 'b requires a' issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

笔落惊风雨 2024-12-16 16:52:55

使用转发声明避免循环依赖。

class terminal;

class symbol
{
  std::vector<terminal> first();
  // ...
};

通过 推测此方法根据 C++ 标准未定义。
@Ben Voight 指出:

C++03 标准第 17.6.4.8 节说:

“特别是,在以下情况下效果未定义:...如果在实例化模板组件时使用不完整的类型作为模板参数,除非该组件特别允许。 .

无论< code>std::vectorf(); 是 std::vector 的一个实例化,正在讨论此处如果那里的答案证明了这一点。那么这个答案没有用,我将删除它,否则它仍然有效。

Avoid circular dependencies by using Forward Declarations.

class terminal;

class symbol
{
  std::vector<terminal> first();
  // ...
};

There is a speculation of this approach being Undefined as per C++ standard.
@Ben Voight points out:

C++03 standard Section 17.6.4.8 says:

"In particular, the effects are undefined in the following cases: ... if an incomplete type is used as a template argument when instantiating a template component, unless specifically allowed for that component...

Whether std::vector<X> f(); is std::vector<X> an instantiation, is being discussed here. If the answer there proves it is then this answer holds no good and I will delete the same, or else this stays valid.

作妖 2024-12-16 16:52:55

编辑:标准可能不允许以下内容(请参阅评论)。在这种情况下,您根本无法拥有适当的循环依赖项:如果 A 的大小取决于类型 B 的成员的大小,但 的大小B 取决于类型 A 成员的大小,那么这样的定义根本没有意义。

不过,我并不完全确定这是否适用于您的情况,因为您只声明了一个返回类型不完整的函数,这是允许的。请参阅詹姆斯提出的问题;希望我们能在那里得到明确的答案。


只需前向声明terminal

class terminal;

class symbol
{
  std::vector<terminal> first();
  // ...
};

您可以前向声明任何只需为不完整类型的内容。不完整类型可用于形成指针、引用、函数签名。仅当使用该类型的变量时才需要知道完整类型。

Edit: The following may not be allowed by the standard (see the comments). In that case, you simply cannot have a proper circular dependency: If the size of A depends on the size of a member of type B, but the size of B depends on the size of a member of type A, then such a definition simply doesn't make sense.

I'm not entirely certain if that applies to your situation, though, since you only declare a function whose return type is incomplete, which is allowed. See the attendant question by James; hopefully we'll get a definite answer there.


Just forward-declare terminal:

class terminal;

class symbol
{
  std::vector<terminal> first();
  // ...
};

You can forward-declare anything that only needs to be an incomplete type. Incomplete types can be used to form pointers, references, function signatures. The complete type only needs to be known when variables of that type are used.

萌化 2024-12-16 16:52:55

Base 类不需要了解 Derived 类的任何信息。这是面向对象设计的一个重要原则。您可以将基类更改为:

class symbol {
  public:
    vector<symbol*> first();
    virtual void polymorphable();
};

现在,first() 返回指向基类的指针向量。通过多态性,每个指针实际上都可以指向派生类。请注意,我将其更改为使用指针。如果您将其更改为简单的 vector ,则不起作用。

或者,如果您确实需要基类知道派生类的存在,您可以向前声明派生类:

#ifndef SYMBOL_H
#define SYMBOL_H

#include "terminal.h"
#include <vector>

using namespace std;
class terminal;  // forward declare the existence of this class

class symbol {
  public:
    vector<terminal*> first();     // change to be a vector of pointers
                                   // to avoid issues with incomplete type
    virtual void polymorphable();
};

#endif

Base class should not need to know anything about Derived class. That's an important principle in Object Oriented Design. You could potentially change the base class to be:

class symbol {
  public:
    vector<symbol*> first();
    virtual void polymorphable();
};

Now, first() returns a vector of pointers to the base class. With polymorphism, each pointer can actually point to the derived class. Note that I changed it to use pointers. If you changed it to simply vector<symbol> that wouldn't work.

Alternatly, if you really need the Base class to know about the existence of the Derived class, you can forward declare the derived class:

#ifndef SYMBOL_H
#define SYMBOL_H

#include "terminal.h"
#include <vector>

using namespace std;
class terminal;  // forward declare the existence of this class

class symbol {
  public:
    vector<terminal*> first();     // change to be a vector of pointers
                                   // to avoid issues with incomplete type
    virtual void polymorphable();
};

#endif
等你爱我 2024-12-16 16:52:55

使用前向声明。

James - 注意声明与实例不同。这段代码工作正常

#include <vector>

class terminal; <--- TELLING THE COMPILER MAY USE terminal in the future.

class symbol 
{        
  std::vector<terminal> first(); <--- NOTE THE COMPILER DOES NOT NEED TO KNOW HOW TO CONSTRUCT EITHER
  // ... 
};           

class terminal: public symbol  < --- TELLS COMPILER THAT terminal INHERITS symbol i.e. CONTAINING THE METHOD first
{
   int wibble; 
};  

int main()
{
    symbol s;
    return 0;
}

Als - 你是对的。

Use forward declarations.

James - note declarations differ from instantion. This code works fine

#include <vector>

class terminal; <--- TELLING THE COMPILER MAY USE terminal in the future.

class symbol 
{        
  std::vector<terminal> first(); <--- NOTE THE COMPILER DOES NOT NEED TO KNOW HOW TO CONSTRUCT EITHER
  // ... 
};           

class terminal: public symbol  < --- TELLS COMPILER THAT terminal INHERITS symbol i.e. CONTAINING THE METHOD first
{
   int wibble; 
};  

int main()
{
    symbol s;
    return 0;
}

Als - You are correct.

回忆追雨的时光 2024-12-16 16:52:55

我认为奇怪的重复模板模式可以让你摆脱这个:

template<typename terminal_type>
class symbol_pattern
{
   public:
        std::vector<terminal_type> first();
        virtual void polymorphable();
};

class terminal : public symbol_pattern<terminal>
{
};

typedef symbol_pattern<terminal> symbol;

I think Curiously Recurring Template Pattern can get you out of this one:

template<typename terminal_type>
class symbol_pattern
{
   public:
        std::vector<terminal_type> first();
        virtual void polymorphable();
};

class terminal : public symbol_pattern<terminal>
{
};

typedef symbol_pattern<terminal> symbol;
猫烠⑼条掵仅有一顆心 2024-12-16 16:52:55

考虑使用返回迭代器范围的 begin()end() 函数,而不是让单个成员函数返回容器,类似于标准库容器本身所做的事情:

class terminal;

class terminal_iterator { /* defined appropriately */

struct symbol
{
    terminal_iterator begin_terminals() const;
    terminal_iterator end_terminals() const;
};

如果您已经在要迭代的地方有一个 std::vector ,您只需 typedefterminal const*terminal_iterator; (或使用类似的 typedef)并定义成员相应地发挥作用。

如果您没有容器(即成员函数将具体化序列本身),您可以考虑编写自己的迭代器类来生成序列。

提供 begin()end() 范围访问器有时比简单地为容器提供访问器要多做一些工作,但范围访问器可以提供更大的灵活性和抽象性。

Rather than having a single member function return a container, consider having begin() and end() functions that return an iterator range, similar to what the Standard Library containers do themselves:

class terminal;

class terminal_iterator { /* defined appropriately */

struct symbol
{
    terminal_iterator begin_terminals() const;
    terminal_iterator end_terminals() const;
};

If you already have a std::vector<terminal> somewhere that you are going to be iterating over, you can just typedef terminal const* terminal_iterator; (or use a similar typedef) and define the member functions accordingly.

If you don't have a container (i.e., the member function would materialize the sequence itself), you can consider writing your own iterator class that generates the sequence.

Providing begin() and end() range accessors is sometimes a bit more work than simply providing an accessor for a container, but range accessors make for greater flexibility and abstraction.

随风而去 2024-12-16 16:52:55

前向声明+智能指针(尽管这现在将把东西存储在堆上而不是堆栈上......可能是不可取的)

#ifndef SYMBOL_H
#define SYMBOL_H

#include <vector>
#include <memory>

using namespace std;

class terminal; // Make a forward declaration like this

class symbol {
   public:
        vector<shared_ptr<terminal>> first();
        virtual void polymorphable();
};

#endif

Forward declaration + smart pointer (although this will now store things on the heap instead of stack... could be undesirable)

#ifndef SYMBOL_H
#define SYMBOL_H

#include <vector>
#include <memory>

using namespace std;

class terminal; // Make a forward declaration like this

class symbol {
   public:
        vector<shared_ptr<terminal>> first();
        virtual void polymorphable();
};

#endif
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文