2010-02-16 14 views
18

Я пытался проверить действительность спецификации private на C++. Здесь идет: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; 
} 

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

+0

Я знаю, что здесь возможен дизайн Cheshire Cat (Pimpl - private impl. Http://en.wikipedia.org/wiki/Cheshire_Cat_Idiom_%28programming_technique%29), а также что спецификаторы доступа являются компиляторами времени компиляции , – legends2k

+0

Почему бы вам просто не перестроить его? –

+0

@ Dominic: Мое намерение состояло в том, чтобы увидеть, что произойдет, если я пытаюсь получить доступ к частным частям класса. – legends2k

ответ

30

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

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

7

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

2

Существует нет A :: actualPrintX реализация где угодно. Это ваша ошибка компоновщика.

+0

Спасибо за уведомления, это опечатка, я исправил ее сейчас :) – legends2k

+0

Парни, пожалуйста, не голосуйте за него. Ранее в вопросе, я только что положил 'actualPrintX()' вместо 'A :: actualPrintX()'; он просто уведомил меня об этой опечатке. – legends2k

9

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

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

Этот article от Herb Sutter объясняет это довольно красиво - он также предоставляет законный и переносимый способ обхода системы спецификатора доступа.

+0

Но я не определил ничего более одного раза; Согласны, я подделал декларацию, но почему это становится случаем нарушения ODR? – legends2k

+2

Вы определили 'class A' дважды - один раз в блоке перевода' classTester.cpp' и один раз в блоке перевода 'class_A.cpp'. Нарушение ODR заключается в том, что эти два определения не являются indentical. –

+0

Aargh, я вижу это сейчас! Спасибо за статью Саттера. – legends2k

5

Я пробовал. Это nm программы, которую я написал, имея тест класса с одним личным методом и общедоступным.

0000000100000dcc T __ZN4Test3barEv 
0000000100000daa T __ZN4Test3fooEv 

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

+0

Да, как я уже упоминал, в G ++ не было абсолютно никакой ошибки для доступа к 'actualPrintX()'; но символы в VC++, похоже, имеют некоторую разницу, и, следовательно, компоновщик выбросил ошибку. Благодаря! – legends2k

5

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

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

2

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

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

// 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: 
} 
+0

Аналогично, рядом с вопросом, в комментариях, я упомянул и о методе Чешира-Кота, который похож на этот, только без reinterpret_cast. – legends2k

+0

Простите меня, я, должно быть, пропустил это; хорошо знать, что на самом деле есть имя. – Ioan

1

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

//"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! 
} 
+0

Вы также можете добавить '#define class struct', чтобы поймать неквалифицированные частные vars. – mskfisher

1

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

Смежные вопросы