C ++ Является ли личное действительно частным? - PullRequest
18 голосов
/ 16 февраля 2010

Я проверял допустимость private спецификатора доступа в C ++.Здесь идет:

Интерфейс:

// class_A.h

class A
{
public:
    void printX();
private:
    void actualPrintX();
    int x;
};

Реализация:

// class_A.cpp
void A::printX()
{
    actualPrintX();
}

void A::actualPrintX()
{
    std::cout << x:
}

Я встроил это в статическую библиотеку (.a / .lib).Теперь у нас есть пара class_A.h и classA.a (или classA.lib). Я отредактировал class_A.h и удалил из него private:.

Теперь в другом classTester.cpp:

#include "class_A.h"    // the newly edited header

int main()
{
    A a;

    a.x = 12;           // both G++ and VC++ allowed this!
    a.printX();         // allowed, as expected
    a.actualPrintX();   // allowed by G++, VC++ gave a unresolved linker error

    return 0;
}

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

Ответы [ 9 ]

30 голосов
/ 16 февраля 2010

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

Наличие двух разных заголовочных файлов не соответствует стандартам, поэтому технически вы входите в неопределенную область поведения, но практически, как вы выяснили, большинству компиляторов все равно.

9 голосов
/ 16 февраля 2010

Вы вышли за пределы того, что разрешено в C ++, поэтому то, что вы делаете, не разрешено - но, конечно, в некоторых ситуациях это может работать на некоторых компиляторах.

В частности, вы нарушаете правило One Definition .

Эта статья Херба Саттера объясняет это довольно хорошо - она ​​также предоставляет легальный и портативный способ обойти систему спецификаторов доступа.

7 голосов
/ 16 февраля 2010

Нет. Частный контроль доступа предназначен для того, чтобы помешать ВАМ совершать глупые поступки, а не в качестве механизма безопасности, чтобы запретить другим доступ к вашим данным или функциям. Есть много, много способов обойти это.

5 голосов
/ 16 февраля 2010

Я согласен с большинством других ответов.

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

5 голосов
/ 16 февраля 2010

Я пытался.Это нм программы, которую я написал с классом Test с одним закрытым методом и открытым.

0000000100000dcc T __ZN4Test3barEv
0000000100000daa T __ZN4Test3fooEv

Как видите, подпись точно такая же.Линкер не имеет абсолютно ничего, чтобы отличить приватный метод от публичного.

2 голосов
/ 16 февраля 2010

Поскольку никто не упомянул способ заблокировать это ... Один из возможных способов заблокировать доступ к закрытым членам - это объявить их как отдельный внутренний тип, не видимый вне файла. Конечно, если вы хотите предоставить явный доступ к этому внутреннему, вы должны будете предоставить внутреннюю декларацию. Это также обычно делается с использованием внутреннего заголовка, содержащего этот тип.

Примечание. В этом примере вам нужно будет отслеживать выделение / освобождение этого внутреннего объекта. Есть другие способы сделать это, которые не требуют этого.

// class_A.h
class A {
public:
  void printX();
private:
  void *Private;
};

// class_A.cpp
class A_private {
  void actualPrintX();
  int x;
};

void A::printX() {
  reinterpret_cast<A_private *>(Private)->actualPrintX();
}

void A_private::actualPrintX() {
  std::cout << x:
}
2 голосов
/ 16 февраля 2010

Везде нет реализации A :: actualPrintX. Это ваша ошибка компоновщика.

1 голос
/ 16 февраля 2010

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

1 голос
/ 16 февраля 2010

В большинстве случаев вам даже не нужно редактировать заголовочный файл, чтобы сделать приватных участников публичными. Вы можете сделать это с препроцессором. Примерно так:

//"classWithPrivateMembers.hpp"
class C
{
private: //this is crucial for this method to work
    static int m;
};

int C::m = 12;

и тогда это будет работать:

#define private public  
#include "classWithPrivateMembers.hpp"  
#undef private

int main()
{
    C::m = 34; // it works!
}
...