C ++: доступ к постоянным переменным-членам через класс или экземпляр?

В C ++ есть ли причина не обращаться к статическим переменным-членам через экземпляр класса? Я знаю, что Java не одобряет это, и мне было интересно, имеет ли это значение для C ++. Пример:

class Foo {
  static const int ZERO = 0;
  static const int ONE = 1;
  ...
};


void bar(const Foo& inst) {
   // is this ok?
   int val1 = inst.ZERO;
   // or should I prefer:
   int val2 = Foo::ZERO
   ...
};

У меня бонусный второй вопрос. Если я объявляю статический двойник, я должен где-то его определить, и это определение должно повторять тип. Почему нужно повторять тип? Например:

In a header:
  class Foo {
    static const double d;
  };
In a source file:
  const double Foo::d = 42;

Почему я должен повторять часть "const double" в моем файле cpp?


person criddell    schedule 06.08.2009    source источник
comment
Не точный дубликат, но я думаю, что, вероятно, применимы те же ответы: stackoverflow.com/questions/840522/   -  person Fred Larson    schedule 07.08.2009
comment
да. Прочтите ответ Адама Розенталя, чтобы узнать очень вескую причину, почему вам следует предпочесть класс в качестве префикса, а не экземпляра.   -  person quark    schedule 07.08.2009


Ответы (6)


Что касается первого вопроса, помимо вопроса стиля (он делает очевидным, что это переменная класса и не имеет связанного объекта), Фред Ларсен в комментариях к вопросу ссылается на предыдущий вопрос. Прочтите Ответ Адама Розенталя на очень вескую причину, по которой вы хотите быть осторожными с этим. (Я бы проголосовал за Фреда, если бы он опубликовал его в качестве ответа, но я не могу поверить в то, где это должно быть. Я проголосовал за Адама.)

Что касается вашего второго вопроса:

Почему я должен повторять часть "const double" в моем файле cpp?

Вы должны повторить тип в первую очередь как деталь реализации: это то, как компилятор C ++ анализирует объявление. Это не совсем идеальный вариант для локальных переменных, и C ++ 1x (ранее C ++ 0x) использует ключевое слово auto, чтобы избежать повторения для обычных функциональных переменных.

Итак, это:

vector<string> v;
vector<string>::iterator it = v.begin();

может стать таким:

vector<string> v;
auto it = v.begin();

Нет четкой причины, по которой это не может работать и со статикой, поэтому в вашем случае thos:

const double Foo::d = 42;

вполне может стать этим.

static Foo::d = 42;

Ключ в том, чтобы иметь какой-нибудь способ идентифицировать это как объявление.

Заметьте, я не говорю ясной причины: грамматика C ++ - живая легенда: чрезвычайно трудно охватить все ее крайние случаи. Я не думаю, что сказанное выше неоднозначно, но может быть. Если это не так, они могли бы добавить это к языку. Расскажите им об этом ... для C ++ 2x: /.

person quark    schedule 06.08.2009

Я бы предпочел Foo::ZERO inst.ZERO, потому что он более ясно показывает, что происходит. Однако в методе класса Foo я бы просто использовал ZERO.

Что касается вопроса о бонусе, const просто является частью полного типа.

person Reunanen    schedule 06.08.2009
comment
Я понимаю, что const является частью типа. Мне интересно, зачем мне вообще повторять типаж. Я просто хочу сказать: Foo :: d = 42; В заголовочном файле объявлен только один Foo :: d. Представляет ли это возможность некоторой двусмысленности, которую я здесь не вижу? Может ли быть функция-член с именем d? Я знаю, что есть веская причина для повторения этого типа, я просто не знаю, что это такое. - person criddell; 07.08.2009
comment
Может, это просто правила языка? - person Juan; 07.08.2009
comment
Я попытался объяснить это в комментарии, но не хватило места (вздох). Отвечаю на него ниже. - person quark; 07.08.2009

Учитывая, что вы объявляете их статическими и делаете их константами класса, я бы использовал Foo :: Zero, чтобы сообщить о намерениях случайному, а не случайному читателю вашего кода.

Я бы также заменил все имена констант в верхнем регистре, то есть превратил Foo :: ZERO в Foo :: Zero. Обычное соглашение для макросов препроцессора заключается в том, чтобы называть их заглавными буквами, а использование аналогичной схемы именования для ваших констант C ++ вызывает проблемы, потому что препроцессор может просто пропустить ваши константы C ++, и вы получите очень интересные сообщения об ошибках.

person Timo Geusch    schedule 06.08.2009
comment
Ничего себе, хороший момент по соглашению об именах. На самом деле меня это укусило не один раз (пожалуйста, все - перестаньте # определять ПИ !!!) - person criddell; 07.08.2009

Я бы использовал Foo :: ZERO, но это только я. Особенно, если вы производите от Foo, что сбивает с толку.

Что касается вашего второго вопроса, вам нужно создать память для двойного значения, и это происходит в модуле реализации.

Я думаю, что единственный тип, для которого вам не нужно создавать память, - это интегральный тип const. Затем вы можете поместить значение в файл заголовка. Но поскольку у него нет адреса, вы не сможете передать его по ссылке на функцию, если не поместите определение в файл .cpp. (Похоже, это так и работает с gcc).

person Juan    schedule 06.08.2009

Неважно, какую форму вы используете в своем примере. Оба они означают одно и то же. Я бы предпочел использовать классовый метод в целом, потому что у вас не всегда может быть экземпляр, удобный для использования оператора точки.

Приятно иметь оба варианта, если вы считаете, что кто-то пишет функцию-шаблон. Возможно, они закодировали функцию с помощью оператора точки. Ваш класс со статическим членом класса все равно можно использовать для создания экземпляра шаблона, потому что этот синтаксис поддерживается.

Что касается вашего бонусного вопроса, то язык таков. Всегда нужно объявлять полный тип. Возможно, вам стоит задать этот вопрос в отдельном вопросе.

person Brian Neal    schedule 06.08.2009

лично я бы использовал анонимное перечисление. Хотя конечный результат точно такой же :)

Что касается ваших вопросов. Я определенно предпочитаю Foo :: Zero, потому что, глядя на него, очевидно, к чему вы обращаетесь. inst.Zero требует, чтобы вы заранее выяснили, какой тип inst.

Вы должны повторить тип данных, потому что так работает C ++. Точно так же, если вы написали следующее в заголовочном файле.

extern int foo;

Вам все равно нужно будет упомянуть

int foo

в файле CPP. Как упоминалось в pukku, вы объявляете переменную типа «const int». Таким образом, "const int" необходимо повторить в определении переменной.

person Goz    schedule 06.08.2009
comment
анонимные перечисления задают определенный размер хранилища (int). Если вы хотите, чтобы константы были другого типа, вы не можете использовать анонимные перечисления. - person Chris Cleeland; 07.08.2009