Метод класса C ++, возвращает вектор <subclass> - PullRequest
1 голос
/ 11 октября 2011

У меня небольшие проблемы с методом, который я пытаюсь написать для класса.У меня есть символ класса и класс терминала.Терминал класса расширяет символ класса, но один из методов символа класса должен возвращать вектор.Например:

#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

Однако при этом я получаю две ошибки при сборке, причем одна или другая идет первой: "терминал": undeclared identifier "в строке, которая определяет векторную функцию, и" 'symbol': base class undefined "в строке с определением класса терминала.

Как мне решить это 'a требует b', 'б требует 'проблема?

Ответы [ 7 ]

3 голосов
/ 11 октября 2011

Избегайте циклических зависимостей, используя Форвардные объявления .

class terminal;

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

Существует предположение, что этот подход не определен согласно стандарту C ++.
@Бен Войт указывает:

C ++ 03 стандарт В разделе 17.6.4.8 говорится:

"В частности, эффекты не определены в следующих случаях:... если неполный тип используется в качестве аргумента шаблона при создании экземпляра компонента шаблона , если это специально не разрешено для этого компонента ...

Если std::vector<X> f(); равно std::vector<X> экземпляр, обсуждается здесь . Если ответ там доказывает, что это так, то этот ответ бесполезен, и я его удалю, иначе он останется действительным.

2 голосов
/ 11 октября 2011

Редактировать: Следующее может быть запрещено стандартом (см. Комментарии). В этом случае вы просто не можете иметь правильную циклическую зависимость: если размер A зависит от размера члена типа B, а размер B зависит от размера члена типа A, тогда такое определение просто не имеет смысла.

Я не совсем уверен, применимо ли это к вашей ситуации, поскольку вы объявляете только функцию, тип возвращаемой информации которой не завершен, для которой допускается . Смотрите сопутствующий вопрос от Джеймса ; надеюсь, мы получим там определенный ответ.


Просто наперед объявить terminal:

class terminal;

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

Вы можете заранее объявить все, что только должно быть неполным типом. Неполные типы могут использоваться для формирования указателей, ссылок, сигнатур функций. Полный тип должен быть известен только тогда, когда переменные этого типа используются .

1 голос
/ 11 октября 2011

Используйте предварительные декларации.

Джеймс - объявления примечаний отличаются от экземпляра. Этот код отлично работает

#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 - Вы правы.

1 голос
/ 11 октября 2011
Класс

Base не должен ничего знать о классе Derived. Это важный принцип в объектно-ориентированном дизайне. Вы могли бы потенциально изменить базовый класс на:

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

Теперь first() возвращает вектор указателей на базовый класс. С полиморфизмом каждый указатель может фактически указывать на производный класс. Обратите внимание, что я изменил его, чтобы использовать указатели. Если вы измените его на vector<symbol>, это не сработает.

В качестве альтернативы, если вам действительно нужен базовый класс, чтобы узнать о существовании класса 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
0 голосов
/ 11 октября 2011

Вместо того, чтобы одна функция-член возвращала контейнер, рассмотрите возможность использования функций 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() иногда немного сложнее, чемпросто предоставляя средство доступа для контейнера, но средства доступа к диапазону обеспечивают большую гибкость и абстракцию.

0 голосов
/ 11 октября 2011

Объявление вперед + умный указатель (хотя теперь это будет хранить вещи в куче, а не в стеке ... может быть нежелательно)

#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
0 голосов
/ 11 октября 2011

Я думаю, что CURLYURURURING Template Pattern может помочь вам в этом:

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