Архив за Май 2008

Must Read: Герб Саттер "Новые сложные задачи на C++"

Май 26, 2008

Эта книга из серии Must Read. Вот ссылка на неё на сайте самого автора. Книга вышла в серии под названием «C++ In-Depth» под редакцией создателя языка – Бьярна Страуструпа. Книга выдержана в традиционной уже для этой серии книг манере задач для самостоятельного обдумывания с последующим подробным объяснением. Причём вопросы делятся на вопросы для новичков в C++ и вопросы для профессионалов. Не удивляйтесь, если прочитав её вы увидите, что-то знакомое – некоторые материалы на блоге используют информацию подчерпнутую и в ней, равно как и в любых других источниках. Темы задач, предложенных в книге довольно обширны – это: обобщённое программирование и стандартная библиотека, безопасность исключений, дизайн классов, наследование и полиморфизм, оптимизация и эффективность, головоломки и отдельно рассмотрены ошибки при проектировании класса std::string.

OZON.ru

Вокруг public, private и protected

Май 19, 2008

Тема довольно избита, однако, я всё-таки решил начать с неё. Думаю, новичкам будет полезно.

Итак, 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 работает только для доступа к закрытым членам данных или вызова виртуальных функций. Остальные же способы позволяют как модифицировать закрытые данные, так и вызывать закрытые невиртуальные методы.

На сегодня всё.

C++ Для Всех. Полетели!

Май 6, 2008

Приветствую всех читателей!

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