null — c ++ Полиморфизм: предварительное объявление? Включать охрану? или что-то еще?

Я знаю, что существует миллион тем, объясняющих, как использовать предварительную декларацию и включать охранников. Я приехал из Явы, где мне не пришлось беспокоиться об этих вещах. Это моя первая программа на С ++, так что со мной (и давать советы).

Вот моя проблема …

у меня есть родительский класс под названием Token

я имею два детских класса, ErrorToken и EndToken. Каждый из этих классов должен иметь возможность создавать объект другого, и вернуть его через вызов функции. Каждый из них имеет свой отдельный класс заголовков.

Таким образом, ErrorToken должен иметь возможность создавать новый объект EndToken и возвращать его, а EndToken должен иметь возможность создавать новый объект ErrorToken и возвращать его.

Что будет лучшим способом добиться успеха в этом? Я бы предпочел, чтобы он был как можно более совместимым с кросс-компилятором, Я не хочу использовать прагму один раз. (но это по сути то, что я ищу).

В идеале я хотел бы иметь возможность сделать что-то вроде этого …

#ifndef ErrorToken
#include "ErrorToken.h"#endif

Но это никогда не работает (и я думаю, что это неправильно? Может кто-нибудь помочь мне понять, почему?).

Я понимаю, что предварительное объявление работает только с сигнатурами функций и указателями (это правильно?), Поэтому я не думаю, что это сработает в моей ситуации, поскольку мне нужно, чтобы он мог запускать конструктор … или компилятору просто нужно знать, что конструктор завершается в этот момент?

1

Решение

Ну, используйте предварительные декларации. Как вы сказали, существуют миллионы объяснений, а теперь миллионы и одно:

ErrorToken.h:

#ifndef H_ERROR_TOKEN
#define H_ERROR_TOKEN

#include "Token.h"
class EndToken;

class ErrorToken : public Token
{
public:
EndToken makeEndToken();
};

#endif

EndToken.h:

#ifndef H_END_TOKEN
#define H_END_TOKEN

#include "Token.h"
class ErrorToken;

class EndToken : public Token
{
public:
ErrorToken makeErrorToken();
};

#endif

Теперь в каждом файле реализации вы можете включить оба заголовка:

#include "ErrorToken.h"#include "EndToken.h"
ErrorToken EndToken::makeErrorToken()
{
return ErrorToken();   // example
}

EndToken ErrorToken::makeEndToken()
{
return EndToken();
}

Как отметил @James Kanze, вы можете быть не уверены, как работает C ++. Следующий код может более соответствовать типу поведения, которое вы ожидаете от Java, и имеет больше смысла в полиморфном способе проектирования:

class Token { virtual ~Token()  {} };

class ErrorToken : public Token
{
std::unique_ptr<Token> makeEndToken();
};

class EndToken : public Token
{
std::unique_ptr<Token> makeErrorToken();
};

std::unique_ptr<Token> EndToken::makeErrorToken()
{
return { new ErrorToken; }
}

std::unique_ptr<Token> ErrorToken::makeEndToken()
{
return { new EndToken; }
}

Поскольку вы обрабатываете объекты только через базовые указатели, заголовки не должны знать что-нибудь о других производных классах. (Я оставляю вам возможность подразделять код на файлы; каждый блок помещается в отдельный файл.)

2

Другие решения

Почему твой

#ifndef ErrorToken

неправильно? Во-первых, поместите его в заголовочный файл, во-вторых, убедитесь, что что-то на самом деле определяет ErrorToken


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

//"ErrorToken.h"#ifndef ERROR_TOKEN_INCLUDED
#define ERROR_TOKEN_INCLUDED

class EndToken;

class ErrorToken
{
public:
EndToken DoSomething();
};
#endif

//"EndToken.h"#ifndef END_TOKEN_INCLUDED
#define END_TOKEN_INCLUDED
class ErrorToken;

class EndToken
{
public:
ErrorToken DoSomething();
};
#endif

Было несколько предыдущих обсуждений круговых зависимостей: Вот, Вот а также Вот

Затем вы можете выполнить большую часть работы в файле cpp, где вы включаете другой заголовок, чтобы он знал полное определение класса, а не только предварительное объявление, и мог использовать его.

Сделайте первый шаг назад и спросите: «Они действительно должны знать друг о друге?»

1

По вопросам рекламы [email protected]