Pascal - Lớp học
Bạn đã thấy rằng các đối tượng Pascal thể hiện một số đặc điểm của mô hình hướng đối tượng. Chúng thực hiện đóng gói, ẩn dữ liệu và kế thừa, nhưng chúng cũng có những hạn chế. Ví dụ, đối tượng Pascal không tham gia vào tính đa hình. Vì vậy, các lớp được sử dụng rộng rãi để thực hiện hành vi hướng đối tượng thích hợp trong một chương trình, đặc biệt là phần mềm dựa trên GUI.
Một Lớp được định nghĩa theo cách gần giống như một Đối tượng, nhưng là một con trỏ đến một Đối tượng chứ không phải chính Đối tượng. Về mặt kỹ thuật, điều này có nghĩa là Lớp được phân bổ trên Heap của một chương trình, trong khi Đối tượng được phân bổ trên Stack. Nói cách khác, khi bạn khai báo một biến kiểu đối tượng, nó sẽ chiếm nhiều không gian trên ngăn xếp bằng kích thước của đối tượng, nhưng khi bạn khai báo một biến thuộc kiểu lớp, nó sẽ luôn có kích thước bằng một con trỏ trên ngăn xếp. Dữ liệu lớp thực tế sẽ nằm trên heap.
Định nghĩa các lớp Pascal
Một lớp được khai báo giống như một đối tượng, sử dụng khai báo kiểu. Hình thức chung của một khai báo lớp như sau:
type class-identifier = class
private
field1 : field-type;
field2 : field-type;
...
public
constructor create();
procedure proc1;
function f1(): function-type;
end;
var classvar : class-identifier;
Cần lưu ý những điểm quan trọng sau:
Định nghĩa lớp chỉ nên nằm dưới phần khai báo kiểu của chương trình.
Một lớp được định nghĩa bằng cách sử dụng class từ khóa.
Trường là các mục dữ liệu tồn tại trong mỗi thể hiện của lớp.
Các phương thức được khai báo trong định nghĩa của một lớp.
Có một hàm tạo được xác định trước được gọi là Createtrong lớp Root. Mọi lớp trừu tượng và mọi lớp cụ thể đều là con của Root, vì vậy tất cả các lớp đều có ít nhất một hàm tạo.
Có một trình hủy xác định trước được gọi là Destroytrong lớp Root. Mọi lớp trừu tượng và mọi lớp cụ thể đều là hậu duệ của Root, vì vậy, tất cả các lớp đều có ít nhất một hàm hủy.
Chúng ta hãy định nghĩa một lớp Rectangle có hai thành viên dữ liệu kiểu số nguyên - chiều dài và chiều rộng và một số hàm thành viên để thao tác với các thành viên dữ liệu này và một thủ tục để vẽ hình chữ nhật.
type
Rectangle = class
private
length, width: integer;
public
constructor create(l, w: integer);
procedure setlength(l: integer);
function getlength(): integer;
procedure setwidth(w: integer);
function getwidth(): integer;
procedure draw;
end;
Chúng ta hãy viết một chương trình hoàn chỉnh để tạo một thể hiện của một lớp hình chữ nhật và vẽ hình chữ nhật. Đây cũng là ví dụ chúng ta đã sử dụng khi thảo luận về các đối tượng Pascal. Bạn sẽ thấy cả hai chương trình gần như giống nhau, với các ngoại lệ sau:
Bạn sẽ cần bao gồm chỉ thị {$ mode objfpc} để sử dụng các lớp.
Bạn sẽ cần bao gồm chỉ thị {$ m +} để sử dụng các hàm tạo.
Khởi tạo lớp khác với khởi tạo đối tượng. Chỉ khai báo biến không tạo khoảng trống cho instance, bạn sẽ sử dụng hàm tạo create để cấp phát bộ nhớ.
Đây là ví dụ hoàn chỉnh -
{$mode objfpc} // directive to be used for defining classes
{$m+} // directive to be used for using constructor
program exClass;
type
Rectangle = class
private
length, width: integer;
public
constructor create(l, w: integer);
procedure setlength(l: integer);
function getlength(): integer;
procedure setwidth(w: integer);
function getwidth(): integer;
procedure draw;
end;
var
r1: Rectangle;
constructor Rectangle.create(l, w: integer);
begin
length := l;
width := w;
end;
procedure Rectangle.setlength(l: integer);
begin
length := l;
end;
procedure Rectangle.setwidth(w: integer);
begin
width :=w;
end;
function Rectangle.getlength(): integer;
begin
getlength := length;
end;
function Rectangle.getwidth(): integer;
begin
getwidth := width;
end;
procedure Rectangle.draw;
var
i, j: integer;
begin
for i:= 1 to length do
begin
for j:= 1 to width do
write(' * ');
writeln;
end;
end;
begin
r1:= Rectangle.create(3, 7);
writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
r1.draw;
r1.setlength(4);
r1.setwidth(6);
writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
r1.draw;
end.
Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:
Draw Rectangle: 3 by 7
* * * * * * *
* * * * * * *
* * * * * * *
Draw Rectangle: 4 by 6
* * * * * *
* * * * * *
* * * * * *
* * * * * *
Hiển thị của các thành viên trong lớp
Khả năng hiển thị cho biết khả năng truy cập của các thành viên trong lớp. Các thành viên lớp Pascal có năm kiểu hiển thị:
Sr.No | Khả năng hiển thị & Khả năng tiếp cận |
---|---|
1 | Public Những thành viên này luôn có thể truy cập. |
2 | Private Các thành viên này chỉ có thể được truy cập trong mô-đun hoặc đơn vị có chứa định nghĩa lớp. Chúng có thể được truy cập từ bên trong các phương thức của lớp hoặc từ bên ngoài chúng. |
3 | Strict Private Các thành viên này chỉ có thể được truy cập từ các phương thức của chính lớp đó. Các lớp khác hoặc các lớp con trong cùng một đơn vị không thể truy cập chúng. |
4 | Protected Điều này cũng giống như private, ngoại trừ, các thành viên này có thể truy cập vào các loại con, ngay cả khi chúng được triển khai trong các mô-đun khác. |
5 | Published Điều này tương tự như Public, nhưng trình biên dịch tạo ra thông tin kiểu cần thiết để tự động truyền trực tuyến các lớp này nếu trình biên dịch ở trạng thái {$ M +}. Các trường được xác định trong một phần đã xuất bản phải thuộc loại lớp. |
Các hàm tạo và hủy cho các lớp Pascal
Hàm tạo là các phương thức đặc biệt, được gọi tự động bất cứ khi nào một đối tượng được tạo. Vì vậy, chúng tôi tận dụng tối đa hành vi này bằng cách khởi tạo nhiều thứ thông qua các hàm khởi tạo.
Pascal cung cấp một hàm đặc biệt gọi là create () để định nghĩa một hàm tạo. Bạn có thể truyền bao nhiêu đối số tùy thích vào hàm khởi tạo.
Ví dụ sau sẽ tạo một phương thức khởi tạo cho một lớp có tên Books và nó sẽ khởi tạo giá và tiêu đề cho sách tại thời điểm tạo đối tượng.
program classExample;
{$MODE OBJFPC} //directive to be used for creating classes
{$M+} //directive that allows class constructors and destructors
type
Books = Class
private
title : String;
price: real;
public
constructor Create(t : String; p: real); //default constructor
procedure setTitle(t : String); //sets title for a book
function getTitle() : String; //retrieves title
procedure setPrice(p : real); //sets price for a book
function getPrice() : real; //retrieves price
procedure Display(); // display details of a book
end;
var
physics, chemistry, maths: Books;
//default constructor
constructor Books.Create(t : String; p: real);
begin
title := t;
price := p;
end;
procedure Books.setTitle(t : String); //sets title for a book
begin
title := t;
end;
function Books.getTitle() : String; //retrieves title
begin
getTitle := title;
end;
procedure Books.setPrice(p : real); //sets price for a book
begin
price := p;
end;
function Books.getPrice() : real; //retrieves price
begin
getPrice:= price;
end;
procedure Books.Display();
begin
writeln('Title: ', title);
writeln('Price: ', price:5:2);
end;
begin
physics := Books.Create('Physics for High School', 10);
chemistry := Books.Create('Advanced Chemistry', 15);
maths := Books.Create('Algebra', 7);
physics.Display;
chemistry.Display;
maths.Display;
end.
Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:
Title: Physics for High School
Price: 10
Title: Advanced Chemistry
Price: 15
Title: Algebra
Price: 7
Giống như hàm tạo ngầm định có tên là create, cũng có một phương thức hủy ngầm định bằng cách sử dụng mà bạn có thể giải phóng tất cả các tài nguyên được sử dụng trong lớp.
Di sản
Các định nghĩa lớp Pascal có thể kế thừa tùy ý từ một định nghĩa lớp cha. Cú pháp như sau:
type
childClas-identifier = class(baseClass-identifier)
< members >
end;
Ví dụ sau cung cấp một lớp tiểu thuyết, lớp này kế thừa lớp Sách và thêm nhiều chức năng hơn dựa trên yêu cầu.
program inheritanceExample;
{$MODE OBJFPC} //directive to be used for creating classes
{$M+} //directive that allows class constructors and destructors
type
Books = Class
protected
title : String;
price: real;
public
constructor Create(t : String; p: real); //default constructor
procedure setTitle(t : String); //sets title for a book
function getTitle() : String; //retrieves title
procedure setPrice(p : real); //sets price for a book
function getPrice() : real; //retrieves price
procedure Display(); virtual; // display details of a book
end;
(* Creating a derived class *)
type
Novels = Class(Books)
private
author: String;
public
constructor Create(t: String); overload;
constructor Create(a: String; t: String; p: real); overload;
procedure setAuthor(a: String); // sets author for a book
function getAuthor(): String; // retrieves author name
procedure Display(); override;
end;
var
n1, n2: Novels;
//default constructor
constructor Books.Create(t : String; p: real);
begin
title := t;
price := p;
end;
procedure Books.setTitle(t : String); //sets title for a book
begin
title := t;
end;
function Books.getTitle() : String; //retrieves title
begin
getTitle := title;
end;
procedure Books.setPrice(p : real); //sets price for a book
begin
price := p;
end;
function Books.getPrice() : real; //retrieves price
begin
getPrice:= price;
end;
procedure Books.Display();
begin
writeln('Title: ', title);
writeln('Price: ', price);
end;
(* Now the derived class methods *)
constructor Novels.Create(t: String);
begin
inherited Create(t, 0.0);
author:= ' ';
end;
constructor Novels.Create(a: String; t: String; p: real);
begin
inherited Create(t, p);
author:= a;
end;
procedure Novels.setAuthor(a : String); //sets author for a book
begin
author := a;
end;
function Novels.getAuthor() : String; //retrieves author
begin
getAuthor := author;
end;
procedure Novels.Display();
begin
writeln('Title: ', title);
writeln('Price: ', price:5:2);
writeln('Author: ', author);
end;
begin
n1 := Novels.Create('Gone with the Wind');
n2 := Novels.Create('Ayn Rand','Atlas Shrugged', 467.75);
n1.setAuthor('Margaret Mitchell');
n1.setPrice(375.99);
n1.Display;
n2.Display;
end.
Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:
Title: Gone with the Wind
Price: 375.99
Author: Margaret Mitchell
Title: Atlas Shrugged
Price: 467.75
Author: Ayn Rand
Cần lưu ý những điểm quan trọng sau:
Các thành viên của lớp Sách có protected hiển thị.
Lớp Novels có hai hàm tạo, vì vậy overload toán tử được sử dụng để nạp chồng hàm.
Thủ tục Books.Display đã được khai báo virtual, để cùng một phương thức từ lớp Novels có thể override nó.
Hàm tạo Novels.Create gọi hàm tạo lớp cơ sở bằng cách sử dụng inherited từ khóa.
Giao diện
Các giao diện được định nghĩa để cung cấp một tên hàm chung cho người triển khai. Những người triển khai khác nhau có thể triển khai các giao diện đó theo yêu cầu của họ. Bạn có thể nói, giao diện là bộ xương, được thực hiện bởi các nhà phát triển. Sau đây là một ví dụ về giao diện -
type
Mail = Interface
Procedure SendMail;
Procedure GetMail;
end;
Report = Class(TInterfacedObject, Mail)
Procedure SendMail;
Procedure GetMail;
end;
Xin lưu ý rằng, khi một lớp triển khai một giao diện, nó phải triển khai tất cả các phương thức của giao diện. Nếu một phương thức của một giao diện không được triển khai, thì trình biên dịch sẽ báo lỗi.
Các lớp trừu tượng
Một lớp trừu tượng là một lớp không thể được khởi tạo, chỉ được kế thừa. Một lớp trừu tượng được chỉ định bằng cách bao gồm từ trừu tượng ký hiệu trong định nghĩa lớp, như thế này:
type
Shape = ABSTRACT CLASS (Root)
Procedure draw; ABSTRACT;
...
end;
Khi kế thừa từ một lớp trừu tượng, tất cả các phương thức được đánh dấu là trừu tượng trong khai báo lớp của cha phải được định nghĩa bởi con; ngoài ra, các phương thức này phải được xác định với cùng một khả năng hiển thị.
Từ khóa tĩnh
Khai báo các thành viên hoặc phương thức của lớp dưới dạng tĩnh làm cho chúng có thể truy cập được mà không cần khởi tạo lớp. Một thành viên được khai báo là tĩnh không thể được truy cập bằng một đối tượng lớp khởi tạo (mặc dù một phương thức tĩnh có thể). Ví dụ sau minh họa khái niệm -
program StaticExample;
{$mode objfpc}
{$static on}
type
myclass=class
num : integer;static;
end;
var
n1, n2 : myclass;
begin
n1:= myclass.create;
n2:= myclass.create;
n1.num := 12;
writeln(n2.num);
n2.num := 31;
writeln(n1.num);
writeln(myclass.num);
myclass.num := myclass.num + 20;
writeln(n1.num);
writeln(n2.num);
end.
Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:
12
31
31
51
51
Bạn phải sử dụng chỉ thị {$ static on} để sử dụng các thành viên tĩnh.