Это называется неполным типом , и является концепцией C++, унаследованной от C.
Неполные типы работают Таким образом: прежде чем вы определили class B
в своем коде, вы можете использовать class B varname
как, скажем, аргумент в прототипах функций, или использовать указатели на этот тип как class B* ptr
- везде, где не требуется никаких подробностей о типе, кроме его имени ,
На самом деле, вы можете написать его по-разному - просто поставить class B;
(который должен работать как класс декларации), прежде чем использовать его в качестве неполного типа, а затем вы можете написать B varname
вместо class B varname
.
Указатели на неполные типы часто используются с opaque pointers, которые, вероятно, являются наиболее распространенным использованием неполных типов в C++. Острые указатели описаны достаточно хорошо в связанной статье Википедии. Короче говоря, это метод, который позволяет вашему API скрывать всю реализацию класса.
Использование неполного типа syntaxis вы описали в своем вопросе, пример кода из Википедии:
//header file:
class Handle {
public:
/* ... */
private:
struct CheshireCat; // Not defined here
CheshireCat* smile; // Handle
};
//CPP file:
struct Handle::CheshireCat {
int a;
int b;
};
можно переписать в виде:
//header file:
class Handle {
public:
/* ... */
private:
struct CheshireCat* smile; // Handle
};
//CPP file:
struct CheshireCat {
int a;
int b;
};
Примечание этого: эти фрагменты кода не эквивалентно, поскольку первый определяет тип Handle::CheshireCat
, а последний имеет его просто как CheshireCat
.
На код, который вы дали в качестве примера:
В C, причина не компиляции довольно проста: struct A
в прототипе функции является декларация области видимости прототипа функции, и, таким образом, он отличается от struct A
, который объявлен последним. C и C++ имеют несколько разные правила для этого конкретного случая. Если вы переадресовываете struct
следующим образом: struct A;
перед прототипом функции, он будет скомпилирован на обоих языках!
Другие известные применения этого syntaxis:
Это syntaxis занимает важное место в рамках обратной совместимости С ++ с C. Вы видите, в C, после определения или переадресации объявления struct
следующим образом: struct A {};
или struct A;
, фактическое имя этого типа будет struct A
. Чтобы использовать это имя как A
, вам необходимо использовать typedef
. C++ делает последнее автоматически, но позволяет использовать A
как struct A
, так и A
. То же самое касается class
-es union
-s, и enum
-s.
Собственно, некоторые утверждают, что это имеет семантическое значение. Рассмотрим функцию со следующей сигнатурой: int asdf(A *paramname)
. Знаете ли вы, что A
- это просто просмотр декларации? Это class
, struct
, enum
или union
? Люди говорят, что подобная подпись может быть сделана яснее: int asdf(enum A *paramname)
. Это хороший способ написания самодокументирующего кода.
Это, в основном, осталось от C. – chris
Я не вижу, как 'visit' может быть переадресацией' Composite', указав его как параметр. Если что-то 'Composite' должно быть объявлено перед' visit' для его компиляции? – asimes
Вот что я тоже думал, но это не так. – user975989