С++ Является ли частное частным?
Я тестировал действительность спецификатора доступа private
в С++. Здесь:
Интерфейс:
// 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:
.
Теперь в другом классеTester.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;
}
Я знаю, что после вмешательства в заголовок библиотеки все ставки отключены (я имею в виду, системная целостность и т.д.). Хотя метод хакерский, это действительно разрешено? Есть ли способ заблокировать это? Или я здесь что-то не так?
Ответы
Ответ 1
private
- это не механизм безопасности. Это способ общения и сокрытия информации, о которой другие части вашей программы не нужно знать, тем самым уменьшая общую сложность.
Наличие двух разных файлов заголовков не соответствует стандартам, так что технически вы входите в область поведения undefined, но практически, как вы уже нашли, большинству компиляторов это не понравится.
Ответ 2
Вы отклонились от того, что разрешено на С++, поэтому то, что вы делаете, недопустимо, но, конечно, в некоторых ситуациях оно может работать с некоторыми компиляторами.
В частности, вы нарушаете правило определения 1.
Эта статья статьи от Herb Sutter объясняет это довольно хорошо - она также предоставляет законный и переносимый способ обхода системы спецификатора доступа.
Ответ 3
Нет. Частный контроль доступа предназначен для того, чтобы вы не делали глупые вещи, а не как механизм безопасности, чтобы остановить доступ других пользователей к вашим данным или функциям. Существует множество способов обойти это.
Ответ 4
Я пробовал. Это nm программы, которую я написал, имея тест класса с одним личным методом и общедоступным.
0000000100000dcc T __ZN4Test3barEv
0000000100000daa T __ZN4Test3fooEv
Как вы можете видеть, подпись точно такая же. У компоновщика нет абсолютно ничего, чтобы отличать частный метод от публичного.
Ответ 5
Я согласен с большинством других ответов.
Однако я хотел бы отметить, что для компилятора вполне приемлемо физически упорядочивать элементы по-разному при удалении этого private
. Если это сработает, это удача. Вы не можете рассчитывать на это. Если обе стороны не используют одно и то же объявление, они не используют один и тот же класс.
Ответ 6
Нет реализации A:: actualPrintX в любом месте. Это ваша ошибка компоновщика.
Ответ 7
Поскольку никто не упомянул способ блокировать это... Один из возможных способов блокировки доступа к закрытым членам - объявить их как отдельный внутренний тип, невидимый вне файла. Конечно, если вы хотите предоставить явный доступ к этому внутреннему, вам необходимо будет предоставить внутреннюю декларацию. Это также обычно делается с использованием внутреннего заголовка, содержащего этот тип.
Примечание. Вам нужно будет отслеживать выделение/освобождение этого внутреннего объекта в этом примере. Есть и другие способы сделать это, чтобы этого не требовалось.
// 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:
}
Ответ 8
В большинстве случаев вам даже не нужно редактировать заголовочный файл, чтобы публиковать публичные члены. Вы можете сделать это с помощью препроцессора. Что-то вроде этого:
//"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!
}
Ответ 9
Помните также, что при изменении доступа к переменной-члену компилятор может поместить его в другое смещение внутри объекта класса. Стандарт позволяет компиляторам довольно много свободы в перегруппировке членов (по крайней мере, на том же уровне доступа, я думаю).