Тема довольно избита, однако, я всё-таки решил начать с неё. Думаю, новичкам будет полезно.
Итак, public, private и protected – это модификаторы доступа, а не видимости, как ошибочно думают некоторые. Private члены видны снаружи класса, но не доступны.
Теперь кратко, кому какой доступ они предоставляют.
- Public – доступ открыт всем, кто видит определение данного класса.
- Private – доступ открыт самому классу (т.е. функциям-членам данного класса) и друзьям (friend) данного класса, как функциям, так и классам.
- Protected – доступ открыт классам, производным от данного.
Далее приведены примеры доступа с указанием какие поля в каких местах программы доступны.
1 class some {
2 friend void f(some&);
3 public:
4 int a_;
5 protected:
6 int b_;
7 private:
8 int c_;
9 };
10
11 void f(some& obj) {
12 obj.a_ = 0; // ok
13 obj.b_ = 0; // ok
14 obj.c_ = 0; // ok
15 }
16
17 void g(some& obj) {
18 obj.a_ = 0; // ok
19 obj.b_ = 0; // CT error
20 obj.c_ = 0; // CT error
21 }
22
23 class derived : public some {
24 derived() {
25 a_ = 0; // ok
26 b_ = 0; // ok
27 c_ = 0; // CT error
28 }
29 };
В C++ существует public-наследование, private-наследование и protected-наследование. В зависимости от того, какой тип используется, изменяется доступ к членам базового класса для клиентов производного. В таблице сведена информация об этом изменении.
| Исходный модификатор доступа | |||
| public | private | protected | |
| public-наследование | public | private | protected |
| private-наследование | private | private | private |
| protected-наследование | protected | private | protected |
Следует добавить, что производный класс может изменить модификатор доступа с protected на public, разместив using объявление в соответствующей секции класса:
1 class some {
2 public:
3 int a;
4 protected:
5 int b;
6 private:
7 int c;
8 };
9
10 class derived : public some {
11 public:
12 using some::b;
13 };
14
15 void f(derived& obj) {
16 obj.b = 0; // ok
17 }
Напоследок приведу несколько приёмов, с помощью которых можно «достучаться» до закрытых функций или данных. Допустим, у нас есть класс some и нам нужно обнулить закрытую переменную c:
- Модифицировать определение класса, добавив друга (функцию или класс)
1 class some {
2 friend class some_friend;
3 public:
4 int a;
5 protected:
6 int b;
7 private:
8 int c;
9 };
10
11 class some_friend {
12 public:
13 static void hack(some& obj) {
14 obj.c = 0;
15 }
16 };
- Воспользоваться препроцессором:
1 #define private public
2
3 class some {
4 public:
5 int a;
6 protected:
7 int b;
8 private:
9 int c;
10 };
11
12 void hack(some& obj) {
13 obj.c = 0;
14 }
- Создать класс с таким же расположением в памяти и воспользоваться reinterpret_cast для преобразования указателей:
1 class some {
2 public:
3 int a;
4 protected:
5 int b;
6 private:
7 int c;
8 };
9
10 class hack_some {
11 public:
12 int a;
13 int b;
14 int c;
15 };
16
17 void h(some& obj) {
18 reinterpret_cast<hack_some*>(&obj)->c = 0;
19 }
- Если у «взламываемого» класса есть шаблонная функция, можно её специализировать своим типом:
1 class some {
2 public:
3 int a;
4 template<class T> void func(void) {
5 a = b + c;
6 }
7 protected:
8 int b;
9 private:
10 int c;
11 };
12
13 class hack_template_param{};
14
15 template<>
16 void some::func<hack_template_param>(void) {
17 c = 0;
18 }
19
20 void hack(void) {
21 some o;
22 o.func<hack_template_param>();
23 };
Очевидно, что способ с reinterpret_cast работает только для доступа к закрытым членам данных или вызова виртуальных функций. Остальные же способы позволяют как модифицировать закрытые данные, так и вызывать закрытые невиртуальные методы.
На сегодня всё.
Май 22, 2008 в 12:43 дп |
Табуляцая съехала, трудно читать примеры. Поправь, если не тяжело.
Май 22, 2008 в 10:32 дп |
Сергей, спасибо, поправил.
Май 22, 2008 в 1:54 пп |
Фигассе отжиг, афтор
Новичкам очень полезно знать, как убить и без того кривой ООП в с++, это вы правильно подметили.
За идею корежить private поля убить-убить-убить РЖАВОЙ СЕКИРОЙ УЖОСА =).
С наилучшими, Meowth.
Май 22, 2008 в 2:18 пп |
meowth,
Улыбнуло
Май 22, 2008 в 10:27 пп |
Взломы для доступа к private стоит все-таки убрать, наверное, действительно.
И еще, тяжело код читать, стоит подправить стили. Firefox 3b5
Май 23, 2008 в 10:21 дп |
Alno, у меня Opera, всё хорошо отображает. Может, в FF3 проблема? А насчёт убрать, не согласен. Зачем закрывать доступ к информации? Я же не пропагандирую постоянно писать в таком стили, но может сложиться ситуация, когда иного пути просто не будет.
Май 26, 2008 в 12:37 дп |
Все, вспомнил;) Меня долго мучало ощущение дежавю – где-то я такое уже читал. И вот я вспомнил – это примеры с книги Herb Sutter «Exceptional C++ Style». Я прав?
Май 26, 2008 в 9:22 дп |
Сергей, действительно так. Думаю, что и в других источниках (и в книгах и в сети) можно найти эту информацию.
Май 27, 2008 в 1:51 дп |
Мощная книга, кстати. Взламывает наивные представления о возможности языка С++
Май 27, 2008 в 9:33 дп |
Сергей, для меня такими «взломщиками» стали Элджер и Александреску
Июнь 26, 2008 в 7:50 пп |
аффтар аццкий хакир
самая смешная статья по С++ которую я когда-либо читал
Июнь 27, 2008 в 8:10 дп |
Torvin, а что конкретно насмешило?
Июнь 30, 2008 в 7:23 пп |
в С++ нет средств для защиты кода. это логично, поскольку С/С++ – никсовые детища. чтобы распространять даже скомпиленную либу, нужно предоставить заголовочный файл с определением всех классов, который с легкостью подвергается изменению (которое в любом случае необходимы для способов 1-2). таким образом если нужно добраться до закрытого члена даже класса из сторонней библиотеки – сделать это не представляет сложности.
а все потому, что модификаторы доступа служат не для «защиты» каких-то «секретных» частей кода, а _исключительно_ для защиты от программистких ошибок. т.е. для инкапсуляции. в тех же целях добавлен модификатор const. автор похоже этого не понимает…
единственную практическую ценность представляет лишь способ 3 с reinterpret_cast который пригодится в случае с чем-то вроде COM. и к С++ напрямую не имеет никакого отношения.
P.S.
#define private public жжот. надеюсь вы это не в серьез
Июль 1, 2008 в 8:39 дп |
Torvin, спасибо за ответ. Автор понимает для чего служат модификаторы доступа. Тем не менее, в результате совокупного действия разных факторов у вас на руках может оказаться библиотека без исходных кодов, с которой придётся проделать операцию описанную в этой заметке. Дай бог, чтобы этого с вами не случилось
Июль 1, 2008 в 7:26 пп |
вот я и говорю что не бывает C++ библиотеки без заголовочного файла. в котором можно любые private-мемберы исправить на public.
где вы такую библиотеку видели???
Июль 2, 2008 в 8:30 дп |
Torvin, исправить-то можно, только потом можно поиметь проблемы с линкером, если он имена для public и private членов по-разному преобразует.
Июль 2, 2008 в 8:31 дп |
Простая защита от таких вещей – идиома PImpl.
Июль 3, 2008 в 9:19 пп |
ну те же самые проблемы будут и в случае с #define
Июль 4, 2008 в 8:00 дп |
Torvin, по-моему, это очевидно
Сентябрь 22, 2008 в 8:53 дп |
Огромное тебе спасибо за пример с reinterpret_cast.
Май 10, 2009 в 7:32 пп |
Кста, постите тексты в социалки. Больше народу читать будет)