Con trỏ lớp C++ tới chính nó
Một khai báo lớp có thể chứa đối tượng tĩnh của kiểu tự, nó cũng có thể có con trỏ tới kiểu tự, nhưng nó không thể có đối tượng không tĩnh kiểu tự Show
Ví dụ, chương trình sau hoạt động tốt class String { char const *(*d_sp)() const; public: char const *get() const; };65 class String { char const *(*d_sp)() const; public: char const *get() const; };66 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };68 class String { char const *(*d_sp)() const; public: char const *get() const; };69 class String { char const *(*d_sp)() const; public: char const *get() const; };70 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };72 class String { char const *(*d_sp)() const; public: char const *get() const; };73 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };660 class String { char const *(*d_sp)() const; public: char const *get() const; };661 class String { char const *(*d_sp)() const; public: char const *get() const; };662 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };665 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };667 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };669 class String { char const *(*d_sp)() const; public: char const *get() const; };670 class String { char const *(*d_sp)() const; public: char const *get() const; };671 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };673 ________ 174 ________ 1675 ________ 1676 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };678 class String { char const *(*d_sp)() const; public: char const *get() const; };679 class String { char const *(*d_sp)() const; public: char const *get() const; };680 Và chương trình sau đây cũng hoạt động tốt class String { char const *(*d_sp)() const; public: char const *get() const; };681 class String { char const *(*d_sp)() const; public: char const *get() const; };66 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };68 class String { char const *(*d_sp)() const; public: char const *get() const; };69 class String { char const *(*d_sp)() const; public: char const *get() const; };70 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };72 class String { char const *(*d_sp)() const; public: char const *get() const; };73 ________ 174 ________ 1691 ________ 1692 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };665 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };667 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };669 class String { char const *(*d_sp)() const; public: char const *get() const; };670 class String { char const *(*d_sp)() const; public: char const *get() const; };671 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };673 ________ 174 ________ 1675 ________ 1676 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };678 class String { char const *(*d_sp)() const; public: char const *get() const; };679 class String { char const *(*d_sp)() const; public: char const *get() const; };680 Nhưng chương trình sau tạo ra lỗi biên dịch "trường `self' có kiểu không đầy đủ" class String { char const *(*d_sp)() const; public: char const *get() const; };671 class String { char const *(*d_sp)() const; public: char const *get() const; };66 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };68 class String { char const *(*d_sp)() const; public: char const *get() const; };69 class String { char const *(*d_sp)() const; public: char const *get() const; };70 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };72 class String { char const *(*d_sp)() const; public: char const *get() const; };73 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };721 class String { char const *(*d_sp)() const; public: char const *get() const; };722 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };665 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };667 class String { char const *(*d_sp)() const; public: char const *get() const; };67 class String { char const *(*d_sp)() const; public: char const *get() const; };669 class String { char const *(*d_sp)() const; public: char const *get() const; };670 class String { char const *(*d_sp)() const; public: char const *get() const; };671 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };673 ________ 174 ________ 1675 ________ 1676 class String { char const *(*d_sp)() const; public: char const *get() const; };74 class String { char const *(*d_sp)() const; public: char const *get() const; };678 class String { char const *(*d_sp)() const; public: char const *get() const; };679 class String { char const *(*d_sp)() const; public: char const *get() const; };680 Nếu một đối tượng không tĩnh là thành viên thì phần khai báo của lớp không đầy đủ và trình biên dịch không có cách nào tìm ra kích thước của các đối tượng trong lớp. Cảm ơn Manish Jain và Venki vì sự đóng góp của họ cho bài đăng này. Vui lòng viết bình luận nếu bạn thấy bất cứ điều gì không chính xác hoặc bạn muốn chia sẻ thêm thông tin về chủ đề thảo luận ở trên Tồn tại các tình huống mà chúng ta không cần một con trỏ tới một đối tượng mà là một con trỏ tới các thành viên của một lớp. Con trỏ tới các thành viên có thể được sử dụng một cách hữu ích để định cấu hình hành vi của các đối tượng của lớp. Tùy thuộc vào thành viên nào mà con trỏ tới thành viên trỏ tới đối tượng sẽ hiển thị hành vi nhất định Mặc dù con trỏ tới các thành viên có công dụng của chúng, tính đa hình thường có thể được sử dụng để thực hiện hành vi có thể so sánh được. Hãy xem xét một lớp học có một thành viên char const * ( String::somefun ) () const2 thực hiện một trong một loạt các hành vi thay thế. Thay vì chọn hành vi lựa chọn tại thời điểm xây dựng đối tượng, lớp có thể sử dụng giao diện của một số lớp cơ sở (trừu tượng), chuyển một đối tượng của một số lớp dẫn xuất tới hàm tạo của nó và do đó có thể định cấu hình hành vi của nó. Điều này cho phép cấu hình dễ dàng, có thể mở rộng và linh hoạt, nhưng việc truy cập vào các thành viên dữ liệu của lớp sẽ kém linh hoạt hơn và có thể yêu cầu sử dụng các khai báo `friend'. Trong những trường hợp như vậy, con trỏ tới các thành viên thực sự có thể được ưu tiên hơn vì điều này cho phép cấu hình (hơi kém linh hoạt hơn) cũng như truy cập trực tiếp vào các thành viên dữ liệu của một lớp Vì vậy, sự lựa chọn rõ ràng là giữa một bên là cấu hình dễ dàng và mặt khác là dễ dàng truy cập vào các thành viên dữ liệu của lớp. Trong chương này, chúng ta sẽ tập trung vào các con trỏ tới các thành viên, điều tra xem những con trỏ này cung cấp những gì 16. 1. Con trỏ đến các thành viên. một ví dụBiết cách con trỏ tới biến và đối tượng được sử dụng không trực quan dẫn đến khái niệm con trỏ tới thành viên. Ngay cả khi các kiểu trả về và kiểu tham số của các hàm thành viên được tính đến, vẫn có thể dễ dàng gặp phải những điều bất ngờ. Ví dụ, hãy xem xét lớp sau.class String { char const *(*d_sp)() const; public: char const *get() const; }; Đối với lớp này, không thể để char const * ( String::somefun ) () const3 trỏ đến hàm thành viên char const * ( String::somefun ) () const4 vì không thể cho char const * ( String::somefun ) () const5 địa chỉ của hàm thành viên char const * ( String::somefun ) () const6 Một trong những lý do tại sao điều này không hoạt động là biến char const * ( String::somefun ) () const5 có phạm vi toàn cầu (nó là một con trỏ tới một hàm, không phải là một con trỏ tới một hàm trong char const * ( String::somefun ) () const8), trong khi hàm thành viên char const * ( String::somefun ) () const6 được định nghĩa trong lớp char const * ( String::somefun ) () const8 và . Việc char const * ( String::somefun ) () const5 là thành viên dữ liệu của lớp char const * ( String::somefun ) () const8 không liên quan ở đây. Theo định nghĩa của char const * ( String::somefun ) () const5, nó trỏ đến một hàm tồn tại ở đâu đó bên ngoài lớp Do đó, để định nghĩa một con trỏ tới một thành viên (dữ liệu hoặc hàm, nhưng thường là hàm) của một lớp, phạm vi của con trỏ phải chỉ ra phạm vi lớp. Làm như vậy, một con trỏ tới thành viên char const * ( String::somefun ) () const4 được định nghĩa như thế này class String { char const *(*d_sp)() const; public: char const *get() const; };5 Vì vậy, bằng cách thêm tiền tố vào thành viên dữ liệu con trỏ class String { char const *(*d_sp)() const; public: char const *get() const; };06 bởi class String { char const *(*d_sp)() const; public: char const *get() const; };07, nó được định nghĩa là một con trỏ trong ngữ cảnh của lớp char const * ( String::somefun ) () const8. Theo định nghĩa của nó, nó là một con trỏ tới một hàm trong lớp char const * ( String::somefun ) () const8, không mong đợi các đối số, không sửa đổi dữ liệu của đối tượng và trả về một con trỏ tới các ký tự không đổi 16. 2. Xác định con trỏ tới các thành viênCon trỏ tới các thành viên được xác định bằng cách đặt trước ký hiệu con trỏ bình thường với lớp thích hợp cộng với toán tử phân giải phạm vi. Do đó, trong phần trước, chúng tôi đã sử dụngchar const * ( String::somefun ) () const00 để chỉ ra rằng char const * ( String::somefun ) () const5
Do đó, nguyên mẫu của một chức năng phù hợp là char const * ( String::somefun ) () const8 đó là bất kỳ hàm không tham số char const * ( String::somefun ) () const05 nào trong lớp char const * ( String::somefun ) () const8, trả về một giá trị char const * ( String::somefun ) () const06 Khi xác định con trỏ tới các thành viên, quy trình chuẩn để xây dựng con trỏ tới hàm vẫn có thể được áp dụng
Đây là một ví dụ khác, xác định một con trỏ tới một thành viên dữ liệu. Giả sử lớp char const * ( String::somefun ) () const8 chứa một thành viên char const * ( String::somefun ) () const33. Làm thế nào để xây dựng một con trỏ tới thành viên này?
Ngoài ra, một quy tắc ngón tay cái rất đơn giản là
trở thành một con trỏ tới một hàm thành viên sau khi thêm tiền tố vào phạm vi lớp class String { char const *(*d_sp)() const; public: char const *get() const; };51 Không có gì buộc chúng tôi phải xác định con trỏ tới các thành viên trong các lớp mục tiêu ( char const * ( String::somefun ) () const8) của chúng. Con trỏ tới các thành viên có thể được định nghĩa trong lớp đích của chúng (để chúng trở thành thành viên dữ liệu) hoặc trong lớp khác hoặc dưới dạng biến cục bộ hoặc biến toàn cục. Trong tất cả các trường hợp này, con trỏ tới biến thành viên có thể được cung cấp địa chỉ của loại thành viên mà nó trỏ tới. Phần quan trọng là một con trỏ tới thành viên có thể được khởi tạo hoặc gán mà không yêu cầu sự tồn tại của một đối tượng của lớp đích của con trỏ Việc khởi tạo hoặc gán địa chỉ cho một con trỏ như vậy chỉ cho biết con trỏ trỏ tới thành viên nào. Đây có thể được coi là một số loại địa chỉ tương đối; . Không có đối tượng nào được yêu cầu khi con trỏ tới các thành viên được khởi tạo hoặc gán. Mặc dù được phép khởi tạo hoặc gán một con trỏ cho thành viên, nhưng (tất nhiên) không thể gọi các thành viên đó mà không chỉ định một đối tượng thuộc loại chính xác Trong ví dụ sau đây, việc khởi tạo và gán cho con trỏ tới các thành viên được minh họa (vì mục đích minh họa, tất cả các thành viên của lớp char const * ( String::somefun ) () const36 được định nghĩa là char const * ( String::somefun ) () const37). Trong ví dụ này, toán tử char const * ( String::somefun ) () const38 được sử dụng để xác định địa chỉ của các thành viên. Các toán tử này cũng như phạm vi lớp được yêu cầu. Ngay cả khi được sử dụng bên trong triển khai thành viên class String { char const *(*d_sp)() const; public: char const *get() const; };52Điều này không liên quan gì đặc biệt. Sự khác biệt với con trỏ ở phạm vi toàn cầu là chúng ta hiện đang giới hạn bản thân trong phạm vi của lớp char const * ( String::somefun ) () const36. Do hạn chế này, tất cả các định nghĩa con trỏ và tất cả các biến có địa chỉ được sử dụng phải được cung cấp phạm vi lớp char const * ( String::somefun ) () const36 Con trỏ tới thành viên cũng có thể được sử dụng với hàm thành viên char const * ( String::somefun ) () const01. Không yêu cầu cú pháp đặc biệt khi trỏ đến các thành viên ảo. Việc xây dựng, khởi tạo và gán con trỏ được thực hiện giống hệt với cách thực hiện với các thành viên không ảo 16. 3. Sử dụng con trỏ tới các thành viênSử dụng con trỏ tới thành viên để gọi một hàm thành viên yêu cầu sự tồn tại của một đối tượng của lớp thành viên mà con trỏ tới thành viên tham chiếu tới. Với các con trỏ hoạt động ở phạm vi toàn cầu, toán tử hủy hội nghịchar const * ( String::somefun ) () const31 được sử dụng. Với con trỏ tới đối tượng, toán tử bộ chọn trường hoạt động trên con trỏ ( char const * ( String::somefun ) () const03) hoặc bộ chọn trường hoạt động trên đối tượng ( char const * ( String::somefun ) () const04) có thể được sử dụng để chọn thành viên thích hợp Để sử dụng một con trỏ tới thành viên kết hợp với một đối tượng, bộ chọn trường con trỏ tới thành viên ( char const * ( String::somefun ) () const05) phải được chỉ định. Để sử dụng một con trỏ tới một thành viên thông qua một con trỏ tới một đối tượng, `bộ chọn trường con trỏ tới thành viên thông qua một con trỏ tới một đối tượng' ( char const * ( String::somefun ) () const06) phải được chỉ định. Hai toán tử này kết hợp các khái niệm về lựa chọn trường (các phần char const * ( String::somefun ) () const04 và char const * ( String::somefun ) () const03) để tiếp cận trường thích hợp trong một đối tượng và hủy bỏ tham chiếu. một hoạt động hủy đăng ký được sử dụng để tiếp cận hàm hoặc biến con trỏ tới thành viên trỏ tới Sử dụng ví dụ từ phần trước, hãy xem cách chúng ta có thể sử dụng con trỏ tới hàm thành viên và con trỏ tới thành viên dữ liệu class String { char const *(*d_sp)() const; public: char const *get() const; };53Chúng tôi lưu ý
char const * ( String::somefun ) () const16 từ phần 9. 3. char const * ( String::somefun ) () const16 định nghĩa các thành viên dữ liệu chứa tên, địa chỉ và số điện thoại của một người. Giả sử chúng tôi muốn xây dựng cơ sở dữ liệu char const * ( String::somefun ) () const16 của nhân viên. Cơ sở dữ liệu nhân viên có thể được truy vấn, nhưng tùy thuộc vào loại người truy vấn cơ sở dữ liệu, tên, tên và số điện thoại hoặc tất cả thông tin được lưu trữ về người đó sẽ được cung cấp. Điều này ngụ ý rằng một hàm thành viên như char const * ( String::somefun ) () const19 phải trả về một cái gì đó như ` char const * ( String::somefun ) () const20' trong trường hợp người truy vấn cơ sở dữ liệu không được phép xem địa chỉ của người đó và địa chỉ thực trong các trường hợp khác Cơ sở dữ liệu nhân viên được mở chỉ định một đối số phản ánh trạng thái của nhân viên muốn thực hiện một số truy vấn. Trạng thái có thể phản ánh vị trí của họ trong tổ chức, chẳng hạn như char const * ( String::somefun ) () const21, char const * ( String::somefun ) () const22, char const * ( String::somefun ) () const23 hoặc char const * ( String::somefun ) () const24. Hai loại đầu tiên được phép xem tất cả thông tin về nhân viên, char const * ( String::somefun ) () const23 được phép xem số điện thoại của nhân viên, trong khi char const * ( String::somefun ) () const24 chỉ được phép xác minh xem một người có thực sự là thành viên của tổ chức hay không Bây giờ chúng tôi xây dựng một thành viên char const * ( String::somefun ) () const27 trong lớp cơ sở dữ liệu. Một triển khai tiêu chuẩn của lớp này có thể là class String { char const *(*d_sp)() const; public: char const *get() const; };54 Mặc dù không mất nhiều thời gian, nhưng dù sao thì char const * ( String::somefun ) () const28 vẫn phải được đánh giá mỗi khi char const * ( String::somefun ) () const29 được gọi. Thay vì sử dụng một switch, chúng ta có thể định nghĩa một thành viên class String { char const *(*d_sp)() const; public: char const *get() const; };500 như một con trỏ tới một hàm thành viên của lớp class String { char const *(*d_sp)() const; public: char const *get() const; };501 trả về một class String { char const *(*d_sp)() const; public: char const *get() const; };502 và mong đợi một con trỏ tới một char const * ( String::somefun ) () const16 làm đối số của nó Thay vì đánh giá công tắc, con trỏ này có thể được sử dụng để trỏ tới class String { char const *(*d_sp)() const; public: char const *get() const; };504, class String { char const *(*d_sp)() const; public: char const *get() const; };505 hoặc class String { char const *(*d_sp)() const; public: char const *get() const; };506. Hơn nữa, hàm thành viên mà con trỏ trỏ tới sẽ được biết vào thời điểm đối tượng class String { char const *(*d_sp)() const; public: char const *get() const; };501 được xây dựng và do đó, giá trị của nó chỉ cần được xác định một lần (tại thời điểm xây dựng đối tượng PersonData) Đã khởi tạo class String { char const *(*d_sp)() const; public: char const *get() const; };500, chức năng thành viên char const * ( String::somefun ) () const29 hiện được triển khai đơn giản như class String { char const *(*d_sp)() const; public: char const *get() const; };55 Thành viên class String { char const *(*d_sp)() const; public: char const *get() const; };500 được định nghĩa như sau (trong lớp class String { char const *(*d_sp)() const; public: char const *get() const; };501, bỏ qua các thành viên khác) class String { char const *(*d_sp)() const; public: char const *get() const; };56 Cuối cùng, hàm khởi tạo khởi tạo class String { char const *(*d_sp)() const; public: char const *get() const; };500. Điều này có thể được thực hiện bằng cách sử dụng một công tắc đơn giản class String { char const *(*d_sp)() const; public: char const *get() const; };57 Lưu ý cách xác định địa chỉ của các hàm thành viên. Phạm vi của lớp class String { char const *(*d_sp)() const; public: char const *get() const; };501 phải được chỉ định, mặc dù chúng ta đã ở trong một hàm thành viên của lớp class String { char const *(*d_sp)() const; public: char const *get() const; };501 Vì các giá trị class String { char const *(*d_sp)() const; public: char const *get() const; };515 đã được biết, nên cũng có thể dễ dàng tránh được giá trị char const * ( String::somefun ) () const28 trong hàm tạo ở trên bằng cách xác định một mảng tĩnh gồm các con trỏ tới các hàm. Lớp class String { char const *(*d_sp)() const; public: char const *get() const; };501 định nghĩa mảng tĩnh class String { char const *(*d_sp)() const; public: char const *get() const; };58 và class String { char const *(*d_sp)() const; public: char const *get() const; };518 có thể được khởi tạo thời gian biên dịch class String { char const *(*d_sp)() const; public: char const *get() const; };59 Hàm tạo, thay vì sử dụng char const * ( String::somefun ) () const28, giờ đây gọi trực tiếp thành phần được yêu cầu từ phần tử mảng thích hợp char const * ( String::somefun ) () const80 Một ví dụ sử dụng con trỏ tới thành viên dữ liệu được cung cấp trong phần 19. 1. 61, trong ngữ cảnh của thuật toán di truyền ________ 1520 16. 4. Con trỏ tới các thành viên tĩnhCác thành viên tĩnh của một lớp có thể được sử dụng mà không cần có sẵn một đối tượng của lớp của chúng. Các thành viên tĩnh công khai có thể được gọi giống như các hàm miễn phí, mặc dù tên lớp của chúng phải được chỉ định khi chúng được gọiGiả sử một lớp char const * ( String::somefun ) () const8 có hàm thành viên tĩnh công khai class String { char const *(*d_sp)() const; public: char const *get() const; };522, trả về số lượng đối tượng chuỗi được tạo cho đến nay. Sau đó, không sử dụng bất kỳ đối tượng char const * ( String::somefun ) () const8 nào, hàm class String { char const *(*d_sp)() const; public: char const *get() const; };524 có thể được gọi char const * ( String::somefun ) () const81 Các thành viên tĩnh công khai có thể được gọi giống như các hàm tự do (nhưng xem thêm phần 8. 2. 1). Các thành viên tĩnh riêng tư chỉ có thể được gọi trong ngữ cảnh của lớp của chúng, bởi các hàm thành viên hoặc bạn bè của lớp chúng Vì các thành viên tĩnh không có đối tượng liên quan, địa chỉ của chúng có thể được lưu trữ trong các biến con trỏ hàm thông thường, hoạt động ở cấp độ toàn cầu. Con trỏ tới thành viên không thể được sử dụng để lưu trữ địa chỉ của thành viên tĩnh. Thí dụ char const * ( String::somefun ) () const82 16. 5. kích thước con trỏMột đặc điểm thú vị của con trỏ tới thành viên là kích thước của chúng khác với kích thước của con trỏ 'bình thường'. Xét chương trình nhỏ sau.char const * ( String::somefun ) () const83Trên kiến trúc 32 bit, một con trỏ tới hàm thành viên yêu cầu tám byte, trong khi các loại con trỏ khác yêu cầu bốn byte (Sử dụng trình biên dịch g++ của GNU) Kích thước con trỏ hiếm khi được sử dụng một cách rõ ràng, nhưng kích thước của chúng có thể gây nhầm lẫn trong các câu lệnh như char const * ( String::somefun ) () const84 Tất nhiên, class String { char const *(*d_sp)() const; public: char const *get() const; };525 có thể không phải là công cụ phù hợp để hiển thị giá trị của các con trỏ cụ thể C++ này. Các giá trị của các con trỏ này có thể được chèn vào các luồng khi sử dụng một _____1526, diễn giải lại các con trỏ 8 byte dưới dạng một chuỗi các giá trị size_t ____ char const * ( String::somefun ) () const85 Nhưng tại sao kích thước của chúng khác với kích thước của con trỏ thông thường? . Nó có nguồn gốc từ class String { char const *(*d_sp)() const; public: char const *get() const; };529 và class String { char const *(*d_sp)() const; public: char const *get() const; };530. Do đó, một class String { char const *(*d_sp)() const; public: char const *get() const; };531 chứa cả một class String { char const *(*d_sp)() const; public: char const *get() const; };532 và một class String { char const *(*d_sp)() const; public: char const *get() const; };533. Một class String { char const *(*d_sp)() const; public: char const *get() const; };531 sẽ được tổ chức như trong hình 23Hình 23. tiêu chuẩn. tổ chức đối tượng fstream Trong class String { char const *(*d_sp)() const; public: char const *get() const; };535, lớp cơ sở đầu tiên là class String { char const *(*d_sp)() const; public: char const *get() const; };536 và lớp cơ sở thứ hai là class String { char const *(*d_sp)() const; public: char const *get() const; };530. Nhưng cũng có thể là ngược lại, như minh họa trong class String { char const *(*d_sp)() const; public: char const *get() const; };538. đầu tiên là class String { char const *(*d_sp)() const; public: char const *get() const; };530, sau đó là class String { char const *(*d_sp)() const; public: char const *get() const; };529. Và đó là mấu chốt của bánh quy Nếu chúng ta có một đối tượng class String { char const *(*d_sp)() const; public: char const *get() const; };541 và thực hiện class String { char const *(*d_sp)() const; public: char const *get() const; };542, thì chúng ta gọi hàm class String { char const *(*d_sp)() const; public: char const *get() const; };543. Nhưng nếu chúng ta làm class String { char const *(*d_sp)() const; public: char const *get() const; };544, thì chúng ta gọi hàm class String { char const *(*d_sp)() const; public: char const *get() const; };545. Các chức năng này có địa chỉ riêng của chúng, giả sử &seekg và &seekp. Nhưng khi chúng ta gọi một hàm thành viên (như class String { char const *(*d_sp)() const; public: char const *get() const; };544) thì những gì chúng ta thực sự đang làm là class String { char const *(*d_sp)() const; public: char const *get() const; };547 Nhưng vấn đề ở đây là class String { char const *(*d_sp)() const; public: char const *get() const; };548 không thể hiện đúng địa chỉ đối tượng. class String { char const *(*d_sp)() const; public: char const *get() const; };549 hoạt động trên một class String { char const *(*d_sp)() const; public: char const *get() const; };533, và đối tượng đó không bắt đầu từ class String { char const *(*d_sp)() const; public: char const *get() const; };548, vì vậy (trong class String { char const *(*d_sp)() const; public: char const *get() const; };535), tại class String { char const *(*d_sp)() const; public: char const *get() const; };553 Vì vậy, trình biên dịch, khi gọi một hàm thành viên của lớp bằng cách sử dụng tính kế thừa, phải thực hiện hiệu chỉnh vị trí tương đối của một đối tượng mà chúng ta đang gọi các thành viên. Tuy nhiên, khi chúng tôi xác định một cái gì đó như char const * ( String::somefun ) () const86 và sau đó thực hiện class String { char const *(*d_sp)() const; public: char const *get() const; };554, trình biên dịch không còn biết chức năng nào thực sự được gọi. nó chỉ nhận địa chỉ của hàm. Để giải quyết vấn đề của trình biên dịch, dịch chuyển (đối với vị trí của đối tượng ofstream) hiện được lưu trữ trong chính con trỏ thành viên. Đó là một lý do tại sao trường dữ liệu bổ sung là cần thiết khi sử dụng con trỏ hàm Dưới đây là một minh họa cụ thể. trước tiên, chúng tôi xác định 2 cấu trúc, mỗi cấu trúc có một hàm thành viên (tất cả nội tuyến, sử dụng triển khai một dòng để tiết kiệm dung lượng) char const * ( String::somefun ) () const87 Sau đó, chúng tôi xác định C, có nguồn gốc từ cả A (đầu tiên) và B (tiếp theo) (có thể so sánh với class String { char const *(*d_sp)() const; public: char const *get() const; };531, nhúng class String { char const *(*d_sp)() const; public: char const *get() const; };532 và class String { char const *(*d_sp)() const; public: char const *get() const; };533) char const * ( String::somefun ) () const88 Tiếp theo, trong class String { char const *(*d_sp)() const; public: char const *get() const; };558, chúng tôi xác định các đối tượng của hai hiệp hội khác nhau và gán địa chỉ của class String { char const *(*d_sp)() const; public: char const *get() const; };559 cho các trường class String { char const *(*d_sp)() const; public: char const *get() const; };560 của chúng, nhưng class String { char const *(*d_sp)() const; public: char const *get() const; };561 xem nó như một thành viên trong thế giới class String { char const *(*d_sp)() const; public: char const *get() const; };562, trong khi class String { char const *(*d_sp)() const; public: char const *get() const; };563 xem nó như một thành viên trong thế giới class String { char const *(*d_sp)() const; public: char const *get() const; };564 Khi các trường con trỏ của liên kết đã được gán, các mảng value[] của chúng được sử dụng để hiển thị nội dung của các trường ptr (xem bên dưới) char const * ( String::somefun ) () const89 Khi chạy chương trình này, chúng ta thấy char const * ( String::somefun ) () const0 (giá trị địa chỉ của bạn (giá trị đầu tiên trên hai dòng) có thể khác nhau). Lưu ý rằng địa chỉ của các hàm giống nhau, nhưng vì trong thế giới C, đối tượng B nằm ngoài đối tượng A và đối tượng A lớn 4 byte, chúng ta phải thêm 4 vào giá trị của con trỏ ` class String { char const *(*d_sp)() const; public: char const *get() const; };565' khi gọi hàm . Đó chính xác là những gì mà giá trị shift trong trường thứ hai của con trỏ nói với trình biên dịch Một lớp có thể có một con trỏ tới chính nó không?Một khai báo lớp có thể chứa đối tượng tĩnh của kiểu tự, nó cũng có thể có con trỏ tới kiểu tự , nhưng nó không thể có kiểu khác. .
Một lớp có thể là thành viên của chính nó không?Cụ thể, mọi phần tử của một lớp là một tập hợp, vì vậy một lớp riêng không thể là phần tử của bất kỳ lớp nào (bao gồm cả chính nó). Tuy nhiên, hoàn toàn hợp lý khi cho phép các tập hợp là các phần tử của chính chúng.
Có từ khóa self trong C++ không?Trong quá trình triển khai một phương thức, mã định danh Self tham chiếu đến đối tượng mà phương thức đó được gọi . Biến Self là một tham số ẩn cho từng phương thức đối tượng. Một phương thức có thể sử dụng biến này để tham chiếu đến lớp sở hữu của nó.
Bạn có thể gán lại con trỏ không?Như bạn đã biết, địa chỉ của một đối tượng trong C++ có thể được lưu trữ thông qua tham chiếu hoặc thông qua con trỏ. Mặc dù có vẻ như chúng đại diện cho các khái niệm tương tự, nhưng một trong những điểm khác biệt quan trọng là bạn có thể gán lại một con trỏ để trỏ đến một địa chỉ khác , nhưng bạn không thể . |