[Lập trình C/C++] Thư viện chuẩn STL ( Standard Template Library ) trong C++
Bài viết này trình bày về kĩ thuật sử dụng ” bộ nhớ động ” trong lập trình, tức là thành phần dữ liệu có kích thước thay đổi trong quá trình đang chạy ( xử lí ). Đây là kĩ thuật lập trình vô cùng quan trọng , không thể thiếu đối với các lập trình viên chuyên nghiệp.
Bài viết này trình bày về kĩ thuật sử dụng ” bộ nhớ động ” trong lập trình, tức là thành phần dữ liệu có kích thước thay đổi trong quá trình đang chạy ( xử lí ). Đây là kĩ thuật lập trình vô cùng quan trọng , không thể thiếu đối với các lập trình viên chuyên nghiệp.
I. Giới thiệu
– Thư viện chuẩn STL ( Standard Template Library ) của C++ cung cấp sẵn kiểu vector<T> cho phép lập trình viên khai báo và sử dụng các mảng có kích thước thay đổi một cách rất dễ dàng.
– Kiểu vector<T> được cài đặt sẵn cho C++ chuẩn để sử dụng mảng gồm các phần tử có kiểu là T ( sẽ được thay thế bởi một kiểu cụ thể(int , float , double, struct ) khi dùng ) và có khả năng có kích thước thay đổi. Trước hết chúng ta xét một ví dụ đơn giản với mảng động nhờ khai thác kiểu vector<T>.
– Thư viện chuẩn STL ( Standard Template Library ) của C++ cung cấp sẵn kiểu vector<T> cho phép lập trình viên khai báo và sử dụng các mảng có kích thước thay đổi một cách rất dễ dàng.
– Kiểu vector<T> được cài đặt sẵn cho C++ chuẩn để sử dụng mảng gồm các phần tử có kiểu là T ( sẽ được thay thế bởi một kiểu cụ thể(int , float , double, struct ) khi dùng ) và có khả năng có kích thước thay đổi. Trước hết chúng ta xét một ví dụ đơn giản với mảng động nhờ khai thác kiểu vector<T>.
– Kiểu vector<T> được cài đặt sẵn cho C++ chuẩn để sử dụng mảng gồm các phần tử có kiểu là T ( sẽ được thay thế bởi một kiểu cụ thể(int , float , double, struct ) khi dùng ) và có khả năng có kích thước thay đổi. Trước hết chúng ta xét một ví dụ đơn giản với mảng động nhờ khai thác kiểu vector<T>.
II. Ưu điểm
– Có đầy đủ các tính chất mà mảng bình thường có.
– Có thêm các đặc tính riêng của vector( khắc phục các nhược điểm mà mảng bình thường mắc phải ).
VD: Mảng bình thường ( tĩnh hoặc động ) thì chúng ta cần khai báo trước số lượng phần tử mảng , còn mảng vector thì không cần khái báo trước số lượng phần tử mảng, vì vector có thể xác định trước hoặc không cần xác định trước nhờ vào cơ chế tự động thêm phần tử vào cuối mảng ( push_back() ).
==> Cơ chế này tương tự như cơ chế của List, Queue.
– Tự động giải phóng vùng nhớ khi kết thúc chương trình.
– Các thao tac như: Thêm, Xóa, Tìm kiếm…., bên mảng thì chúng ta cần phải đi thiết lập .Còn bên vector thì đã có các hàm hỗ trợ các công việc trên.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//
//
// Hàm nhập phần tử vào vector
void Array_Input(vector<int> &a, int n)
{
for(int i = 0; i < n; i++)
{
cout << “\nNhap phan tu a[ “ << i << ” ] = “;
cin >> a[i];
}
}
// Hàm xuất phần tử
void Array_Output(vector<int> &a)
{
int size = a.size(); // Trả về số lượng phần tử hiện tại có trong mảng vector
for(int i = 0; i < size; i++)
{
cout << a[i] << ” “;
}
}
– Có đầy đủ các tính chất mà mảng bình thường có.
– Có thêm các đặc tính riêng của vector( khắc phục các nhược điểm mà mảng bình thường mắc phải ).
– Có thêm các đặc tính riêng của vector( khắc phục các nhược điểm mà mảng bình thường mắc phải ).
VD: Mảng bình thường ( tĩnh hoặc động ) thì chúng ta cần khai báo trước số lượng phần tử mảng , còn mảng vector thì không cần khái báo trước số lượng phần tử mảng, vì vector có thể xác định trước hoặc không cần xác định trước nhờ vào cơ chế tự động thêm phần tử vào cuối mảng ( push_back() ).
==> Cơ chế này tương tự như cơ chế của List, Queue.
– Tự động giải phóng vùng nhớ khi kết thúc chương trình.
– Các thao tac như: Thêm, Xóa, Tìm kiếm…., bên mảng thì chúng ta cần phải đi thiết lập .Còn bên vector thì đã có các hàm hỗ trợ các công việc trên.
==> Cơ chế này tương tự như cơ chế của List, Queue.
– Tự động giải phóng vùng nhớ khi kết thúc chương trình.
– Các thao tac như: Thêm, Xóa, Tìm kiếm…., bên mảng thì chúng ta cần phải đi thiết lập .Còn bên vector thì đã có các hàm hỗ trợ các công việc trên.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//
//
// Hàm nhập phần tử vào vector
void Array_Input(vector<int> &a, int n)
{
for(int i = 0; i < n; i++)
{
cout << “\nNhap phan tu a[ “ << i << ” ] = “;
cin >> a[i];
}
}
// Hàm xuất phần tử
void Array_Output(vector<int> &a)
{
int size = a.size(); // Trả về số lượng phần tử hiện tại có trong mảng vector
for(int i = 0; i < size; i++)
{
cout << a[i] << ” “;
}
}
|
III. Khuyết điểm
– Tận dụng không gian bộ nhớ tốn hơn mảng bình thường (do cơ chế container buộc phải tạo ra những vùng nhớ riêng của nó để quản lý chặt chẽ hơn).
– Tận dụng không gian bộ nhớ tốn hơn mảng bình thường (do cơ chế container buộc phải tạo ra những vùng nhớ riêng của nó để quản lý chặt chẽ hơn).
IV. Khởi tạo vector<T>
– Khai báo thư viện hỗ trợ: #include<vector> (Lưu ý: cần có using namespace std; )
VD:
+ vector<int> arr; // Mảng vector arr chứa các phần tử là kiểu số nguyên
+ vector<float> arr; // Mảng vector arr chứa các phần tử là kiểu số thực
+ vector<SinhVien> arr; // Mảng vector arr chứa các phần tử là kiểu sinh viên
– Khai báo thư viện hỗ trợ: #include<vector> (Lưu ý: cần có using namespace std; )
VD:
VD:
+ vector<int> arr; // Mảng vector arr chứa các phần tử là kiểu số nguyên
+ vector<float> arr; // Mảng vector arr chứa các phần tử là kiểu số thực
+ vector<SinhVien> arr; // Mảng vector arr chứa các phần tử là kiểu sinh viên
V. Một số hàm hỗ trợ trên vector<T>
1. size() : Trả về số lượng phần tử hiện có của mảng
– Hàm này có 2 dạng:
* resize(<số lượng>):
+ Cấp phát mảng với số lượng phần tử cụ thể cho trước.
+ Mặc định khi cấp phát với resize là các phần tử đều mang giá trị mặc định ban đầu là 0 (giống như cấp phát với cơ chế calloc bên mảng động con trỏ).
+ Nếu mảng chưa từng được tạo thì sẽ tạo mảng với số lượng cụ thể đó.
+ Nếu mảng đã được tạo mà tiếp tục resize thì kích thước của mảng sẽ co lại hay dãn ra so với mảng ban đầu.
+ Nếu là co lại thì 1 số phần tử trong mảng ban đầu sẽ bị mất, nếu là dãn ra thì các phần tử lúc đầu vẫn tiếp tục tồn tại (giống cơ chế cấp phát lại: realloc bên mảng động con trỏ).
* resize(<số lượng>, <giá trị mặc định>):
+ Cấp phát mảng với số lượng phần tử cụ thể cho trước với giá trị mặc định của các phần tử cho trước.
+ Mảng được tạo mới nếu chưa có, nếu đã có rồi mảng sẽ co lại hoặc dãn ra so với mảng ban đầu.
2. push_back(x): Thêm phần tử x vào cuối mảng vector, và mảng tự động giãn ra thêm 1 phần tử.
3. pop_back(): Xóa phần tử cuối cùng trong mảng, và mảng tự động co lại , giảm bớt đi 1 phần tử.
4. front(): Trả về phần tử đầu tiên của mảng.
5. back(): Trả về phần tử cuối cùng của mảng.
6. clear(): Xóa tất cả các phần tử hiện có trong mảng vector, sau khi thực hiện hàm này thì vector trở thành mảng rỗng.
7. erase(): Hàm xóa phần tử trong vector
Có 3 dạng:
Dạng 1: erase(<Tên vector>.begin() + <Số nguyên x>): Xóa phần tử tại vị trí số nguyên x( mảng bắt đầu từ vị trí 0 ).
Dạng 2: erase(<Tên vector>.begin() + < Số nguyên x>, < Tên vector >.begin() + < Số nguyên y >): Xóa các phần tử từ vị trí x đến vị trí y – 1.
Dạng 3: erase(0): Xóa tất cả các phần tử trong vector.
* Lưu ý cả 2 trường hợp trên: Nếu vị trí truyền vào không hợp lệ thì chương trình sẽ bị lỗi.
8. insert(): Hàm thêm phần tử vào vector
– Hàm này có 4 dạng:
+ Dạng 1: insert(<Tên vector>.begin() + <số nguyên x>, <giá trị cần chèn>): Chèn giá trị vào vị trí x trong mảng.
+ Dạng 2: insert(<Tên vector>.begin() + <số nguyên x>, <số lần chèn y>, <giá trị cần chèn>): Chèn y lần giá trị vào vị trí x trong mảng.
+ Dạng 3: insert(<Tên vector>.begin() + <số nguyên x>, <Tên vector a>.begin() + <số nguyên y>, <Tên vector a>.begin() + <số nguyên z>):
—> Lấy các phần tử từ vector a bắt đầu lấy từ vị trí y cho đến vị trí z – 1 và chèn vào vị trí x của vector hiện tại.
+ Dạng 4: insert(<Tên vector>.begin() + <số nguyên x>, <Tên mảng a> + <số nguyên y>, <Tên mảng a> + <số nguyên z>):
—-> Lấy các phần tử lấy từ mảng a bắt đầu lấy từ vị trí y đến vị trí z – 1 và chèn vào vị trí x của vector hiện tại.
1. size() : Trả về số lượng phần tử hiện có của mảng
– Hàm này có 2 dạng:
* resize(<số lượng>):
* resize(<số lượng>):
+ Cấp phát mảng với số lượng phần tử cụ thể cho trước.
+ Mặc định khi cấp phát với resize là các phần tử đều mang giá trị mặc định ban đầu là 0 (giống như cấp phát với cơ chế calloc bên mảng động con trỏ).
+ Nếu mảng chưa từng được tạo thì sẽ tạo mảng với số lượng cụ thể đó.
+ Nếu mảng đã được tạo mà tiếp tục resize thì kích thước của mảng sẽ co lại hay dãn ra so với mảng ban đầu.
+ Nếu là co lại thì 1 số phần tử trong mảng ban đầu sẽ bị mất, nếu là dãn ra thì các phần tử lúc đầu vẫn tiếp tục tồn tại (giống cơ chế cấp phát lại: realloc bên mảng động con trỏ).
+ Nếu mảng chưa từng được tạo thì sẽ tạo mảng với số lượng cụ thể đó.
+ Nếu mảng đã được tạo mà tiếp tục resize thì kích thước của mảng sẽ co lại hay dãn ra so với mảng ban đầu.
+ Nếu là co lại thì 1 số phần tử trong mảng ban đầu sẽ bị mất, nếu là dãn ra thì các phần tử lúc đầu vẫn tiếp tục tồn tại (giống cơ chế cấp phát lại: realloc bên mảng động con trỏ).
* resize(<số lượng>, <giá trị mặc định>):
+ Cấp phát mảng với số lượng phần tử cụ thể cho trước với giá trị mặc định của các phần tử cho trước.
+ Mảng được tạo mới nếu chưa có, nếu đã có rồi mảng sẽ co lại hoặc dãn ra so với mảng ban đầu.
2. push_back(x): Thêm phần tử x vào cuối mảng vector, và mảng tự động giãn ra thêm 1 phần tử.
3. pop_back(): Xóa phần tử cuối cùng trong mảng, và mảng tự động co lại , giảm bớt đi 1 phần tử.
4. front(): Trả về phần tử đầu tiên của mảng.
5. back(): Trả về phần tử cuối cùng của mảng.
6. clear(): Xóa tất cả các phần tử hiện có trong mảng vector, sau khi thực hiện hàm này thì vector trở thành mảng rỗng.
7. erase(): Hàm xóa phần tử trong vector
Có 3 dạng:
Dạng 1: erase(<Tên vector>.begin() + <Số nguyên x>): Xóa phần tử tại vị trí số nguyên x( mảng bắt đầu từ vị trí 0 ).
Dạng 2: erase(<Tên vector>.begin() + < Số nguyên x>, < Tên vector >.begin() + < Số nguyên y >): Xóa các phần tử từ vị trí x đến vị trí y – 1.
Dạng 3: erase(0): Xóa tất cả các phần tử trong vector.
* Lưu ý cả 2 trường hợp trên: Nếu vị trí truyền vào không hợp lệ thì chương trình sẽ bị lỗi.
8. insert(): Hàm thêm phần tử vào vector
– Hàm này có 4 dạng:
+ Dạng 1: insert(<Tên vector>.begin() + <số nguyên x>, <giá trị cần chèn>): Chèn giá trị vào vị trí x trong mảng.
+ Dạng 2: insert(<Tên vector>.begin() + <số nguyên x>, <số lần chèn y>, <giá trị cần chèn>): Chèn y lần giá trị vào vị trí x trong mảng.
+ Dạng 3: insert(<Tên vector>.begin() + <số nguyên x>, <Tên vector a>.begin() + <số nguyên y>, <Tên vector a>.begin() + <số nguyên z>):
—> Lấy các phần tử từ vector a bắt đầu lấy từ vị trí y cho đến vị trí z – 1 và chèn vào vị trí x của vector hiện tại.
+ Dạng 4: insert(<Tên vector>.begin() + <số nguyên x>, <Tên mảng a> + <số nguyên y>, <Tên mảng a> + <số nguyên z>):
—-> Lấy các phần tử lấy từ mảng a bắt đầu lấy từ vị trí y đến vị trí z – 1 và chèn vào vị trí x của vector hiện tại.
+ Mảng được tạo mới nếu chưa có, nếu đã có rồi mảng sẽ co lại hoặc dãn ra so với mảng ban đầu.
2. push_back(x): Thêm phần tử x vào cuối mảng vector, và mảng tự động giãn ra thêm 1 phần tử.
3. pop_back(): Xóa phần tử cuối cùng trong mảng, và mảng tự động co lại , giảm bớt đi 1 phần tử.
4. front(): Trả về phần tử đầu tiên của mảng.
5. back(): Trả về phần tử cuối cùng của mảng.
6. clear(): Xóa tất cả các phần tử hiện có trong mảng vector, sau khi thực hiện hàm này thì vector trở thành mảng rỗng.
7. erase(): Hàm xóa phần tử trong vector
Có 3 dạng:
Dạng 1: erase(<Tên vector>.begin() + <Số nguyên x>): Xóa phần tử tại vị trí số nguyên x( mảng bắt đầu từ vị trí 0 ).
Dạng 2: erase(<Tên vector>.begin() + < Số nguyên x>, < Tên vector >.begin() + < Số nguyên y >): Xóa các phần tử từ vị trí x đến vị trí y – 1.
Dạng 3: erase(0): Xóa tất cả các phần tử trong vector.
* Lưu ý cả 2 trường hợp trên: Nếu vị trí truyền vào không hợp lệ thì chương trình sẽ bị lỗi.
8. insert(): Hàm thêm phần tử vào vector
– Hàm này có 4 dạng:
+ Dạng 1: insert(<Tên vector>.begin() + <số nguyên x>, <giá trị cần chèn>): Chèn giá trị vào vị trí x trong mảng.
+ Dạng 2: insert(<Tên vector>.begin() + <số nguyên x>, <số lần chèn y>, <giá trị cần chèn>): Chèn y lần giá trị vào vị trí x trong mảng.
+ Dạng 3: insert(<Tên vector>.begin() + <số nguyên x>, <Tên vector a>.begin() + <số nguyên y>, <Tên vector a>.begin() + <số nguyên z>):
—> Lấy các phần tử từ vector a bắt đầu lấy từ vị trí y cho đến vị trí z – 1 và chèn vào vị trí x của vector hiện tại.
+ Dạng 4: insert(<Tên vector>.begin() + <số nguyên x>, <Tên mảng a> + <số nguyên y>, <Tên mảng a> + <số nguyên z>):
—-> Lấy các phần tử lấy từ mảng a bắt đầu lấy từ vị trí y đến vị trí z – 1 và chèn vào vị trí x của vector hiện tại.
VI: Kết luận
– Bài viết này chỉ là những lời giới thiệu cơ bản về kiểu vector<T> trong thư viện chuẩn STL. Cơ bản nhưng với số lượng kiến thức nêu trên thì các bạn dư sức làm các Project liên quan đến mảng. Có thể khẳng định rằng : đây là 1 mảng động tối ưu nhất trong việc lưu trữ các phần tử trong ngôn ngữ C++. Nó có đầy đủ các tính năng linh hoạt về ” bộ nhớ động ” của các mảng khác như: Danh sách liên kết, Mảng 1 chiều( tĩnh và động ), Queue……
Bạn muốn học lập trình hãy liên hệ ngay với Mỹ Vân để được nhận ưu đãi từ học viện nhé
– Bài viết này chỉ là những lời giới thiệu cơ bản về kiểu vector<T> trong thư viện chuẩn STL. Cơ bản nhưng với số lượng kiến thức nêu trên thì các bạn dư sức làm các Project liên quan đến mảng. Có thể khẳng định rằng : đây là 1 mảng động tối ưu nhất trong việc lưu trữ các phần tử trong ngôn ngữ C++. Nó có đầy đủ các tính năng linh hoạt về ” bộ nhớ động ” của các mảng khác như: Danh sách liên kết, Mảng 1 chiều( tĩnh và động ), Queue……
Bạn muốn học lập trình hãy liên hệ ngay với Mỹ Vân để được nhận ưu đãi từ học viện nhé
Không có nhận xét nào:
Đăng nhận xét