Так что я узнал о занятиях и наткнулся на то, что нашел довольно неловким для меня.
class Nebla
{
public:
int test()
{
printout();
return x;
}
void printout()
{
printout2();
}
private:
int x,y;
void printout2()
{
cout<<"Testing my class";
}
};
Я обнаружил, что в классе я могу использовать функции, прежде чем объявить их (прототип их)
Вы можете видеть, что я использовал
printout()
,printout2()
до объявления.
И я могу использовать переменные также до объявления их
Вы можете видеть, что я сделал
return x
; перед объявлением х.
Почему я могу использовать функции и переменные в классах до объявления, но вне класса, если я это сделаю, я получу ошибку?
Спасибо
Хороший вопрос; Я полагался на эту функцию в течение многих лет, не думая об этом. Я просмотрел несколько книг по С ++, чтобы найти ответ, в том числе и Страуструпа Язык программирования C ++ а также Аннотированное C ++ Справочное руководство, но никто не признает и не объясняет разницу. Но я думаю, что могу рассуждать об этом.
Я полагаю, что причина того, что ваш пример работает, состоит в том, что тела вашего test
а также printout
действительно не там, где они появляются в вашем файле. Код
class MyClass {
void someFun() {
x = 5;
}
int x;
};
…который, по-видимому, нарушает правило необходимости объявлять переменные перед их использованием, фактически эквивалентен:
class MyClass {
void someFun();
int x;
};
void MyClass::someFun() {
x = 5;
}
Как только мы переписываем это так, становится очевидно, что материал внутри вашего MyClass
определение на самом деле список декларации. И те могут быть в любом порядке. Вы не полагаетесь на x
пока не объявлено. Я знаю, что это правда, потому что если бы вы переписали пример следующим образом,
void MyClass::someFun() {
x = 5;
}
class MyClass {
void someFun();
int x;
};
…это больше не будет компилироваться! Итак, определение класса идет первым (с его полным списком членов), а затем ваши методы могут использовать любой член, не обращая внимания на порядок, в котором они объявлены в классе.
Последняя часть головоломки состоит в том, что C ++ запрещает объявлять любого члена класса за пределами определения класса, поэтому, как только компилятор обработает ваше определение класса, он узнает полный список членов класса. Об этом говорится на стр.170 Страуструпа Аннотированное C ++ Справочное руководство: Список членов определяет полный набор членов класса. Никто не может быть добавлен в другом месте. «
Спасибо, что заставили меня исследовать это; Я узнал что-то новое сегодня. 🙂
Просто чтобы прояснить, это требуется стандартом C ++, а не только тем, как несколько компиляторов обрабатывают определения классов.
N3242 3.3.7:
Потенциальная область действия имени, объявленного в классе, состоит не только из декларативной области, следующей за точкой объявления имени, но также и из всех тел функций. скобки или равно-инициализаторы нестатических членов-данных и аргументов по умолчанию в этом классе (включая такие вещи во вложенных классах).
Помимо хорошего ответа Филиппа, Страуструп дает хорошее объяснение Правила поиска имени в Дизайн и эволюция C ++. Это описано в «6.3 Разъяснения». В 6.3.1.1, «Правила поиска имени ARM», он упоминает 2 правила, определенные в РУКА:
[1] Правило переопределения типа: имя типа не может быть переопределено в классе после его использования там. [2] Правило переписывания: встроенные функции-члены анализируются так, как если бы они были определены сразу после окончания объявлений их классов.Таким образом, в вашем случае будет применяться правило перезаписи (как выводил Филипп), поэтому вы можете пересылать ссылки на этих учеников.
Эта книга может представлять большой исторический интерес (она написана в 1994 году), но я думаю, что эти правила применяются сегодня так же.
Причина, по которой вы можете это сделать, заключается в том, что к тому времени, когда вы test
, printout
или же printout2
, они уже были созданы. Если вы вызываете функцию вне произвольная функция до ее реализации, тогда вы получите ошибку.
Представьте, что функции-члены класса асинхронны с потоком оценки остальной части класса. Это не будет работать с автономными функциями, но вы можете получить доступ к элементам данных, которые еще не были созданы. Я не совсем уверен, почему мы можем сделать это, но я думаю, что это связано с мгновенным созданием объекта класса.