Tại sao các đối tượng của cùng một lớp lại có quyền truy cập vào dữ liệu riêng tư của nhau?


98

Tại sao các đối tượng của cùng một lớp lại có quyền truy cập vào dữ liệu riêng tư của nhau?

class TrivialClass {
public: 
  TrivialClass(const std::string& data) :
    mData(data) {};

  const std::string& getData(const TrivialClass& rhs) const {
    return rhs.mData;
  };

private:
  std::string mData;
};

int main() {
  TrivialClass a("fish");
  TrivialClass b("heads");

  std::cout << "b via a = " << a.getData(b) << std::endl;
  return 0;
}

Mã này hoạt động. Đối tượng a hoàn toàn có thể truy cập dữ liệu riêng tư từ đối tượng b và trả về. Tại sao điều này nên được như vậy? Tôi nghĩ rằng dữ liệu cá nhân là riêng tư. (Tôi bắt đầu bằng cách cố gắng hiểu các hàm tạo bản sao trong thành ngữ ma cô, nhưng sau đó tôi phát hiện ra rằng tôi thậm chí còn không hiểu tình huống đơn giản này.)


18
Chà, như một điểm khởi đầu, bạn sẽ không thể triển khai đúng bất kỳ hàm tạo bản sao nào cho bất kỳ thứ gì ngoại trừ các lớp đơn giản nhất. Bạn có thể coi các lớp học là người bạn tốt nhất của chính họ :-)
Cameron

4
Hãy nghĩ về quyền riêng tư từ khách hàng của họ, nhưng tất cả nhân viên của lớp đều có quyền truy cập
Martin Beckett

Cảm ơn Cameron. Điều đó có lý, nhưng tại sao quyền truy cập này không bị hạn chế chỉ đối với các hàm tạo sao chép và toán tử gán?
Keith

5
Các đối tượng cùng loại thường tương tác với nhau rất nhiều. Và không ai bắt bạn phải viết một phương thức cung cấp dữ liệu riêng tư của một phiên bản khác. :)
UncleBens

4
Đơn giản bởi vì, tại thời điểm biên dịch, trình biên dịch không có cách nào để xác định cùng một đối tượng của nó. Việc thực thi quyền truy cập như vậy sẽ yêu cầu hỗ trợ thời gian chạy.
Chethan

Câu trả lời:


80

Vì đó là cách nó hoạt động trong C ++. Trong C ++, kiểm soát truy cập hoạt động trên cơ sở từng lớp , không phải trên cơ sở từng đối tượng.

Kiểm soát truy cập trong C ++ được thực hiện như một tính năng tĩnh, thời gian biên dịch. Tôi nghĩ rằng khá rõ ràng là không thực sự có thể thực hiện bất kỳ điều khiển truy cập có ý nghĩa nào cho mỗi đối tượng tại thời điểm biên dịch. Chỉ có thể thực hiện điều khiển từng lớp theo cách đó.

Một số gợi ý về điều khiển theo từng đối tượng có trong đặc tả truy cập được bảo vệ , đó là lý do tại sao nó thậm chí còn có chương dành riêng trong tiêu chuẩn (11.5). Nhưng vẫn còn bất kỳ tính năng cho mỗi đối tượng được mô tả ở đó khá thô sơ. Một lần nữa, kiểm soát truy cập trong C ++ có nghĩa là hoạt động trên cơ sở từng lớp.


9
+1. C ++ lớn về cơ chế thời gian biên dịch, không lớn về cơ chế thời gian chạy. Quy tắc chung khá tốt.
Nemo

4
Của bạn "không thực sự có thể triển khai bất kỳ kiểm soát truy cập có ý nghĩa nào cho mỗi đối tượng tại thời điểm biên dịch". Tại sao không? Trong void X::f(X&x), trình biên dịch có khả năng dễ dàng phân biệt this->ax.a. Không phải (luôn luôn) trình biên dịch có thể biết điều đó *thisxthực sự là cùng một đối tượng nếu x.f(x)được gọi, nhưng tôi rất có thể thấy một nhà thiết kế ngôn ngữ thấy điều này OK.
André Caron

@ AndréCaron Tôi nghĩ đây thực sự là một cái ấm đựng cá lớn hơn nhiều, vậy thì bạn có thể làm như vậy. Khi nội tuyến không xảy ra trình biên dịch sẽ luôn luôn phải làm một kiểm tra liệu this&xđều giống nhau. Để làm cho nó tồi tệ hơn, điều này thực sự kết thúc là một vấn đề ngay cả với X::f(Y& y), bởi vì đối tượng cụ thể của chúng tôi có thể thuộc loại Zkế thừa từ cả XY. Tóm lại, đó là một mớ hỗn độn thực sự, không hiệu quả, khó có thể tạo ra công việc hợp lý với MI.
Nir Friedman

@NirFriedman Tôi nghĩ bạn hiểu sai đề xuất. Khi biên dịch X::f(X& x), nếu có quyền truy cập x.a, nó sẽ không biên dịch. Không có gì khác thay đổi, không cần phải chèn kiểm tra, vì vậy hiệu suất của các chương trình vẫn còn hiệu lực không bị ảnh hưởng. Và nó không được đề xuất như một thay đổi đột phá đối với C ++ hiện có, mà là thứ mà các nhà thiết kế có thể đã làm khi giới thiệu privateban đầu.
Alexey Romanov

31

"Riêng tư" thực sự không phải là một cơ chế kiểm soát truy cập theo nghĩa "Tôi đã đặt ảnh của mình trên facebook ở chế độ riêng tư để bạn không thể nhìn thấy chúng."

Trong C ++, "private" chỉ đơn giản nói rằng đây là những phần của một lớp mà bạn (người lập trình của lớp) có thể thay đổi trong các phiên bản tương lai, v.v. và bạn không muốn những người lập trình khác sử dụng lớp của mình dựa vào sự tồn tại hoặc chức năng của chúng. .

Nếu bạn muốn kiểm soát truy cập thực sự, bạn nên thực hiện các kỹ thuật bảo mật dữ liệu chính hãng.


13

Đây là một câu hỏi hay và tôi đã gặp câu hỏi này gần đây. Tôi đã có một số cuộc thảo luận với các đồng nghiệp của mình và đây là bản tóm tắt cuộc thảo luận của chúng tôi: Đây là do thiết kế. Nó không có nghĩa là thiết kế này hoàn toàn hợp lý cho mọi trường hợp, nhưng phải có một số cân nhắc tại sao mỗi lớp riêng tư được chọn. Những lý do có thể mà chúng tôi có thể nghĩ đến bao gồm:

Trước hết, chi phí kiểm soát truy cập cho mỗi phiên bản có thể rất cao. Điều này đã được thảo luận bởi những người khác trong chủ đề này. Về lý thuyết, điều này có thể được thực hiện thông qua kiểm tra con trỏ này . Tuy nhiên, điều này không thể được thực hiện tại thời điểm biên dịch và chỉ có thể được thực hiện trong thời gian chạy. Vì vậy, bạn phải xác định quyền kiểm soát truy cập của từng thành viên tại thời điểm chạy và khi nó bị vi phạm, có thể chỉ có các trường hợp ngoại lệ mới được đưa ra. Chi phí cao.

Thứ hai, kiểm soát truy cập mỗi lớp có trường hợp sử dụng riêng, như hàm tạo bản sao hoặc toán tử =. Sẽ khó thực hiện chúng nếu kiểm soát truy cập là từng trường hợp.

Thêm vào đó, kiểm soát truy cập chủ yếu từ góc độ lập trình / ngôn ngữ, để biết cách mô-đun hóa / kiểm soát quyền truy cập vào mã / thành viên, không phải dữ liệu.


12

Đó là một quyết định thiết kế ngôn ngữ tùy ý. Ví dụ, trong Ruby , privatethực sự có nghĩa là riêng tư, như trong "chỉ cá thể mới có thể truy cập các thành viên dữ liệu cá nhân của riêng nó". Tuy nhiên, điều này có phần hạn chế.

Như đã chỉ ra trong các nhận xét, các trình tạo bản sao và toán tử gán là những nơi phổ biến mà bạn truy cập trực tiếp vào các thành viên dữ liệu riêng tư của cá thể khác. Có ít lý do rõ ràng hơn tại sao.

Hãy xem xét trường hợp sau đây. Bạn đang triển khai danh sách liên kết OO. Danh sách liên kết có một lớp nút lồng nhau để quản lý con trỏ. Bạn có thể triển khai lớp nút này để nó tự quản lý các con trỏ (thay vì đặt con trỏ công khai và được quản lý bởi danh sách). Trong trường hợp như vậy, bạn sẽ có các đối tượng nút muốn sửa đổi con trỏ của các đối tượng nút khác ở những vị trí khác mà hàm tạo bản sao điển hình và toán tử gán.


4

Bí quyết là hãy nhớ rằng dữ liệu là private đến lớp , không phải là ví dụ của lớp. Bất kỳ phương thức nào trong lớp của bạn đều có thể truy cập dữ liệu riêng tư của bất kỳ phiên bản nào của lớp đó; không có cách nào để giữ dữ liệu riêng tư trong một phiên bản trừ khi bạn cấm các phương thức truy cập rõ ràng vào các thành viên dữ liệu riêng tư của các phiên bản khác.


1

Ngoài tất cả các câu trả lời ở trên, hãy xem xét các hàm tạo bản sao tùy chỉnh, toán tử gán và tất cả các hàm khác mà bạn sẽ viết cho một lớp hoạt động trên các trường hợp khác . Bạn sẽ cần các hàm truy cập cho tất cả các thành viên dữ liệu đó.


-8

Dữ liệu cá nhân vẫn ở chế độ riêng tư cho đến khi ai đó có quyền truy cập vào nó tiết lộ cho người khác.

Khái niệm này cũng áp dụng cho các trường hợp khác, chẳng hạn như:

class cMyClass
{
public:
   // ...
   // omitted for clarity
   // ...

   void Withdraw(int iAmount)
   {
      iTheSecretVault -= iAmount;
   }

private:
   int iTheSecretVault;
};

Làm thế nào mọi người có thể rút tiền? :)


3
Ví dụ này không liên quan đến một cá thể lớp truy cập các thành viên dữ liệu riêng tư của cá thể khác.
André Caron

@Andre, "Khái niệm này cũng áp dụng cho các tình huống khác, chẳng hạn như ..."
YeenFei

^ "tình huống khác" là lạc đề theo định nghĩa, vì vậy ví dụ của bạn không có liên quan (và tôi không chắc nó sẽ có nhiều thông tin ở bất kỳ nơi nào khác)
underscore_d

1
Đây không phải là lời giải thích chính xác cho câu hỏi.
Panda
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.