2010-02-16 38 views
18

Tôi đã thử tính hợp lệ của thông số truy cập private trong C++. Ở đây đi:C++ Riêng tư có thực sự riêng tư không?

Interface:

// class_A.h 

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

Thực hiện:

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

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

tôi đã xây dựng này vào một thư viện tĩnh (.a/lib). Bây giờ chúng ta có một cặp class_A.h và classA.a (hoặc classA.lib). Tôi đã chỉnh sửa class_A.h và xóa private: khỏi nó.

Bây giờ trong một 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; 
} 

Tôi biết rằng sau khi giả mạo tiêu đề của thư viện tất cả các cược đang tắt (Ý tôi là, hệ thống toàn vẹn, vv) Tuy có phương pháp là hacky, đây là thực sự được phép? Có cách nào để chặn điều này không? Hay tôi đang làm gì sai ở đây?

+0

Tôi biết rằng thiết kế Cheshire Cat (Pimpl - private impl. Http://en.wikipedia.org/wiki/Cheshire_Cat_Idiom_%28programming_technique%29) có thể được thực hiện ở đây và các bộ định danh truy cập cũng là các trình biên dịch thời gian biên dịch bởi trình biên dịch . – legends2k

+0

Tại sao bạn không thể xây dựng lại nó? –

+0

@Dominic: Mục đích của tôi là xem điều gì sẽ xảy ra nếu tôi đang cố gắng truy cập các phần riêng tư của một lớp học. – legends2k

Trả lời

30

privatekhông cơ chế bảo mật. Đó là cách truyền đạt ý định và ẩn thông tin mà các phần khác trong chương trình của bạn không cần biết, do đó làm giảm độ phức tạp tổng thể.

Có hai tệp tiêu đề khác nhau không tuân thủ các tiêu chuẩn, vì vậy về mặt kỹ thuật bạn đang nhập vào lãnh thổ hành vi không xác định, nhưng thực tế, như bạn đã tìm thấy, hầu hết các trình biên dịch sẽ không quan tâm.

7

Không. Điều khiển truy cập riêng tư ở đó ngăn bạn làm những điều ngu ngốc, không phải là cơ chế bảo mật để ngăn người khác truy cập dữ liệu hoặc chức năng của bạn. Có rất nhiều, rất nhiều cách để có được xung quanh nó.

2

Không có triển khai A :: actualPrintX ở bất kỳ đâu. Đó là lỗi liên kết của bạn.

+0

Cảm ơn bạn đã thông báo, đó là lỗi đánh máy, tôi đã sửa nó ngay bây giờ :) – legends2k

+0

Các bạn, xin đừng bỏ phiếu cho anh ấy. Trước đó trong câu hỏi, tôi vừa đặt 'actualPrintX()' thay vì 'A :: actualPrintX()'; anh ta vừa thông báo cho tôi về lỗi đánh máy đó. – legends2k

9

Bạn đã vượt qua những gì được phép trong C++, vì vậy những gì bạn đang làm không được phép - nhưng tất nhiên nó có thể hoạt động trên một số trình biên dịch trong một số trường hợp.

Cụ thể, bạn đang vi phạm One Definition Rule.

Điều này article bởi Herb Sutter giải thích nó khá độc đáo - nó cũng cung cấp một cách hợp pháp và di động để phá vỡ hệ thống xác định truy cập.

+0

Nhưng tôi đã không xác định bất cứ điều gì nhiều hơn một lần; Đồng ý, tôi đã giả mạo tuyên bố, nhưng làm thế nào nó trở thành một trường hợp vi phạm ODR? – legends2k

+2

Bạn đã định nghĩa 'lớp A' hai lần - một lần trong đơn vị dịch' classTester.cpp' và một lần trong đơn vị dịch 'class_A.cpp'. Vi phạm ODR là hai định nghĩa đó không phải là indentical. –

+0

Aargh, tôi hiểu rồi! Cảm ơn bài viết của Sutter trên cùng. – legends2k

5

Tôi đã thử. Đây là một nm của một chương trình tôi đã viết có một bài kiểm tra lớp học với một phương pháp riêng và một công cộng.

0000000100000dcc T __ZN4Test3barEv 
0000000100000daa T __ZN4Test3fooEv 

Như bạn thấy, chữ ký hoàn toàn giống nhau. Trình liên kết hoàn toàn không có gì để phân biệt một phương thức riêng tư với một phương thức công khai.

+0

Vâng, như tôi đã đề cập, trong G ++, hoàn toàn không có lỗi khi truy cập 'actualPrintX()'; nhưng các biểu tượng trong VC++ dường như có một số khác biệt và do đó mối liên kết đã ném một lỗi. Cảm ơn! – legends2k

5

Tôi đồng ý với hầu hết các câu trả lời khác.

Tuy nhiên, tôi muốn chỉ ra rằng nó hoàn toàn chấp nhận được đối với trình biên dịch để sắp xếp vật lý các thành viên khác nhau khi bạn xóa private. Nếu nó hoạt động, đó là may mắn. Bạn không thể tin tưởng vào nó. Nếu cả hai bên không sử dụng cùng một tuyên bố, họ không thực sự sử dụng cùng một lớp.

2

Vì không có ai đề cập đến cách ngăn chặn việc này ...Một cách có thể để chặn quyền truy cập vào các thành viên riêng tư là khai báo chúng dưới dạng một loại nội bộ riêng biệt không hiển thị bên ngoài tệp. Tất nhiên, nếu bạn muốn cung cấp quyền truy cập rõ ràng vào nội bộ này, bạn sẽ phải cung cấp tuyên bố nội bộ. Điều đó cũng thường được thực hiện bằng cách sử dụng tiêu đề nội bộ chứa loại đó.

Lưu ý: Bạn sẽ phải theo dõi phân bổ/giải phóng đối tượng bên trong trong ví dụ này. Có nhiều cách khác để làm điều đó mà không cần điều này.

// 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

Tương tự, bên cạnh câu hỏi, trong phần bình luận, tôi cũng đã đề cập đến phương pháp Cheshire Cat, tương tự như vậy, chỉ không có reinterpret_cast. – legends2k

+0

Xin lỗi tôi, tôi chắc đã bỏ lỡ điều đó; tốt để biết nó thực sự có một cái tên. – Ioan

1

Trong hầu hết các trường hợp, bạn thậm chí không phải chỉnh sửa tệp tiêu đề để đặt thành viên riêng tư ở chế độ công khai. Bạn có thể làm điều này với preprocesor. Một cái gì đó như thế này:

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

int C::m = 12; 

và sau đó, điều này sẽ làm việc:

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

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

Bạn cũng có thể thêm '#define class struct' để bắt các vars riêng không đủ tiêu chuẩn. – mskfisher

1

Ghi nhớ cũng là khi bạn thay đổi quyền truy cập một biến thành viên của, trình biên dịch có thể đặt nó ở một khác nhau bù đắp trong lớp vật. Tiêu chuẩn này cho phép các trình biên dịch có số tiền tự do hợp lý trong việc sắp xếp lại các thành viên (ít nhất là trong cùng một cấp truy cập, tôi nghĩ).

Các vấn đề liên quan