Метод и переменная одного имени в классе: ошибка компиляции в C++, но не в Java?
Вопрос:
Я столкнулся с проблемой компиляции в C++, когда пытаюсь определить переменную и метод с одинаковым именем в классе. Ниже приведен код, который вызывает ошибку при компиляции:
class Test {
bool isVal() const {
return isVal;
}
private:
bool isVal;
};
Когда я пытаюсь скомпилировать этот файл, я получаю следующую ошибку:
testClass.cpp:9: declaration of `bool Test::isVal'
testClass.cpp:3: conflicts with previous declaration `bool Test::isVal()'
При этом тот же самый подход работает в Java, как показано ниже:
class Test {
private boolean isVal;
public boolean isVal() {
return isVal;
}
}
Почему возникает ошибка компиляции в C++, но не в Java? Как корректно решить эту проблему в C++?
5 ответ(ов)
Поскольку C++ — это не Java, вы можете получить адрес члена класса:
&Test::isVal
Таким образом, нельзя иметь несколько членов с одинаковым именем, за исключением перегрузки методов класса. Даже если бы можно было разрешить неоднозначность каким-то образом через приведение типов, следующая проблема уже возникла бы в других местах.
В C++ многие разработчики, включая меня, обычно называются члены данных частью специальным образом, например, добавляя префикс m
перед именем. Это позволяет избежать проблемы:
class Test {
public:
bool IsVal() const { return mIsVal; }
private:
bool mIsVal;
};
Использование такого соглашения об именах помогает явно различать члены класса и параметры или локальные переменные, что делает код более читаемым и поддерживаемым.
В C++ используется манглирование имен для функций и глобальных переменных, в то время как локальные переменные не подлежат манглированию. Проблема возникает из-за того, что в C (соответственно, и в C++) возможно обращаться к адресу переменной или функции. Например:
struct noob{
bool noobvar;
void noobvar(){};
};
Можно задать вопрос: почему бы не применять манглирование имен и к локальным переменным, обеспечивая внутреннее локальное представление, например:
bool __noobvar_avar;
void __noobvar_void_fun;
Допустим, они получают адреса во время выполнения, скажем, 0x000A для переменной и 0x00C0 для функции.
Однако если в коде написать:
&noob::noobvar
Что должна сделать программа?
- Вернуть адрес переменной noobvar, т.е. 0x000A
- Вернуть адрес функции noobvar, т.е. 0x00C0
Как видно, поскольку в C, а следовательно, и в C++, можно запрашивать "адрес переменной", иметь переменные и функции с одинаковыми именами в одной и той же области видимости было бы неправильно. Таким образом, в C++ не допускается наличие функций и переменных с одинаковыми именами в одном и том же скоупе, чтобы избежать конфликтов при обращении по адресу.
Да, вы правы в том, что функции в C/C++ являются указателями на адрес в памяти, где расположен код. Это действительно может вызвать неоднозначность, когда вы имеете в виду переменную с именем isVal
, и одновременно функцию с тем же именем.
В языке C/C++ функции и переменные могут иметь одно и то же имя, но это может привести к конфликтам и путанице. Когда происходит такая ситуация, компилятор должен определить, о каком элементе идет речь, исходя из контекста, в котором оно используется. Если компилятор сталкивается с многозначностью, он выдает ошибку.
Чтобы избежать такой неоднозначности, рекомендуется использовать разные имена для переменных и функций. Например, можно использовать префиксы или суффиксы, чтобы четко обозначить, что именно это переменная или функция.
bool isVal; // переменная
void isValFunc() { /* код функции */ } // функция
Таким образом, можно гарантировать, что код будет более читаемым и понятным, а также минимизировать возможность возникновения ошибок.
Краткий ответ: "потому что именно так работает C++." В C++ нет отдельного пространства имен для переменных-членов и методов, в отличие от Java (по слухам, так как я сам это не пробовал).
В любом случае, вспомните старую историю о человеке, который пришел к доктору и сказал: "Доктор, у меня болит, когда я делаю это." На что доктор ответил: "Ну, не делай это!" Это языковая особенность, которая со временем может превратиться в глупую ошибку программиста.
В следующем разделе черновика стандарта C++ N3337 указано, когда имя может быть перегружено.
13 Перегрузка
1 Когда для одного и того же имени в одном и том же объеме указан два или более различных объявления, это имя считается перегруженным. По аналогии, два объявления в одном объеме, которые указывают на одно и то же имя, но с различными типами, называются перегруженными объявлениями. Только объявления функций и шаблонов функций могут быть перегружены; объявления переменных и типов перегружать нельзя.
Когда вы определяете класс следующим образом:
class Test {
bool isVal() const {
return isVal;
}
private:
bool isVal;
};
вы перегружаете имя isVal
вScope вашего класса. Такая перегрузка допустима только в том случае, если isVal
является членом функции. Перегрузка недопустима, когда isVal
является членом переменной.
В данном случае у вас есть как метод (функция), так и член класса (переменная) с одним и тем же именем isVal
. Это может привести к неопределенному поведению и путанице. Если вы хотите использовать одно и то же имя для метода и переменной, вам следует рассмотреть возможность использования других имен для избежания конфликтов.
Как сопоставить любой символ на нескольких строках в регулярном выражении?
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?
Как объявить массив в одну строку?
Неопределенная ссылка на виртуальную таблицу (vtable)
Какие проблемы следует учитывать при переопределении equals и hashCode в Java?