Если вы объявите открытую переменную в классе, вы можете изменить эту переменную из любой функции, которая также является членом этого класса.
Если вы объявляете переменную внутри функции, то ее область действия не выходит за пределы функции.
Так является ли переменная открытого класса по сути глобальной переменной, к которой может обращаться любой член класса?
Если это так, в чем разница между глобальной и общедоступной переменной?
Если вы объявите открытую переменную в классе, вы можете изменить эту переменную из любой функции, которая также является членом этого класса.
Не совсем: то же самое относится и к закрытым и защищенным переменным. То есть, если класс имеет 3 переменные, одна из которых является public
, один protected
и один private
функции, являющиеся членами этого класса, не могут изменять только общедоступную функцию; напротив, они могут изменять все 3. Членство в одном классе дает вам самые высокие разрешения. Разницу между этими 3 модификаторами доступа можно увидеть при попытке манипулировать этими переменными из вне этот класс: общедоступные переменные достижимы из любого места, защищенные переменные достижимы из того же класса и из производных от него классов (если они есть), а закрытые переменные доступны только из других членов того же класса.
Если вы объявляете переменную внутри функции, то ее область действия не выходит за пределы функции.
Да, и то же самое на самом деле относится к любому блоку: если вы объявляете переменную внутри for
петля, его область будет петлей. Функции — это всего лишь один из видов блоков.
Так является ли переменная открытого класса по сути глобальной переменной, к которой может обращаться любой член класса?
Нет, как я сказал выше, переменная открытого класса может быть доступна членам любой класс, и даже из чего-то, что вообще не принадлежит ни одному классу: это именно то, что означает «публичный».
Если это так, в чем разница между глобальной и общедоступной переменной?
Как сказал КориКрамер в его ответ, член класса живет внутри объекта, и объекты независимы друг от друга, поэтому, если вы создадите 10 объектов одного класса, у всех 10 будет собственная копия этой переменной. static
члены класса являются исключением, потому что они являются общими для всех объектов класса, и на самом деле им даже не нужен ни один объект (вы знакомы с концепцией одиночка? Без статических элементов это не сработало бы).
Практическое отличие: допустим, вы хотите сохранить имя файла в месте, доступном для всех ваших функций. Глобал подойдет. Вместо этого открытый член класса требует, чтобы вы сначала создали объект этого класса, а затем этот объект должен находиться в области видимости! Если вы создаете объект внутри main()
а затем вы хотите прочитать эту переменную из функции с именем write_results_to_file()
Таким образом, чтобы вы знали, в какой файл записывать данные, вы должны позаботиться о передаче объекта этой функции, иначе объект окажется вне области видимости, а открытый член будет недоступен, несмотря на то, что он является общедоступным.
Таким образом, глобальные переменные более удобны, так как требуют меньше работы. Это как раз та причина, по которой людям нравится их использовать, но учтите, что такая лень приводит к плохому коду: если вы используете глобальные переменные, писать код проще, но тогда труднее понять, как работает каждая функция. В идеале функция требует только своих аргументов, и тогда она может быть запущена и дать результат. В C ++ обычно вы можете увидеть аргументы функции, посмотрев ее объявление в файле .hh. Но если функция обращается к глобальной переменной, мы можем сказать, что функция использует «скрытый» аргумент, где «скрытый» означает, что он не отображается в своем объявлении. Как только программа вырастает до нетривиального размера, это на самом деле усложняет ситуацию, потому что трудно понять последствия изменений. Например, если программа изменяет глобальную переменную, это повлияет на все функции, использующие эту переменную, но в некоторых случаях это вообще не будет очевидным, и это приведет к незначительным ошибкам, которые трудно найти. В качестве другого примера, тестирование функции может стать намного сложнее: если вы напишете несколько тестовых случаев, вы ожидаете, что всякий раз, когда вы запускаете их, вы получаете один и тот же результат, если передаваемые аргументы одинаковы. Вместо этого результат будет зависеть от аргументов (что ясно), а также от значения глобальных переменных (что не очевидно).
Вот почему глобальные переменные не одобряются: потому что они позволяют легко и быстро писать код, но трудно и медленно его понимать и поддерживать (добавлять новые функции или исправлять ошибки). Их использование не запрещено, но вы должны делать это осторожно и экономно.
Более важная семантика, которую вы упускаете, это то, что переменная члена класса (если это не static
) является специфическим для каждый экземпляр этого класса. Например
class Foo
{
public:
Foo() = default;
int x;
};
Если бы я сказал
Foo a{};
Foo b{};
a.x = 1;
b.x = 7;
a.x != b.x; // value is different
&a.x != &b.x // address is different
Обратите внимание, что переменная-член x
имеет другое значение, имеет другой адрес (так как это совершенно другой int
) и принадлежит каждому экземпляру Foo
,
Теперь о упоминании static
обратите внимание, что я не могу сделать это
Foo::x = 5; // only works if x is static
потому что мне нужен фактический пример из Foo
для которого получить доступ к своим переменным-членам. Скорее static
элемент не требует экземпляра, и весь класс имеет один экземпляр этого элемента, который является общим.