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 ответа:
Что касается Вашего объявления без
using
: они называются "объявлениями доступа" и являются устаревшими. Вот текст из стандарта, из11.3/1
:Доступ члена базового класса может быть изменен в производном классе путем упоминания его квалифицированного идентификатора в объявление производного класса. Такое упоминание называется декларацией доступа. Эффект объявления доступа
qualified-id
;
определяется как эквивалентное объявлениюusing
qualified-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".
Декларация не изменяет прав доступа; вы можете получить доступ к "а "в подклассе только потому, что" а " не является частным.