"Неполный тип" в классе, который имеет один и тот же тип самого класса
У меня есть класс, который должен иметь частный член того же класса, что-то вроде:
class A {
private:
A member;
}
Но это говорит мне, что член является неполным типом. Зачем? Это не говорит мне неполный тип, если я использую указатель, но лучше не использовать указатель. Любая помощь приветствуется
Ответы
Ответ 1
В то время, когда вы объявляете своего участника, вы по-прежнему определяете класс A
, поэтому тип A
по-прежнему undefined.
Однако, когда вы пишете A*
, компилятор уже знает, что A
означает имя класса, поэтому тип "указатель на A" определен. Вот почему вы можете вставить указатель на тип, который вы определяете.
Такая же логика применяется и для других типов, поэтому, если вы просто пишете:
class Foo;
Вы объявляете класс Foo, но вы его никогда не определяете. Вы можете написать:
Foo* foo;
Но не:
Foo foo;
С другой стороны, какую структуру памяти вы ожидаете для своего типа A
, если компилятор разрешил рекурсивное определение?
Однако его иногда логически справедливо, чтобы иметь тип, который каким-то образом ссылается на другой экземпляр того же типа. Обычно люди используют указатели для этого или даже лучше: умные указатели (например, boost::shared_ptr
), чтобы избежать необходимости ручного удаления.
Что-то вроде:
class A
{
private:
boost::shared_ptr<A> member;
};
Ответ 2
Это рабочий пример того, чего вы пытаетесь достичь:
class A {
public:
A() : a(new A()) {}
~A() { delete a; a = nullptr; }
private:
A* a;
};
A a;
Счастливое переполнение стека!
Ответ 3
A
является "неполным" до конца его определения (хотя это не включает тела функций-членов).
Одна из причин этого заключается в том, что до тех пор, пока не закончится определение, нет способа узнать, насколько велика A
(которая зависит от суммы размеров членов и нескольких других вещей). Ваш код - отличный пример: ваш тип A
определяется размером типа A
.
Очевидно, что объект типа A
может не содержать объект-член, который также имеет тип A
.
Вам нужно будет сохранить указатель или ссылку; желая сохранить либо, возможно, подозревается.
Ответ 4
Вы не можете включить A в A. Если вы смогли это сделать, и вы заявили, например, A a;
, вам нужно будет бесконечно ссылаться на a.member.member.member...
. Вы не имеете столько оперативной памяти.
Ответ 5
Как экземпляр class A
также содержит другой экземпляр class A
?
Он может содержать указатель на A, если вы хотите.
Ответ 6
Этот тип ошибки возникает, если вы пытаетесь использовать класс, который еще не был полностью ОПРЕДЕЛЕН.
Попробуйте вместо этого использовать A* member
.
Ответ 7
Проблема возникает, когда компилятор сталкивается с объектом A в коде.
Компилятор протирает руку и задает объект A. При этом он увидит, что A имеет член, который снова имеет тип A. Поэтому для завершения создания A теперь нужно создать экземпляр другого A, а в делая это, он должен создать экземпляр другого А и т.д. Вы можете видеть, что это закончится рекурсией без ограничений. Следовательно, это не допускается. Компилятор гарантирует, что он знает все типы и требования к памяти всех членов, прежде чем он начнет создавать экземпляр объекта класса.
Ответ 8
Простым способом понять причину отсутствия класса A
является попытка взглянуть на него с точки зрения компилятора.
Кроме всего прочего, компилятор должен иметь возможность вычислить размер объекта A
. Знание размера - это очень основное требование, которое проявляется во многих контекстах, таких как выделение пространства в автоматической памяти, вызов оператора new
и оценка sizeof(A)
. Однако для вычисления размера A
требуется знание размера A
, потому что A
является членом A
. Это приводит к бесконечной рекурсии.
Способ компилятора справиться с этой проблемой состоит в том, чтобы считать A
неполным, пока его определение не будет полностью известно. Вам разрешено объявлять указатели и ссылки на неполный класс, но вы не можете объявлять значения.