cuốn sách gpt4 ai đã làm

Kế thừa C++: chuyển đổi gán lớp cha-con, kế thừa kim cương, kế thừa ảo, kế thừa và kết hợp

In lại Tác giả: Người biết Thời gian cập nhật: 2024-03-12 23:33:43 30 4
mua khóa gpt4 Nike

⏰1. Khái niệm thừa kế

Kế thừa là một trong ba đặc điểm chính của hướng đối tượng. Kế thừa có thể hiểu là tái sử dụng ở cấp độ lớp, cho phép chúng ta mở rộng và thêm các hàm mới dựa trên lớp gốc.

Khi tạo một lớp, chúng ta có thể kế thừa các thành viên và phương thức của một lớp hiện có và cải tiến trên cơ sở ban đầu này.Lớp kế thừa được gọi là lớp cơ sở, lớp mới sau kế thừa được gọi là lớp dẫn xuất..

Cách sử dụng như sau:

lớp [tên lớp dẫn xuất]: [kiểu kế thừa] [tên lớp cơ sở]

Ví dụ:

lớp Người { public: string _name; int _age }; lớp Sinh viên : public Person { protected: string _stuNum };

Lớp dẫn xuất Sinh viên ở đây sử dụng lại các phương thức và thành viên của Person, đồng thời mở rộng và bổ sung chúng trên cơ sở này.

⏰2.Phương thức kế thừa

Phương thức kế thừa giống như hạn định truy cập của lớp, được chia thành ba loại: public (kế thừa công khai), riêng tư (kế thừa riêng) và protected (kế thừa được bảo vệ).
Các phương thức kế thừa khác nhau có quyền truy cập khác nhau đối với các thành viên lớp cơ sở được kế thừa trong các lớp dẫn xuất.

Chế độ truy cập của các thành viên khác của lớp cơ sở trong lớp con = Min (chỉ định truy cập của thành viên trong lớp cơ sở, chế độ kế thừa)
Nhận xét:
1. Trong các ứng dụng thực tế, kế thừa công khai thường được sử dụng và kế thừa được bảo vệ/riêng tư hiếm khi được sử dụng. Không nên sử dụng kế thừa được bảo vệ/riêng tư vì các thành viên được kế thừa từ protected/private chỉ có thể được sử dụng trong các lớp dẫn xuất. khả năng mở rộng và bảo trì không mạnh.
2. Khi sử dụng từ khóa class thì phương thức kế thừa mặc định là riêng tư, khi sử dụng struct thì phương thức kế thừa mặc định là public, nhưng tốt nhất nên viết phương thức kế thừa một cách rõ ràng.

⏰3. Chuyển đổi phép gán giữa lớp cơ sở và lớp dẫn xuất

Các lớp dẫn xuất có thể gán các đối tượng, con trỏ hoặc tham chiếu đến các lớp cơ sở.
Ví dụ: lớp Người và lớp Sinh viên ở trên

Kiểu gán này chỉ có thể được lớp dẫn xuất gán cho lớp cơ sở (nhưng các thành viên bổ sung cần phải cắt bỏ như _ stuID), và đối tượng của lớp cơ sở không thể gán cho lớp dẫn xuất.

🍁Một con trỏ của lớp cơ sở có thể được gán cho một con trỏ của lớp dẫn xuất bằng cách ép kiểu, giống:

int main() { Person p1; Sinh viên s1; hPtr1 = &s1;//Trỏ tới đối tượng lớp dẫn xuất Person* hPtr2 = &p1;//Trỏ tới đối tượng lớp cơ sở Sinh viên* pPtr = (Student*)hPtr1;/ /Không vấn đề gì Sinh viên * pPtr = (Student*)hPtr2;//Đôi khi không có vấn đề gì, nhưng có nguy cơ vượt qua ranh giới return 0;

🍁bản tóm tắt:

1. Các lớp dẫn xuất có thể được gán cho các đối tượng, con trỏ hoặc tham chiếu của lớp cơ sở.
2. Các đối tượng của lớp cơ sở không thể được gán cho các đối tượng của lớp dẫn xuất.
3. Con trỏ của lớp cơ sở có thể được gán cho con trỏ của lớp dẫn xuất thông qua chuyển đổi kiểu bắt buộc. **Nhưng phải an toàn khi con trỏ của lớp cơ sở trỏ đến đối tượng của lớp dẫn xuất, nếu không sẽ có nguy cơ vượt quá giới hạn. **Nếu lớp cơ sở ở đây là loại đa hình, bạn có thể sử dụng Dynamic_cast của RTT để xác định và thực hiện chuyển đổi an toàn.

⏰4. Phạm vi và ẩn nấp

🎄trốn:Ẩn hay còn gọi là xác định lại, khi một thành viên có cùng tên xuất hiện trong lớp cơ sở và lớp dẫn xuất thì lớp dẫn xuất sẽ ẩn thành viên của lớp cơ sở có cùng tên rồi sử dụng tên của chính nó.(Nhưng ẩn không có nghĩa là không thể truy cập được. Bạn có thể truy cập rõ ràng các thành viên bị ẩn bằng cách chỉ định phạm vi lớp cơ sở.)

class Person { public: void f(int age) { cout << "Name" << _name << endl; cout << "Tuổi" << _age << endl; Sinh viên : public Person { public: void f() { Person::f(32);//Bạn cần gọi rõ ràng hàm f cout << "Student ID" << _stuNum << endl } riêng tư: chuỗi _stuNum };

Ví dụ: f() ở đây cấu thành việc ẩn

Đồng thời, còn có một vấn đề khác cần quan tâm,Trong các lớp cơ sở và các lớp dẫn xuất, các phương thức có cùng tên không thể cấu thành quá tải vì chúng nằm trong các phạm vi khác nhau. Miễn là tên phương thức giống nhau thì nó sẽ bị ẩn.

⏰5.Hàm thành viên mặc định của lớp dẫn xuất

Trong mỗi lớp, có 6 hàm thành viên mặc định. Ngay cả khi chúng ta không tự triển khai các hàm này, trình biên dịch sẽ giúp chúng ta triển khai chúng.

Có hai điểm cần lưu ý ở đây (các câu hỏi kiểm tra viết thường được kiểm tra):

1. Trong ba trường hợp hàm tạo, hàm tạo sao chép và toán tử=, hàm tạo/sao chép hàm tạo/toán tử= tương ứng của lớp cha phải được gọi để khởi tạo các biến thành viên của lớp cha và nếu lớp cha không có hàm tạo mặc định (Ví dụ: nếu lớp cha viết một hàm tạo có tham số), chúng ta phải gọi rõ ràng (Person (parameter...), Person::operator=(parameter...))

Cuộc gọi rõ ràng của hàm tạo
Hàm tạo của lớp dẫn xuất phải gọi hàm tạo của lớp cơ sở để khởi tạo phần thành viên đó của lớp cơ sở. Nếu lớp cơ sở không có hàm tạo mặc định thì nó phải được gọi một cách rõ ràng trong giai đoạn danh sách khởi tạo của hàm tạo của lớp dẫn xuất.

Sinh viên() :People() { cout << "Sinh viên()" << endl }

sao chép cuộc gọi rõ ràng của hàm tạo

Khuyến cáo rằng tất cả các hàm tạo bản sao nên sử dụng các lệnh gọi rõ ràng, nếu không chúng chắc chắn sẽ được gọi trong hàm tạo bản sao của một lớp con.Hàm tạo của lớp chatình huống(Bởi vì cấu trúc sao chép cũng là một cấu trúc, trongDanh sách khởi tạo, đối với các lớp con,Lớp cha tương đương với một đối tượng kiểu tùy chỉnh, lớp con sẽ gọi hàm tạo của lớp cha để khởi tạo tài nguyên của lớp cha. )

Sinh viên(const Sinh viên& s) :People(s) { cout << "Student(const Sinh viên& s)" << endl;

Toán tử= của lớp dẫn xuất phải gọi toán tử= của lớp cơ sở để hoàn thành bản sao của lớp cơ sở.

Sinh viên& operator=(const Sinh viên& s) { cout << "Student& operator = (const Sinh viên& s)" << endl; if (this != &s) { Person:: operator=(s);

kẻ hủy diệt

Vì trình biên dịch sẽ xử lý tên của hàm hủy thành hàm hủy nên các hàm hủy của lớp dẫn xuất và lớp cơ sở sẽ tạo thành một mối quan hệ ẩn. Do đó, nếu lớp dẫn xuất muốn gọi hàm hủy của lớp cơ sở thì cần phải gọi nó. một cách rõ ràng, nhưng theo mặc định, trình biên dịch sẽ gọi hàm hủy của lớp cơ sở sau khi hàm hủy của lớp dẫn xuất được gọi nên nó bị hủy hai lần.

~Person() { cout << "~Person()" << endl; } ~Student() { Person:: ~Person(); cout << "~Student()" << endl;

Trong lớp dẫn xuất, hàm hủy của lớp cơ sở sẽ bị ẩn. Mặc dù tên của chúng ở đây khác nhau nhưng để đạt được tính đa hình, chúng sẽ được trình biên dịch đổi tên thành hàm hủy. Khi gọi hàm tạo của một lớp con, trước tiên chúng ta gọi hàm tạo của lớp cha và sau đó xây dựng các thành viên của lớp con. Theo thứ tự xây dựng trước rồi mới hủy, chúng ta hủy tài nguyên của lớp con trong hàm hủy. Sau khi hàm hủy được gọi, trình biên dịch sẽ tự động gọi hàm hủy của lớp cha cho chúng ta.

⏰6.Bạn bè và thành viên tĩnh

🍁1.Bạn Nguyên

Mối quan hệ tình bạn sẽ không được kế thừa., nếu lớp con muốn sử dụng bạn của lớp cha thì bản thân lớp con đó cũng phải định nghĩa nó là bạn.

2. Thành viên tĩnh

Lớp cơ sở định nghĩa các thành viên tĩnh,Cho dù nó được kế thừa bao nhiêu lần hay có bao nhiêu lớp con được dẫn xuất thì chỉ có một thành viên tĩnh trong toàn bộ hệ thống kế thừa. Các thành viên tĩnh không còn thuộc về một lớp hoặc đối tượng nhất định mà thuộc về toàn bộ hệ thống kế thừa.

⏰7. Thừa kế kim cương và thừa kế ảo

Đầu tiên, chúng tôi giới thiệu ngắn gọn các khái niệm về thừa kế đơn và thừa kế đa.
Kế thừa đơn: Khi một lớp con chỉ có một lớp cha trực tiếp thì mối quan hệ kế thừa này được gọi là kế thừa đơn.

Đa kế thừa: Khi một lớp con có hai hoặc nhiều lớp cha trực tiếp, mối quan hệ kế thừa này được gọi là đa kế thừa.

thừa kế kim cương: Kế thừa kim cương là trường hợp đặc biệt của đa thừa kế Sau đây là một ví dụ ngắn gọn về thừa kế kim cương và sự mơ hồ mà nó mang lại:

class Human { public: int _age }; class Sinh viên : public Human { public: int _stuNum }; lớp Giáo viên : public Human { public: int _teaNum };

Ồ haha! ! ! Cảm nhận mối quan hệ của thừa kế hình thoi! ! !

🍁Hãy nói thẳng về sự mơ hồ do việc thừa kế kim cương này gây ra:
Nói một cách logic, kích thước của mỗi danh mục sẽ như thế này. Lớp con người là 4 byte, giáo viên và học sinh đều là 8 byte, trợ lý là 12 byte, nhưng thực tế trợ lý là 16 byte.

🍁Tại sao trợ lý có 16 byte?

Đây là hiện thân của vấn đề dư thừa dữ liệu và sự mơ hồ của kế thừa kim cương.
Cả giáo viên và học sinh ở đây đều thừa hưởng cùng một thành viên _tuổi từ con người. Nhưng khi trợ lý kế thừa từ giáo viên và học sinh, nó sẽ kế thừa hai độ tuổi này tương ứng, dẫn đến hai thành viên giống hệt nhau ở đây.

Trong trường hợp này, nếu sau này bạn muốn gán giá trị cho _age, trình biên dịch sẽ nhắc bạn với những hướng dẫn không rõ ràng và báo lỗi.

🍁Sự mơ hồ của việc thừa kế kim cương là một vấn đề chí tử. Làm thế nào để giải quyết nó?

Kế thừa ảo, thêm từ khóa ảo khi kế thừa lớp eo.

lớp Sinh viên : công cộng ảo Human { public: int _stuNum }; lớp Giáo viên : công cộng ảo Human { public: int _teaNum };

Lần này, vấn đề mơ hồ đã được giải quyết. Giáo viên và học sinh đều là 12 byte, trong khi trợ lý là 20 byte.

Tại sao? Đây là trích dẫn từ blog của trùm "Scavenger": Kế thừa ảo từ góc độ bộ nhớ

🍁Tóm tắt ngắn gọn
1. Có thể thấy, nếu không có kế thừa ảo thì các thành viên trong Assistant được sắp xếp liên tục. _age trong Teacher và Học sinh là hai giá trị khác nhau nhưng thực tế một người không có hai độ tuổi nên xuất hiện hiện tượng dư thừa dữ liệu.

2. 8 byte bổ sung ở đây thực chất là hai con trỏ bảng cơ sở ảo. Vì _age trong Human ở đây được chia sẻ giữa giáo viên và học sinh nên để thuận tiện cho việc xử lý, khi phân phối vào bộ nhớ, thành viên _age được chia sẻ này sẽ được đặt ở cuối thành phần đối tượng. Sau đó, hai con trỏ của Giáo viên và Học sinh được truyền vào, chỉ vào một bảng. Hai con trỏ này được gọi là con trỏ bảng cơ sở ảo và hai bảng này được gọi là bảng cơ sở ảo. Phần bù được lưu trữ trong bảng cơ sở ảo. Con người bên dưới có thể được tìm thấy bằng phần bù. Có thể thấy, sau khi sử dụng tính kế thừa ảo, các vấn đề do kế thừa kim cương gây ra có thể được giải quyết.

Tại sao Giáo viên và Học sinh cần tìm tuổi của riêng mình?

Khi gán và chuyển đổi các lớp cơ sở và các lớp dẫn xuất, việc cắt lớp là bắt buộc.

int main() { Trợ lý a; Giáo viên t = a; Học sinh s = a;

Khi gán đối tượng a cho t và s, vì chúng không có _stuNum và _teaNum của nhau nên cần cắt đối tượng, nhưng vì _age được lưu ở cuối đối tượng nên chúng chỉ biết Offset của riêng mình nên bạn có thể tìm thành công _age của riêng bạn khi cắt các phần tử còn thiếu.

⏰8.Tính kế thừa và thành phần

Kế thừa là một cách tái sử dụng, nhưng đó không phải là cách duy nhất!
1. Kế thừa công khai là mối quan hệ is-a.Lớp cơ sở là một danh mục lớn và lớp dẫn xuất là một danh mục con của danh mục lớn này, nhưng về cơ bản chúng giống nhau.
2. Sự kết hợp là mối quan hệ có-có,Đó là một mối quan hệ bao hàm. Ví dụ, đối tượng a là thành viên của đối tượng b, khi đó mối quan hệ của chúng là đối tượng b chứa đối tượng a, đối tượng a là một phần của đối tượng b và đối tượng b chứa đối tượng a.
3. Tái sử dụng trong phương thức kế thừa thường được gọi là tái sử dụng hộp trắng. Trong phương thức kế thừa, các chi tiết bên trong của lớp cơ sở được hiển thị cho các lớp con, điều này phá hủy sự đóng gói của lớp cơ sở ở một mức độ nhất định. thay đổi thì các lớp Derogen thay đổi rất nhiều. Hơn nữa, cả hai có sự phụ thuộc mạnh mẽ và mức độ khớp nối cao.
4. Các tùy chọn tái sử dụng khác với kế thừa thành phần đối tượng. Thành phần đối tượng yêu cầu các đối tượng kết hợp cung cấp các định nghĩa giao diện tốt. Kiểu tái sử dụng này được gọi là tái sử dụng hộp đen và các chi tiết triển khai bên trong của đối tượng là vô hình. Khớp nối thấp.

Trong các dự án thực tế, nếu có thể sử dụng tính kế thừa và kết hợp, hãy sử dụng kết hợp. Khả năng kết hợp thấp và khả năng duy trì mã tốt. Tuy nhiên, tính kế thừa phù hợp với một số mối quan hệ và phải sử dụng tính kế thừa để đạt được tính đa hình.

30 4 0
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress