2009-10-03 26 views
7

Tôi muốn truy cập thành viên dữ liệu cá nhân trong một lớp học. Không có hàm thành viên trong lớp để truy cập thành viên dữ liệu riêng tư. Nó là riêng tư.Tôi có thể chuyển đổi một đối tượng và truy cập các thành viên dữ liệu riêng tư trong C++ không?

Tôi muốn tham gia lớp học và một số cách mở nó. Một phương pháp là sao chép khai báo của lớp, làm cho thành viên private trở thành công khai và gọi lớp class mới something_else. Sau đó, tôi làm một diễn giải lại diễn viên và sao chép các đối tượng ban đầu. Những công việc này. Nhưng tôi muốn một cái gì đó thanh lịch hơn ... hoặc có lẽ chung chung ... hoặc chỉ là một cách khác.

Có các tùy chọn nào? Tôi có thể sử dụng void * không? Tôi có thể ghi nhớ lớp học vào một lớp học trống không? Cách để làm điều này là gì ??

%

+3

Làm cách nào để chỉnh sửa nguồn? '// private:' sẽ hoạt động tốt;) – LiraNuna

+0

Tôi nghĩ rằng anh ấy muốn giữ cho người dùng lớp học của mình không bị lộn xộn (vì vậy riêng tư) và cần triển khai một cái gì đó không thực sự phù hợp với thiết kế hiện tại. – jdehaan

+0

Tôi nghĩ bạn không có mã, phải không? –

Trả lời

18

Tôi giả định rằng

  1. Bạn đã từng trải qua "phá vỡ đóng gói là xấu" sân khấu,
  2. kiệt sức giải pháp khả thi khác,
  3. Không thể thay đổi lớp tiêu đề.

Có một số cách để phá quyền truy cập vào các thành viên riêng tư của lớp học, như được minh họa trong GotW #76.

  1. Sao chép định nghĩa lớp và thêm tuyên bố friend.
  2. Sử dụng macro ác: #define private publictrước khi bao gồm tiêu đề của lớp.
  3. Viết định nghĩa lớp với bố cục nhị phân giống hệt nhau và sử dụng reinterpret_cast để chuyển từ lớp gốc sang lớp giả.
  4. Chuyên chức năng thành viên mẫu nếu có (giải pháp di động duy nhất).
+2

+1. Bài báo WONDERFUL. – LiraNuna

+0

Yêu thích các macro ác đề cập;) – Calyth

1

Bạn có thể, nhưng bạn không nên. Các đối tượng chỉ là bộ nhớ. Bạn chắc chắn có thể đưa con trỏ vào một lớp tương đương có cùng thành viên nhưng mọi thứ đều công khai. Nhưng tại sao bạn muốn làm điều này? Bạn có mã của ai đó mà bạn cần làm việc cùng không? Làm cho họ thêm các phương thức truy cập thích hợp. Bạn có thực sự cần phải đối xử với họ như là thành viên công cộng? Thay đổi lớp học.

Tôi không thực sự chắc chắn những gì bạn đang cố gắng làm, nhưng nó có thể là một sai lầm.

0

Tôi đồng ý với nhận xét "chỉnh sửa nguồn", nhưng tôi nghĩ bạn nên thêm phương thức chứ không phải chỉ nhận xét 'riêng tư'.

Bạn phải khai báo lớp, vì vậy bạn có thể có tiêu đề nhưng có thể không phải tệp .cpp/bất kỳ. Thêm một hàm thành viên nội tuyến vào lớp trong bản sao của tiêu đề và bao gồm tiêu đề này thay cho phần đầu. Bạn vẫn có thể liên kết đến tệp đối tượng cho mã nguồn không thể truy cập được.

Tất nhiên điều này được tính là hack ác, bỏ qua các biện pháp bảo vệ được tích hợp vào ngôn ngữ thay vì làm việc với chúng. Đó là lý do tại sao tôi đề nghị hack tối thiểu ác - không làm cho tất cả mọi thứ riêng tư, và nếu bạn có thể nhận được ngay với một getter (nhưng không có setter) làm điều đó. Tất nhiên điều ác tối thiểu thực sự không phải là để làm điều đó, nếu có cách nào cả để tránh nó.

Hãy nhớ, nếu đây là lớp elses ai đó bạn đang làm việc, phiên bản tiếp theo có thể được triển khai khác và có thể không có thành viên đó.

+0

Nhận xét của tôi cũng mô tả rằng có vấn đề thiết kế ở đâu đó ... – LiraNuna

3

Với ý tưởng bạn đề xuất trong câu hỏi của mình, bạn không cần sao chép đối tượng gốc. Nếu bạn viết biến thể "tất cả công khai" của riêng bạn của khai báo lớp thực, thì hãy đưa con trỏ đến loại mới đó, bạn có thể trực tiếp truy cập đối tượng thông qua nó.

Lý do tại sao không có ý tưởng nào trong số này là một ý tưởng hay. Bạn phải thao tác các đối tượng của một lớp mà bạn không kiểm soát nguồn (nếu không bạn có thể sửa đổi nguồn để cung cấp cho bạn quyền truy cập bạn cần). Nhưng nếu bạn không kiểm soát nguồn, thì điều gì sẽ xảy ra nếu người bảo trì thay đổi cách bố trí lớp của họ? Phiên bản trùng lặp của bạn sẽ không còn phù hợp nữa và sẽ không có cách nào để trình biên dịch phát hiện sự không khớp này. Kết quả có thể sẽ bị lỗi trong thời gian chạy.

3

Vì nó được hiểu không đúng, tôi phải làm rõ. Tất cả các giải pháp sau không yêu cầu bạn phải biên dịch lại đối tượng. Để sử dụng một lớp trong mã của bạn, nếu nó được biên dịch thành một tệp đối tượng, bạn nên bao gồm tệp tiêu đề có khai báo của lớp đó.

#include <class.h> 
ObjectFoo instance; 

có thể (nhưng nguy hiểm trừ khi bạn cẩn thận) để thay đổi tiêu đề (a) hoặc sao chép các tiêu đề đi nơi khác và bao gồm rằng tiêu đề (b), mà không cần biên dịch lại lớp đó .

#include <class_fixed.h> 
ObjectFoo instance; 

Mã của bạn, nơi bạn có bao gồm tiêu đề mới sẽ chỉ nghĩ rằng trong tập tin đối tượng (mà bạn đã không biên dịch lại!) Ông sẽ tìm thấy thi của lớp khai báo là trong class_fixed.h. Trong khi vẫn tồn tại lớp được tuyên bố là trong class.h. Nếu bạn thay đổi bù đắp của các thành viên (thêm thành viên mới ví dụ) trong tiêu đề mới của bạn, bạn đã chết và mã sẽ không hoạt động đúng. Nhưng chỉ cần thay đổi truy cập hoạt động tốt. Biên dịch mã không biết về truy cập, điều này chỉ quan trọng trong việc biên dịch lạ.

Điều này không phải lúc nào cũng có hại. Trong cuộc sống hàng ngày bạn gặp phải một sự thay đổi khi bạn cài đặt phiên bản mới của một thư viện vào hệ thống của bạn và không biên dịch lại tất cả các chương trình phụ thuộc vào nó. Nhưng nó phải được xử lý cẩn thận


Có một số giải pháp.

  1. memcpy()
    Đừng! Không memcpy như sao chép đối tượng đôi khi trải qua chính sách cụ thể áp đặt bởi các nhà thiết kế lớp. Ví dụ: auto_ptrs không thể chỉ là memcopied: nếu bạn memcopy các auto_ptr và sau đó destructor được chạy cho cả hai, bạn sẽ cố gắng để giải phóng cùng một bộ nhớ hai lần và chương trình sẽ sụp đổ.

  2. Thay đổi private:-public: trong tiêu đề hoặc với vĩ mô
    Nếu bạn được phép tạo nó, bạn có thể giải quyết vấn đề của bạn bằng cách chỉnh sửa các tập tin tiêu đề mà đi kèm với việc thực hiện của lớp. Liệu mã nguồn của việc triển khai (tức làcpp-file của lớp) nằm dưới sự kiểm soát của bạn không quan trọng: thay đổi riêng thành công cho các thành viên dữ liệu (tiêu đề) đủ và hoạt động tốt ngay cả khi bạn được cung cấp một thư viện nhị phân chỉ chứa định nghĩa lớp. (Đối với các chức năng thành viên thay đổi truy cập đôi khi thay đổi tên nội bộ của nó, nhưng đối với MSVS và GCC đó là ok.)

  3. Thêm một hàm getter mới
    Trong khi thay đổi private-public là gần như luôn luôn ok (trừ khi bạn dựa vào cụ thể thời gian biên dịch kiểm tra sẽ phá vỡ trình biên dịch nếu lớp có một số thành viên có thể truy cập được), việc thêm hàm getter mới cần được thực hiện cẩn thận. Hàm getter phải là nội tuyến (và do đó được xác định trong tệp tiêu đề của lớp).

  4. reinterpret_cast
    Dàn diễn viên làm việc tốt nếu bạn KHÔNG đúc một con trỏ tới lớp cơ sở năng động (phương tiện năng động "với các chức năng ảo hoặc căn cứ") có ví dụ thực tế tại thời điểm đúc có thể được bắt nguồn từ lớp ở đoạn mã cụ thể.

  5. protected:
    Và chỉ trong trường hợp bạn quên. C++ có thể khai báo thành viên protected:, tức là chỉ có thể truy cập vào các lớp bắt nguồn từ phần đã cho. Điều này có thể đáp ứng nhu cầu của bạn.

0

Cảm ơn bạn ... Tôi đã muốn hiển thị mã cho bản sửa lỗi gốc. Lý do mà ai đó đã nói là tôi không thể thay đổi mã gốc ... vì vậy tôi phải nghỉ ngơi.


#include<iostream> 
using namespace std; 

// Class Objectfoo 
// Pretend Objectfoo lives somewhere else ... I cannot open him up 

class ObjectFoo 
{ 
    private: 
    int datax; 
    public: 
    ObjectFoo() { datax = 100; } 
    void get() { cout << datax << endl;} 
}; 

// Class ObjectBar 
class ObjectBar 
{ 
    public: 
    int datax; 
}; 

ObjectFoo FOOEY; 

ObjectBar* touch_foo(int x, ObjectFoo* foo , ObjectBar* bar) 
{ 
bar = reinterpret_cast<ObjectBar*>(foo); 
bar->datax = x; 
return bar; 
} 

int main() 
{ 
    ObjectBar* bar; 

    cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl; 
    FOOEY.get(); 

    cout << "Changing private member " << endl; 
    bar = touch_foo(5, &FOOEY, bar); 

    cout << "bar->datax = " << bar->datax << endl; 

    cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl; 
    FOOEY.get(); 

    return 0; 
} 

này hoạt động ... nhưng tôi nghĩ rằng tôi muốn một cái gì đó chung chung hơn ... hoặc linh hoạt hơn.

%

+0

Nhiều câu trả lời ở đây cho rằng bạn không thể thay đổi mã '.cpp' ban đầu, trong khi bạn có thể thay đổi - hoặc chỉ sao chép , đổi tên và bao gồm - tệp ** header **. Đọc lại các câu trả lời với thông tin chi tiết này. –

+0

Tốt hơn hết là thêm thông tin này vào câu hỏi ban đầu thay vì thêm thông tin đó làm câu trả lời. – KeithB

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