C ++类方法,返回vector <subclass> </subclass>

时间:2011-10-11 17:46:04

标签: c++ stl polymorphism

我正在尝试为类编写的方法遇到一些麻烦。我有班级符号和班级终端。 class终端扩展了类符号,但类符号的方法之一需要返回一个向量。 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

定义了班级终端:

#ifndef TERMINAL_H
#define TERMINAL_H

#include "symbol.h"

using namespace std;

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

#endif

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

如何解决这个'a要求b','b需要'问题?

7 个答案:

答案 0 :(得分:3)

使用 Forward Declarations

避免循环依赖
class terminal;

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

根据C ++标准,有人猜测这种方法是未定义的 @Ben Voight指出:

C ++ 03标准第17.6.4.8节说:

  

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

std::vector<X> f();std::vector<X>实例化,正在讨论 here 。如果那里的答案证明那么这个答案没有用,我会删除相同的,否则这一点仍然有效。

答案 1 :(得分:2)

编辑:标准可能不允许以下内容(请参阅注释)。在这种情况下,您根本无法获得正确的循环依赖关系:如果A的大小取决于B类型成员的大小,但B的大小取决于大小对于A类型的成员,那么这样的定义根本没有意义。

我不完全确定这是否适用于您的情况,因为您只声明了一个返回类型不完整的函数,允许 。见服务员question by James;希望我们能在那里得到明确答案。


只需向前声明terminal

class terminal;

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

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

答案 2 :(得分:1)

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

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

现在,first()返回指向基类的指针向量。使用多态,每个指针实际上可以指向派生类。请注意,我将其更改为使用指针。如果您将其更改为只会vector<symbol>无效。

另外,如果你真的需要Base类来了解Derived类的存在,你可以转发声明派生类:

#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

答案 3 :(得分:1)

使用前向声明。

詹姆斯 - 注释声明与实例不同。这段代码工作正常

#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 - 你是对的。

答案 4 :(得分:0)

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

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;

答案 5 :(得分:0)

前向声明+智能指针(虽然这会将内容存储在堆上而不是堆栈中......可能是不合需要的)

#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

答案 6 :(得分:0)

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

class terminal;

class terminal_iterator { /* defined appropriately */

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

如果你已经在某个地方有std::vector<terminal>迭代,你可以typedef terminal const* terminal_iterator;(或使用类似的typedef)并相应地定义成员函数。

如果你没有容器(即,成员函数会实现序列本身),你可以考虑编写自己的生成序列的迭代器类。

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