MyTetra Share
Делитесь знаниями!
Модификаторы public, private и protected в C++
Время создания: 18.07.2015 22:51
Текстовые метки: Модификатор, public, private, protected, C++, доступ, наследование, правила, контроль
Раздел: Компьютер - Программирование - Язык C++ (Си++)
Запись: xintrea/mytetra_syncro/master/base/1437249075bdhvw1kmzw/text.html на raw.github.com

Модификаторы доступа


Public, private и protected — это модификаторы доступа, а не видимости, как ошибочно думают некоторые. Ниже кратко написано, кому какой доступ они предоставляют.


  • Public — доступ открыт всем другим классам, кто видит определение данного класса.
  • Protected — доступ открыт классам, производным от данного. То есть, производные классы получают свободный доступ к таким свойствам или методам. Все другие классы такого доступа не имеют.
  • Private — доступ открыт самому классу (т.е. функциям-членам данного класса) и друзьям (friend) данного класса - как функциям, так и классам. Однако производные классы не получают доступа к этим данным совсем. И все другие классы такого доступа не имеют.


Примечание: Следует понимать, что модификаторы доступа - это модификаторы доступа к именам полей класса. Это не модификаторы доступа к данным класса.

То есть, если сделать два поля типа int, причем int a будет Public, а int b будет Protected, то это не значит, что значение int b (т. е. данные) полностью защищено от изменения внешним кодом. Внешний код может получить указатель на a, прибавить к нему через арифметику указателей единицу или любое другое число, чтобы попасть на область памяти переменной b, и считать значение b, и даже его заменить.

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


Далее приведены примеры доступа с указанием, какие поля в каких местах программы доступны.


class some {

  friend void f(some&);

public:

  int a_;

protected:

  int b_;

private:

  int c_;

};

 

void f(some& obj) {

  obj.a_ = 0; // OK

  obj.b_ = 0; // OK

  obj.c_ = 0; // OK

}

 

void g(some& obj) {

  obj.a_ = 0; // OK

  obj.b_ = 0; // compile time error

  obj.c_ = 0; // compile time error

}

 

class derived : public some {

  derived() {

    a_ = 0; // OK

    b_ = 0; // OK

    c_ = 0; // compile time error

  }

};


Модификаторы наследования


В C++ существует public-наследование, private-наследование и protected-наследование. В зависимости от того, какой тип используется, изменяется доступ к членам базового класса для клиентов производного. В таблице сведена информация об этом изменении:



Модификатор наследования

Модификатор доступа

public

private

protected

public-наследование

public

private

protected

private-наследование

private

private

private

protected-наследование

protected

private

protected


Следует добавить, что производный класс может изменить модификатор доступа с protected на public, разместив using объявление в соответствующей секции класса:


class some {

public:

  int a;

protected:

  int b;

private:

  int c;

};

 

class derived : public some {

public:

  using some::b;

};

 

void f(derived& obj) {

  obj.b = 0; // OK

}



Обычно, чтобы сохранить модификаторы доступа, используется public-наследование. Тогда у производного класса модификаторы доступа получаются точно такими же как и у базового класса.



Неэтичные приемы обращения к закрытым полям класса


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


Вот несколько приёмов, с помощью которых можно "достучаться" до закрытых функций или данных. Допустим, у нас есть класс some и нам нужно обнулить закрытую переменную c. Как это можно сделать?


  • Модифицировать определение класса, добавив друга, т. е. friend-функцию или friend-класс

  • class some {

      friend class some_friend;

    public:

      int a;

    protected:

      int b;

    private:

      int c;

    };

     

    class some_friend {

    public:

      static void hack(some& obj) {

        obj.c = 0;

      }

    };


  • Воспользоваться препроцессором, который на месте private будет писать public:


#define private public

 

class some {

public:

  int a;

protected:

  int b;

private:

  int c;

};

 

void hack(some& obj) {

  obj.c = 0;

}



  • Создать класс с таким же расположением в памяти и воспользоваться reinterpret_cast для преобразования указателей:


class some {

public:

  int a;

protected:

  int b;

private:

  int c;

};

 

class hack_some {

public:

  int a;

  int b;

  int c;

};

 

void h(some& obj) {

  reinterpret_cast<hack_some*>(&obj)->c = 0;

}


  • Если у «взламываемого» класса есть шаблонная функция, можно её специализировать своим типом:


class some {

public:

  int a;

  template<class T> void func(void) {

    a = b + c;

  }

protected:

  int b;

private:

  int c;

};

 

class hack_template_param{};

 

template<>

void some::func<hack_template_param>(void) {

  c = 0;

}

 

void hack(void) {

  some o;

  o.func<hack_template_param>();

};



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


PS:

Для компиляторов GCC и CLANG существует опция -fno-access-control, которая позволяет отключить контроль доступа к членам класса со стороны компилятора.


Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования