tek4

Hàm ảo: đa hình thời gian chạy

by - September. 26, 2021
Kiến thức
Học
<p>H&agrave;m ảo l&agrave; một trong c&aacute;c kh&aacute;i niệm cơ bản v&agrave; quan trọng trong C++. Trong b&agrave;i n&agrave;y, ch&uacute;ng ta sẽ t&igrave;m hiểu về h&agrave;m ảo: đa h&igrave;nh thời gian chạy trong C ++ v&agrave; c&aacute;ch sử dụng n&oacute; c&ugrave;ng với c&aacute;c v&iacute; dụ dẫn chứng.</p> <p>Một h&agrave;m ảo l&agrave; một h&agrave;m th&agrave;nh vi&ecirc;n trong lớp cơ sở m&agrave; ch&uacute;ng ta muốn định nghĩa lại trong c&aacute;c lớp dẫn xuất. Về cơ bản, một h&agrave;m ảo được sử dụng trong lớp cơ sở để đảm bảo rằng h&agrave;m đ&oacute; được ghi đ&egrave;. Điều n&agrave;y đặc biệt ph&ugrave; hợp với c&aacute;c trường hợp con trỏ của lớp cơ sở trỏ đến một đối tượng của lớp dẫn xuất.</p> <p>V&iacute; dụ:</p> <pre class="language-cpp"><code>class Lop_co_so { public: void xuat() { // Đoạn m&atilde; } }; class Lop_dan_xuat : public Lop_co_so { public: void xuat() { // Đoạn m&atilde; } };</code></pre> <p>Nếu ch&uacute;ng ta tạo một con trỏ kiểu lớp cơ sở để trỏ đến một đối tượng của lớp dẫn xuất v&agrave; gọi h&agrave;m xuat(), n&oacute; sẽ gọi h&agrave;m xuat() của lớp cơ sở. N&oacute;i c&aacute;ch kh&aacute;c, h&agrave;m th&agrave;nh vi&ecirc;n của lớp cơ sở kh&ocirc;ng bị ghi đ&egrave;.</p> <pre class="language-cpp"><code>int main() { Lop_dan_xuat a; Lop_co_so* b = &amp;a; B -&gt; xuat(); return 0; }</code></pre> <p>Để tr&aacute;nh điều n&agrave;y, ch&uacute;ng ta khai b&aacute;o h&agrave;m xuat() của lớp cơ sở l&agrave; Virtual bằng c&aacute;ch sử dụng từ kho&aacute; virtual.</p> <pre class="language-cpp"><code>class lớp_cơ_sở { public: virtual void xuat() { // Đoạn m&atilde; } };</code></pre> <p>C&aacute;c h&agrave;m ảo l&agrave; một phần kh&ocirc;ng thể thiếu của t&iacute;nh đa h&igrave;nh trong C ++.</p> <p>V&iacute; dụ: H&agrave;m ảo.</p> <pre class="language-cpp"><code>#include &lt;iostream&gt; using namespace std; class lop_co_so { public: virtual void xuat() { cout &lt;&lt; "Day la ham co so" &lt;&lt; endl; } }; class lop_dan_xuat : public lop_co_so { public: void xuat() { cout &lt;&lt; "Day la ham dan xuat" &lt;&lt; endl; } }; int main() { lop_dan_xuat a; lop_co_so* b = &amp;a; b-&gt;xuat(); return 0; }</code></pre> <p>Kết quả:</p> <pre class="language-markup"><code>Day la ham dan xuat</code></pre> <p>Ở đ&acirc;y, ch&uacute;ng ta đ&atilde; khai b&aacute;o h&agrave;m xuat () của lớp cơ sở l&agrave; ảo. V&igrave; vậy, h&agrave;m n&agrave;y bị ghi đ&egrave; ngay cả khi ch&uacute;ng ta sử dụng con trỏ kiểu lớp cơ sở trỏ đến đối tượng được dẫn xuất a.</p> <h2>Ghi đ&egrave; m&atilde; định danh C ++</h2> <p>C ++ 11 đ&atilde; cung cấp cho ta một ghi đ&egrave; m&atilde; định danh rất hữu &iacute;ch để tr&aacute;nh xảy ra lỗi khi sử dụng c&aacute;c h&agrave;m ảo. Định danh n&agrave;y chỉ định c&aacute;c h&agrave;m th&agrave;nh vi&ecirc;n của c&aacute;c lớp dẫn xuất ghi đ&egrave; h&agrave;m th&agrave;nh vi&ecirc;n của lớp cơ sở.</p> <p>V&iacute; dụ:</p> <pre class="language-cpp"><code>class lop_co_so { public: virtual void xuat() { // Đoạn m&atilde; } }; class lop_dan_xuat : public lop_co_so { public: void xuat() override { // Đoạn m&atilde; } };</code></pre> <p>Nếu ch&uacute;ng ta sử dụng một nguy&ecirc;n mẫu h&agrave;m trong lớp dẫn xuất v&agrave; x&aacute;c định h&agrave;m đ&oacute; b&ecirc;n ngo&agrave;i lớp, th&igrave; ch&uacute;ng ta sử dụng đoạn m&atilde; sau:</p> <pre class="language-cpp"><code>class lop_dan_xuat : public lop_co_so { public: void xuat() override; }; void lop_dan_xuat::xuat() { // Đoạn m&atilde; }</code></pre> <h2>C&ocirc;ng dụng của Override</h2> <p>Khi sử dụng c&aacute;c h&agrave;m ảo. c&oacute; thể sẽ g&acirc;y ra lỗi trong khi khai b&aacute;o c&aacute;c h&agrave;m th&agrave;nh vi&ecirc;n của c&aacute;c lớp dẫn xuất. Việc sử dụng m&atilde; định danh Override sẽ nhắc tr&igrave;nh bi&ecirc;n dịch hiển thị th&ocirc;ng b&aacute;o lỗi khi những lỗi n&agrave;y được thực hiện. Nếu kh&ocirc;ng, chương tr&igrave;nh sẽ bi&ecirc;n dịch b&igrave;nh thường nhưng h&agrave;m ảo kh&ocirc;ng bị ghi đ&egrave;.</p> <p>Một số lỗi c&oacute; thể xảy ra:</p> <ul> <li>C&aacute;c h&agrave;m c&oacute; t&ecirc;n kh&ocirc;ng ch&iacute;nh x&aacute;c: V&iacute; dụ, nếu h&agrave;m ảo trong lớp cơ sở được đặt t&ecirc;n l&agrave; xuat (), nhưng ch&uacute;ng ta lại v&ocirc; t&igrave;nh đặt t&ecirc;n cho h&agrave;m ghi đ&egrave; trong lớp dẫn xuất l&agrave; xat ().</li> <li>C&aacute;c h&agrave;m c&oacute; c&aacute;c kiểu trả về kh&aacute;c nhau: Nếu h&agrave;m ảo l&agrave; kiểu void nhưng h&agrave;m trong lớp dẫn xuất l&agrave; kiểu int.</li> <li>C&aacute;c h&agrave;m c&oacute; tham số kh&aacute;c nhau: Nếu c&aacute;c tham số của h&agrave;m ảo v&agrave; c&aacute;c h&agrave;m trong c&aacute;c lớp dẫn xuất kh&ocirc;ng giống nhau.</li> <li>Kh&ocirc;ng c&oacute; h&agrave;m ảo n&agrave;o được khai b&aacute;o trong lớp cơ sở.</li> </ul> <h2>Sử dụng c&aacute;c h&agrave;m ảo</h2> <p>Giả sử ch&uacute;ng ta c&oacute; một lớp cơ sở sinh_vien v&agrave; c&aacute;c lớp dẫn xuất sinh_vien_luat v&agrave; sinh_vien_y. Giả sử mỗi lớp c&oacute; một th&agrave;nh vi&ecirc;n dữ liệu c&oacute; t&ecirc;n l&agrave; ID_sv. Giả sử c&aacute;c biến n&agrave;y được khởi tạo th&ocirc;ng qua c&aacute;c h&agrave;m tạo tương ứng.</p> <pre class="language-cpp"><code>class sinh_vien { private: int ID_sv; public: sinh_vien(): ID(1) {} }; class sinh_vien_luat : public sinh_vien { private: int ID_sv; public: sinh_vien(): ID_sv(3) {} }; class sinh_vien_y : public sinh_vien { private: int ID_sv; public: sinh_vien(): ID_sv(4) {} };</code></pre> <p>B&acirc;y giờ, ch&uacute;ng ta h&atilde;y giả sử rằng chương tr&igrave;nh của ch&uacute;ng ta y&ecirc;u cầu ch&uacute;ng ta tạo hai h&agrave;m c&ocirc;ng khai cho mỗi lớp:</p> <ul> <li>getID () để trả về gi&aacute; trị của ID_sv</li> <li>xuat () để in ra gi&aacute; trị của ID_sv</li> </ul> <p>Ch&uacute;ng ta c&oacute; thể tạo cả hai h&agrave;m n&agrave;y trong mỗi lớp ri&ecirc;ng biệt v&agrave; ghi đ&egrave; ch&uacute;ng, điều n&agrave;y sẽ rất l&acirc;u v&agrave; l&atilde;ng ph&iacute; thời gian.</p> <p>Hoặc ch&uacute;ng ta c&oacute; thể l&agrave;m cho getID () ảo trong lớp sinh_vien, sau đ&oacute; tạo một h&agrave;m xuat () ri&ecirc;ng lẻ chấp nhận một con trỏ kiểu sinh_vien l&agrave;m tham số của n&oacute;. Sau đ&oacute;, ch&uacute;ng ta c&oacute; thể sử dụng h&agrave;m n&agrave;y để ghi đ&egrave; h&agrave;m ảo.</p> <pre class="language-cpp"><code>class sinh_vien { &hellip; public: &hellip; virtual int getID { &hellip; } }; &hellip; void xuat(sinh_vien* sv) { cout &lt;&lt; "ID la: " &lt;&lt; sv-&gt;getID() &lt;&lt; endl; }</code></pre> <p>Điều n&agrave;y sẽ l&agrave;m cho đoạn m&atilde; ngắn hơn, r&otilde; r&agrave;ng hơn v&agrave; &iacute;t phải lặp lại hơn.</p> <p>V&iacute; dụ:</p> <pre class="language-cpp"><code>#include &lt;iostream&gt; #include &lt;string&gt; using namespace std; class sinh_vien { private: int ID_sv; string truong_hoc; public: sinh_vien(){ ID_sv = 1; truong_hoc = "tat ca"; } virtual int getID() { return ID_sv; } virtual string getTruong(){ return truong_hoc; } }; class sinh_vien_luat : public sinh_vien { private: int ID_sv; string truong_hoc; public: sinh_vien_luat(){ ID_sv = 2; truong_hoc = "truong luat"; } int getID() { return ID_sv; } string getTruong(){ return truong_hoc; } }; class sinh_vien_y : public sinh_vien { private: int ID_sv; string truong_hoc; public: sinh_vien_y(){ ID_sv = 3; truong_hoc = "truong y"; } int getID() { return ID_sv; } string getTruong(){ return truong_hoc; } }; void xuat(sinh_vien* sv) { cout &lt;&lt; "Ma so sinh vien "&lt;&lt; sv -&gt; getTruong()&lt;&lt;" la: " &lt;&lt; sv-&gt;getID() &lt;&lt; endl; } int main() { sinh_vien* sv = new sinh_vien(); sinh_vien* sv_luat = new sinh_vien_luat(); sinh_vien* sv_y = new sinh_vien_y(); xuat(sv); xuat(sv_luat); xuat(sv_y); return 0; }</code></pre> <p>Kết quả:</p> <pre class="language-markup"><code>Ma so sinh vien tat ca la: 1 Ma so sinh vien truong luat la: 2 Ma so sinh vien truong y la: 3</code></pre> <p>Ở đ&acirc;y, ch&uacute;ng ta đ&atilde; sử dụng h&agrave;m ảo getID (), getTruong() v&agrave; một con trỏ sv để tr&aacute;nh lặp lại h&agrave;m xuat () trong c&aacute;c lớp. Trong h&agrave;m main (), ch&uacute;ng ta đ&atilde; tạo 3 con trỏ để tạo c&aacute;c đối tượng thuộc c&aacute;c lớp sinh_vien, sinh_vien_luat, sinh_vien_y.</p> <ul> <li>sinh_vien* sv = new sinh_vien();</li> <li>sinh_vien* sv_luat = new sinh_vien_luat();</li> <li>sinh_vien* sv_y = new sinh_vien_y();</li> </ul> <p>Sau đ&oacute;, ch&uacute;ng t&ocirc;i gọi h&agrave;m xuat () bằng c&aacute;ch sử dụng c&aacute;c con trỏ sau:</p> <ul> <li>Khi xuat (sv) được gọi, con trỏ trỏ đến một đối tượng sinh_vien. V&igrave; vậy, h&agrave;m ảo trong lớp sinh_vien được thực thi b&ecirc;n trong xuat ().</li> <li>Khi print (sv_luat) được gọi, con trỏ trỏ đến một đối tượng sinh_vien_luat. V&igrave; vậy, h&agrave;m ảo bị ghi đ&egrave; v&agrave; h&agrave;m sinh_vien_luat được thực thi b&ecirc;n trong xuat ().</li> <li>Khi print (sv_y) được gọi, con trỏ trỏ đến một đối tượng sinh_vien_y. V&igrave; vậy, h&agrave;m ảo bị ghi đ&egrave; v&agrave; h&agrave;m sinh_vien_y được thực thi b&ecirc;n trong xuat ().</li> </ul> <p>Tr&ecirc;n đ&acirc;y l&agrave; kh&aacute;i niệm v&agrave; v&iacute; dụ cơ bản về H&agrave;m ảo: đa h&igrave;nh thời gian chạy trong C++. Hy vọng mọi người c&oacute; thể &aacute;p dụng v&agrave;o trong chương tr&igrave;nh của m&igrave;nh. Mọi người h&atilde;y tiếp tục theo d&otilde;i c&aacute;c b&agrave;i tiếp theo v&agrave; cập nhật c&aacute;c b&agrave;i mới nhất tr&ecirc;n <a href="http://tek4.vn">tek4</a> nh&eacute;!</p>