Не может сделать внешний класс как объект члена внутри внутреннего класса C++
У меня есть 3 вопроса по поводу кода ниже:
class cb
{
public:
int y_;
class iterator
{
public:
//void func() { y_ = 5; } // (1)
private:
int x_;
//cb a; // (2)
};
void funcCB() { }
};
class Human
{
public:
void func() const {
cb c; // (3)
c.funcCB();
}
// (4)
};
1- Почему я не могу использовать переменную-член класса Outer y_
внутри класса, как в (1)?
2- Почему я не могу создать объект из Outer класса внутри внутреннего класса, как в (2), но я могу только создать указатель и ссылку?
3- почему, если я переместил строку в (3) cb c;
в строке (4) я получаю ошибку компиляции?
3 ответа
- Вы не можете использовать поле внешнего класса, потому что ваш итератор класса не имеет прямого доступа к нему. Прежде всего, почему вы хотите иметь доступ к этому полю? Я бы сначала подумал о перепроектировании вашего решения. Но, как вы хотите, вот что вы можете сделать, чтобы решить эту проблему. Вам нужно будет иметь ссылку на объект, который является вашим родителем
class cb
{
public:
cb(): it(*this) {} // Passing the reference to ourself for iterator object init to have access in iterator class access to y_ field.
int y_;
class iterator
{
public:
iterator(cb& ob): a(ob) {}
void func() { a.y_ = 5; }
private:
int x_;
cb& a; // Reference to parent object
};
iterator it; // Added because I don't see the point where you don't want to have that object in your cb class.
void funcCB() { }
};
Я не уверен, чего бы вы хотели добиться здесь. Он выдаст неполный тип, потому что вы захотите создать объект cb, в котором вы создадите объект итератора, в котором вы создадите объект cb, в котором вы снова создадите итератор, чтобы получить бесконечную рекурсию (если я правильно понимаю). Когда вы указываете итератор на конкретный объект, он останавливает цикл.
Вы должны изменить класс cb funcCB на
потому что в противном случае в классе Human понимается, что в func(), которая является константной функцией , вы хотите вызвать funcCB(), которая не изменит объект c из класса Human, и есть разница между квалификаторами, потому что у вас нет константной функции в тот случай. Вы также можете удалить квалификатор const в классе Human для func, и это тоже сработает. Ошибка здесь возникает, когда вы не меняете квалификаторы, потому что это отличается, когда у вас есть константная функция, в которой вы создаете некоторый объект для дальнейшего использования, и когда у вас есть объект как поле класса, и вы вызываете функцию, которая не может изменить состояние класса Human и класс CB не имеет эквивалента функции const для funcCB.
Почему я не могу использовать переменную-член внешнего класса y_ внутри внутреннего класса, как в (1)?
Поскольку это нестатический член данных класса, это означает, что мы должны получить к нему доступ для определенного объекта. Но тут возникает проблема. Оператор присваиванияy_ = 5;
эквивалентно:
vvvv--------------->this points to an object of type iterator and not cb
this->y_ = 5;
В показанном выше эквивалентном утвержденииthis
указатель указывает на текущий экземпляр типаiterator
и не . Но так как для доступа мы должны использовать объект типа, мы получаем указанную ошибку.
В основном следует обращаться к объекту типа . Например, вы можете сделатьfunc
иметь параметр типа, а затем получить доступy_
как показано ниже:
class cb
{
public:
int y_;
class iterator
{
public:
//------------vvv------------------->pass object of type cb by reference
void func(cb& it) { it.y_ = 5; } // (1) OK NOW
//----------------------^^---------->access member y_ on object it
private:
int x_;
};
void funcCB() { }
};
Почему я не могу создать объект из внешнего класса внутри внутреннего класса, как в (2), но я могу создать только указатель и ссылку?
Потому что в какой-то момент класс неполный , и поэтому в какой-то момент мы не можем создать нестатический элемент данных типа . Это видно из полной документации типа , в которой говорится:
Любой из следующих контекстов требует, чтобы тип был полным:
- объявление члена данных нестатического класса типа
T
;
Это означает, что в точке#2
у нас не может быть объявления для нестатического члена данных типа, но мы все еще можем иметь объявление для нестатического члена данных типаcb&
илиcb*
поскольку у нас может быть указатель или ссылка на неполный тип, напримерcb
.
Почему, если я переместил строку на (3)
cb c
; в строке (4) я получаю ошибку компиляции?
Если вы переместили строку#3
(cb c;
) ровняться#4
, вы не получите никаких ошибок времени компиляции. Демо
class Human
{
public:
void func() const {
//cb c; // (3)
//c.funcCB();
}
cb c; // (4) perfectly fine
};
Простите, если я не совсем понимаю - мой C++ немного ржавый.
По вопросу (1). Вы пробовали использовать ключевое слово "this"?
Например,:
void func() { this->y_ = 5; }
Вот пример, почти идентичный вашему:
На вопрос 2 вы запрашиваете рекурсивное включение объекта. Это все равно что сказать: что такое лук? Это кожура, которая содержит лук. Хорошо, что Лук внутри также является Пилингом, который содержит Лук, затем Пил, затем Лук и т. Д. Без какого-либо механизма для остановки (Лук может быть Ядром, который ничего не содержит), первая реализация объекта Лука будет цикл бесконечно, пока вся память не будет израсходована.
Как ссылка, память может быть восстановлена только при необходимости, а не раньше.
На вопрос 3 я выбираю ленивый способ сказать, что это всего лишь правила.
ОБНОВЛЕНИЕ: я не на работе сейчас, поэтому у меня было больше времени, чтобы сделать немного больше исследований. Как я уже сказал, мой C++ немного ржавый.
По сути, для Q 1 вы пытаетесь получить доступ к членам класса "cb" из класса "iterator". Мои исследования показывают, что, поскольку "итератор" является подклассом "cb", он не получает никаких специальных прав доступа. Таким образом, вы не можете получить доступ к "cb::y_" из "iterator", и вы также не можете использовать "this->y_". Методы (функции) внутри класса могут напрямую обращаться к "y_". Подклассы не могут.
Обратите внимание на следующее, которое успешно компилируется:
class cb {
public:
int y_;
class iterator {
public:
void func() {
cb *z = new cb();
z->y_ = 5;
} // (1)
private:
int x_;
cb *a; // (2)
};
void funcCB() { }
};
class Human
{
public:
void func() const {
cb c; // (3)
c.funcCB();
}
// (4)
cb *z;
};
Это, вероятно, не то, что вы пытаетесь достичь, но я ожидаю, что то, что вы хотите сделать, не может быть сделано так, как вы хотите. Лучше всего было бы реализовать "итератор" как отдельный класс, отличный от "cb", и сделать его "другом" классом "cb". Или, может быть, еще лучше и проще, просто включите в класс набор итераторов. Методы будут иметь полный доступ ко всем атрибутам класса.
Также обратите внимание на указатель на ссылку "cb". Он не компилируется как простой экземпляр объекта в подклассе. Опять Луковый вопрос.
Кстати, помните, что C++ - это не Паскаль. Это не Java. Он может выполнять подклассы, но, как вы видите, он не очень хорошо работает, или, по крайней мере, он не делает их так, как это делают другие языки. Вам лучше создавать каждый класс, полностью отличающийся от любого другого класса, кроме обычного наследования. Это своего рода неофициальный "путь C++".