c++ с использованием объявления, области видимости и контроля доступа


Обычно объявление using используется для приведения в область видимости некоторых функций-членов базовых классов, которые в противном случае были бы скрыты. С этой точки зрения это всего лишь механизм, позволяющий сделать доступную информацию более удобной в использовании.
Однако: объявление "using" может также использоваться для изменения ограничений доступа (не только для функций, но и для атрибутов). Например:

class C{
public:
  int a;
  void g(){ cout << "C:g()n"; }
  C() : a(0){}
};

class D : public C{
private:
  using C::a;
  using C::g;
public:
  D() { a = 1; }
};

int main(void){
  D d;
  cout << d.a << endl;  //error: a is inaccessible
  C *cp = &d;
  cout << cp->a << endl; //works
  d.g();  //error: g is inaccessible
  cp->g();  //works
  return 0;
}

Я думаю, что это ограничение доступа в производном классе фактически бесполезно, потому что вы всегда можете получить доступ к g () и a из указателя на базовый класс. Так не должно ли быть хотя бы какое-то предупреждение компилятора? Или было бы еще лучше запретить такое ограничение доступа производным классом? Объявление using-это не единственная возможность добавить ограничения к доступу. Это также можно сделать, переопределив функцию базового класса и поместив ее в раздел с большими ограничениями доступа. Есть ли разумные примеры, когда действительно необходимо ограничить доступ в интернет? таким образом? Если нет, то я не понимаю, почему это должно быть разрешено.

И еще одно: по крайней мере, с g++ один и тот же код хорошо компилируется без слова "using". Это означает для примера выше: можно написать C:: a; и C:: g; вместо использования C:: a; использование C:: g; является ли первое только сокращением для последнего или есть некоторые тонкие различия?

/ / EDIT:
Итак, из обсуждения и ответов ниже мой вывод будет следующим:
- разрешено ограничивать ограничения доступа в производные классы с открытым наследованием
- есть полезные примеры, где его можно было бы использовать
- его использование может вызвать проблемы в сочетании с шаблонами (например, производный класс больше не может быть допустимым параметром для некоторого класса/функции шаблона, хотя его база является)
- более чистый дизайн языка не должен допускать такого использования
- компилятор мог бы по крайней мере выдать какое-то предупреждение

3 10

3 ответа:

Что касается Вашего объявления без using: они называются "объявлениями доступа" и являются устаревшими. Вот текст из стандарта, из 11.3/1:

Доступ члена базового класса может быть изменен в производном классе путем упоминания его квалифицированного идентификатора в объявление производного класса. Такое упоминание называется декларацией доступа. Эффект объявления доступаqualified-id; определяется как эквивалентное объявлению usingqualified-id; [сноска: объявления доступа являются устаревшими; член using-declarations (7.3.3) предоставляет лучшие средства для выполнения тех же самых действий. В более ранних версиях языка C++ объявления доступа были более ограниченными; они были обобщены и сделаны эквивалентными using-declarations - end footnote]

Я бы сказал, что чаще всего нехорошо менять открытые члены на частные или защищенные члены в производный класс, потому что это нарушит принцип подстановки: вы знаете, что базовый класс имеет некоторые функции, и если вы приведете к производному классу, то вы ожидаете, что эти функции тоже будут вызываться, потому что производный класс -это база. И как вы уже упоминали, этот инвариант уже навязан языком, позволяющим конвертировать (который работает неявно!) к ссылке на базовый класс или уточнение имени функции, а затем вызов (затем public) функция.

Если вы хотите запретить кому-то вызывать набор функций базы, то я думаю, что это намекает на то, что сдерживание (или в редких случаях частное наследование) - лучшая идея.

Хотя показанное вами объявление using предоставляет механизм для изменения уровня доступа (но только вниз), это не является основным его использованием в таком контексте. Контекст using там в первую очередь предназначен для предоставления доступа к функциям, которые в противном случае были бы затенены от базового класса из-за языковой механики. Например,

class A {
public:
   void A();
   void B();
};

class B {
public:
   using A::B;
   void B(int); //This would shadow A::B if not for a using declaration
};

Декларация

using C::a

Переносит "a "в локальную область именования, так что позже вы можете использовать" a "для ссылки на" C::a"; поскольку это" C::a "и" a "взаимозаменяемы, пока вы не объявите локальную переменную с именем"a".

Декларация не изменяет прав доступа; вы можете получить доступ к "а "в подклассе только потому, что" а " не является частным.