Tại sao tôi có thể truy cập các biến riêng trong hàm tạo bản sao?


88

Tôi đã học được rằng tôi không bao giờ có thể truy cập một biến private, chỉ với một hàm get trong lớp. Nhưng tại sao tôi có thể truy cập nó trong hàm tạo bản sao?

Thí dụ:

Field::Field(const Field& f)
{
  pFirst = new T[f.capacity()];

  pLast = pFirst + (f.pLast - f.pFirst);
  pEnd  = pFirst + (f.pEnd - f.pFirst);
  std::copy(f.pFirst, f.pLast, pFirst);
}

Tuyên bố của tôi:

private:
  T *pFirst,*pLast,*pEnd;

Bởi vì phương thức tạo bản sao là một thành viên của lớp theo mặc định, và một số phương thức khác cũng vậy.
DumbCoder

+ 53 / -0? Ai đã bỏ phiếu cho điều này? Bạn sẽ sao chép chúng bằng cách nào khác ?!? (Hãy gỡ lỗi các lựa chọn không thay thế: Tạo một getter tham chiếu công khai cho từng thành viên riêng tư? Sau đó, chúng hoàn toàn không phải là riêng tư. Tạo const&getter công khai hoặc theo giá trị cho mỗi thành viên? Khi đó chúng chỉ 'ghi-riêng tư', & đối với các giá trị lãng phí tài nguyên và thất bại đối với các thành viên không thể sao chép.) Tôi cảm thấy bối rối trước sự thành công của một câu hỏi bỏ trống như vậy, hỏi về xây dựng bản sao trong khi hoàn toàn bỏ qua ý nghĩa của nó và không có câu trả lời nào sử dụng logic cơ bản để gỡ rối nó. Họ giải thích technicalities khô, nhưng có một câu trả lời đơn giản hơn nhiều cho một câu hỏi này thiển
underscore_d

8
@underscore_d, "Bạn sẽ sao chép chúng bằng cách nào khác?" là một phản ứng rất lạ theo quan điểm của tôi. Nó giống như trả lời "trọng lực hoạt động như thế nào?" với "làm thế nào khác mọi thứ sẽ rơi xuống!" Việc nhầm lẫn đóng gói mức lớp với đóng gói mức đối tượng trên thực tế khá phổ biến. Thật buồn cười khi bạn nghĩ rằng đó là một câu hỏi ngu ngốc và câu trả lời phải rõ ràng. Hãy nhớ rằng việc đóng gói trong Smalltalk (được cho là ngôn ngữ OO nguyên mẫu) thực sự hoạt động ở cấp độ đối tượng.
aioobe

@aioobe Điểm tốt, cảm ơn. Nhận xét của tôi hơi cực đoan - có lẽ hôm đó máy pha cà phê đã bị hỏng. Tôi đánh giá cao bạn đã chỉ ra lý do tại sao câu hỏi này lại phổ biến, đặc biệt là trong số những người đến từ các ngôn ngữ OO khác (và có lẽ nhiều hơn). Trên thực tế, có thể cho rằng nhận xét của tôi là thứ đã được "nhấp nháy", vì tôi đang viết từ góc độ của một người chủ yếu lập trình bằng C ++. Ngoài ra, hãy yêu thích sự tương tự trọng lực đó!
underscore_d

Câu trả lời:


33

IMHO, các câu trả lời hiện có làm một công việc kém giải thích "Tại sao" của điều này - tập trung quá nhiều vào việc nhắc lại giá trị của hành vi nào. "các công cụ sửa đổi truy cập hoạt động ở cấp độ lớp chứ không phải ở cấp độ đối tượng." - có, nhưng tại sao?

Khái niệm bao quát ở đây là (các) lập trình viên thiết kế, viết và duy trì một lớp được (được) mong đợi hiểu cách đóng gói OO mong muốn và được trao quyền để điều phối việc thực hiện nó. Vì vậy, nếu bạn đang viết class X, bạn không chỉ mã hóa cách một X xđối tượng riêng lẻ có thể được sử dụng bởi mã có quyền truy cập vào nó, mà còn cả cách:

  • các lớp dẫn xuất có thể tương tác với nó (thông qua các chức năng ảo thuần túy tùy chọn và / hoặc quyền truy cập được bảo vệ), và
  • Xcác đối tượng khác biệt hợp tác để đưa ra các hành vi dự kiến ​​đồng thời tôn trọng các điều kiện hậu và bất biến từ thiết kế của bạn.

Nó không chỉ là hàm tạo bản sao - rất nhiều hoạt động có thể liên quan đến hai hoặc nhiều trường hợp của lớp của bạn: nếu bạn đang so sánh, thêm / nhân / chia, sao chép-xây dựng, sao chép, gán, v.v. thì bạn thường gặp hoặc đơn giản là phải có quyền truy cập vào dữ liệu riêng tư và / hoặc được bảo vệ trong đối tượng kia, hoặc muốn nó cho phép triển khai chức năng đơn giản hơn, nhanh hơn hoặc nói chung là tốt hơn.

Cụ thể, các hoạt động này có thể muốn tận dụng quyền truy cập riêng tư để thực hiện những việc như:

  • (các trình tạo bản sao) sử dụng một thành viên riêng của đối tượng "rhs" (bên tay phải) trong danh sách trình khởi tạo, để một biến thành viên chính nó được tạo bản sao thay vì được xây dựng mặc định (nếu thậm chí là hợp pháp) sau đó cũng được gán (một lần nữa, nếu hợp pháp)
  • chia sẻ tài nguyên - xử lý tệp, phân đoạn bộ nhớ được chia sẻ, shared_ptrdữ liệu tham chiếu, v.v.
  • có quyền sở hữu vật, ví dụ: auto_ptr<>"chuyển" quyền sở hữu sang vật thể đang xây dựng
  • sao chép các thành viên "bộ nhớ cache", hiệu chuẩn hoặc trạng thái riêng tư cần thiết để xây dựng đối tượng mới ở trạng thái có thể sử dụng tối ưu mà không cần phải tạo lại chúng từ đầu
  • sao chép / truy cập thông tin chẩn đoán / theo dõi được lưu giữ trong đối tượng đang được sao chép mà không thể truy cập được thông qua các API công khai nhưng có thể được sử dụng bởi một số đối tượng ngoại lệ sau này hoặc ghi nhật ký (ví dụ: một số thông tin về thời gian / hoàn cảnh khi phiên bản không được tạo "nguyên bản" được xây dựng)
  • thực hiện sao chép một số dữ liệu hiệu quả hơn: ví dụ như các đối tượng có thể có một unordered_mapthành viên nhưng chỉ hiển thị công khai begin()end()trình vòng lặp - với quyền truy cập trực tiếp vào size()bạn có thể reservesao chép nhanh hơn; tệ hơn nữa nếu họ chỉ để lộ at()insert()và nếu không throw...
  • sao chép các tham chiếu trở lại đối tượng mẹ / điều phối / quản lý có thể không xác định hoặc chỉ ghi cho mã khách hàng

2
Tôi nghĩ rằng 'lý do tại sao' lớn nhất là nó sẽ là một chi phí thời gian chạy rất lớn để kiểm tra xem this == othermỗi lần bạn truy cập other.x, bạn sẽ phải truy cập nếu các công cụ sửa đổi truy cập hoạt động ở cấp đối tượng.
aioobe

2
@aioobe Tôi nghĩ câu trả lời của bạn nên theo cách, cách nổi bật hơn. Trong khi câu trả lời của Tony thực sự hay và mang tính khái niệm, nếu tôi là một người cá cược, tôi cá rằng câu trả lời của bạn là lý do lịch sử thực tế cho sự lựa chọn. Nó không chỉ hiệu quả hơn mà còn đơn giản hơn nhiều. Sẽ là một câu hỏi tuyệt vời cho Bjarne!
Nir Friedman

Tôi đã đánh dấu câu trả lời của bạn, bởi vì nó giải thích nền;)
demonking

@demonking, tôi nghĩ lý do được đưa ra trong câu trả lời này bao gồm lý do tại sao việc để dữ liệu riêng tư được mở cho các đối tượng khác là thuận tiện . Nhưng công cụ sửa đổi quyền truy cập không có nghĩa là làm cho dữ liệu đủ "công khai". Chúng có ý nghĩa làm cho dữ liệu đóng đủ để đóng gói. (Về mặt tiện lợi , sẽ tốt hơn nếu các biến riêng tư ở nơi công cộng!) Tôi đã cập nhật câu trả lời của mình bằng một phần mà tôi nghĩ giải quyết tốt hơn lý do thực tế .
aioobe

@aioobe: bình luận cũ, nhưng dù sao thì ... "hãy kiểm tra xem this == othermỗi lần bạn truy cập other.x" - bỏ lỡ điểm - nếu other.xchỉ được chấp nhận trong thời gian chạy khi tương đương với this.x, sẽ không có nhiều văn bản con trỏ other.xngay từ đầu; trình biên dịch cũng có thể buộc bạn viết if (this == other) ...this.x...cho bất cứ điều gì bạn định làm. Quan niệm "thuận tiện (thậm chí nhiều hơn nếu các biến riêng là công khai)" của bạn cũng thiếu điểm - cách xác định của Tiêu chuẩn đủ hạn chế để cho phép đóng gói thích hợp , nhưng không gây bất tiện không cần thiết.
Tony Delroy

108

Các công cụ sửa đổi quyền truy cập hoạt động ở cấp độ lớp chứ không phải ở cấp độ đối tượng .

Có nghĩa là, hai đối tượng của cùng một lớp có thể truy cập dữ liệu riêng tư của nhau.

Tại sao:

Chủ yếu là do hiệu quả. Sẽ là một chi phí thời gian chạy không đáng kể để kiểm tra xem this == othermỗi lần bạn truy cậpother.x , bạn sẽ phải truy cập nếu các công cụ sửa đổi truy cập hoạt động ở cấp đối tượng.

Nó cũng hợp lý về mặt ngữ nghĩa nếu bạn nghĩ về nó theo phạm vi: "Tôi cần ghi nhớ phần mã lớn như thế nào khi sửa đổi một biến riêng?" - Bạn cần ghi nhớ mã của cả lớp và đây là mã trực giao với các đối tượng tồn tại trong thời gian chạy.

Và nó cực kỳ thuận tiện khi viết các hàm tạo bản sao và toán tử gán.


35

Bạn có thể truy cập các thành viên riêng tư của một lớp từ trong lớp, thậm chí cả những thành viên của một cá thể khác.


10

Để hiểu câu trả lời, tôi xin nhắc lại một vài khái niệm.

  1. Bất kể bạn tạo bao nhiêu đối tượng, chỉ có một bản sao của một hàm trong bộ nhớ cho lớp đó. Nó có nghĩa là các chức năng chỉ được tạo một lần. Tuy nhiên, các biến là riêng biệt cho từng cá thể của lớp.
  2. this con trỏ được chuyển tới mọi hàm khi được gọi.

Bây giờ là vì this con trỏ, hàm có thể xác định vị trí các biến của phiên bản cụ thể đó. không có vấn đề nếu nó là riêng tư của công cộng. nó có thể được truy cập bên trong chức năng đó. Bây giờ nếu chúng ta truyền một con trỏ đến một đối tượng khác của cùng một lớp. sử dụng con trỏ thứ hai này, chúng tôi sẽ có thể truy cập các thành viên riêng tư.

Hy vọng điều này trả lời câu hỏi của bạn.


6

Hàm tạo bản sao là hàm thành viên của lớp và như vậy có quyền truy cập vào các thành viên dữ liệu của lớp ', ngay cả những thành viên được khai báo là' riêng tư '.


3
Là một người biết ngôn ngữ, tôi hiểu ý bạn. Tuy nhiên, nếu tôi không biết ngôn ngữ này, tôi sẽ nghĩ rằng bạn chỉ đang lặp lại câu hỏi với tôi.
San Jacinto
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.