Skip to content

Bài lab · Tuần 4

Tuần 4 — Từ UML sang lớp đầu tiên ​

Tuần này thêm vào hệ thống thư viện: hai lớp đầu tiên — Document (tài liệu) và Reader (bạn đọc). Đây là nền móng, mọi tuần sau đều xây tiếp lên hai lớp này.

Mang gì từ tuần trước ​

Chưa có gì. Tuần 4 bắt đầu một thư mục mới <MSSV>_w04/. Nhưng từ tuần 5 trở đi bạn sẽ chép thư mục tuần trước sang và sửa tiếp, nên hãy giữ code sạch ngay từ bây giờ.

Mục tiêu ​

  • Đọc một sơ đồ UML và dịch thành lớp C++.
  • Tách mỗi lớp thành hai file: khai báo .h và cài đặt .cpp.
  • Biên dịch nhiều file bằng một lệnh g++.
  • Viết hàm khởi tạo (constructor) có tham số và constructor mặc định.

Quy ước viết file (áp dụng cả học kỳ) ​

  • File .h chứa khai báo: tên lớp, thuộc tính, nguyên mẫu hàm. Không viết thân hàm ở đây.
  • File .cpp chứa cài đặt: thân của từng hàm, viết dạng TenLop::tenHam(...).
  • Trong .h dùng std::string, không viết using namespace std;. Trong .cpp thì được phép using namespace std;.
  • Mỗi .h phải có "chốt chặn" để không bị include hai lần:
    cpp
    #ifndef DOCUMENT_H
    #define DOCUMENT_H
    // ... khai bao ...
    #endif

Khung bài làm

Tải về, giải nén rồi mở thư mục bằng công cụ bạn dùng. Trong đó có sẵn các file cần nộp với những chỗ ghi TODO để bạn tự viết, cùng file HUONG_DAN.txt ghi cách biên dịch.

Tải khung bài làm tuần 4 (.zip)

Lý thuyết cần nắm ​

Tuần này bạn viết lớp đầu tiên. Mọi ví dụ dưới đây dùng một lớp khác hẳn bài lab, lớp Song (bài hát), và đều đã biên dịch, chạy thật. Đọc để nắm cách viết, rồi tự dịch sơ đồ của Document và vẽ sơ đồ của Reader.

1. Lớp và đối tượng ​

Lớp là cái khuôn: nó mô tả một loại đối tượng có những dữ liệu gì (thuộc tính) và làm được gì (phương thức). Đối tượng là một thứ cụ thể làm ra từ khuôn đó. Chưa chia file, cả chương trình nằm trong một file:

cpp
#include <iostream>
#include <string>
using namespace std;

class Song {
public:
    string title;
    int seconds;

    void show() const {
        cout << title << " (" << seconds / 60 << " phut " << seconds % 60 << " giay)" << endl;
    }
};

int main() {
    Song a;                        // tao doi tuong a tu khuon Song
    a.title = "Hello Viet Nam";
    a.seconds = 263;
    a.show();                      // goi phuong thuc cua a
    return 0;
}
text
Hello Viet Nam (4 phut 23 giay)
  • public: cho phép dùng thuộc tính và phương thức từ bên ngoài lớp, ví dụ trong main. Tuần 5 sẽ học cách đóng lại.
  • Dấu ; sau dấu } đóng lớp là bắt buộc.
  • Truy cập thuộc tính và gọi phương thức bằng dấu chấm: a.title, a.show().

2. Đọc sơ đồ UML của một lớp ​

Sơ đồ lớp là một hộp ba tầng: tên lớp, thuộc tính, phương thức. Đây là sơ đồ của lớp Song:

Sơ đồ UML lớp Song: hai thuộc tính title, seconds; hai constructor và hàm showSơ đồ UML lớp Song: hai thuộc tính title, seconds; hai constructor và hàm show

Dịch từng dòng sang C++:

Thấy trên UMLViết trong file .h
Hộp tên Songclass Song { ... };
Dấu + ở đầu dòngđặt dòng đó dưới public:
+ title : stringstd::string title; (UML ghi tên trước kiểu sau, C++ thì ngược lại)
+ seconds : intint seconds;
+ Song()Song(); (constructor trùng tên lớp, không có kiểu trả về)
+ Song(title, seconds)Song(const std::string& title, int seconds);
+ show() : voidvoid show() const; (hàm chỉ in nên thêm const, xem mục 4)

Sơ đồ thường không ghi kiểu của tham số constructor: lấy kiểu của thuộc tính cùng tên.

3. Constructor: hàm chạy khi đối tượng ra đời ​

Ở mục 1 phải gán từng thuộc tính sau khi tạo đối tượng. Constructor làm việc đó ngay lúc tạo:

cpp
#include <iostream>
#include <string>
using namespace std;

class Song {
public:
    string title;
    int seconds;

    Song() : title("Bai hat moi"), seconds(0) {}
    Song(const string& title, int seconds) : title(title), seconds(seconds) {}

    void show() const {
        cout << title << " (" << seconds / 60 << " phut " << seconds % 60 << " giay)" << endl;
    }
};

int main() {
    Song a("Hello Viet Nam", 263);   // constructor co tham so
    Song b;                          // constructor mac dinh: KHONG co ngoac
    a.show();
    b.show();
    return 0;
}
text
Hello Viet Nam (4 phut 23 giay)
Bai hat moi (0 phut 0 giay)
  • Constructor mặc định không có tham số, chạy khi viết Song b;.
  • Constructor có tham số nhận giá trị ngay lúc tạo: Song a("Hello Viet Nam", 263);.
  • Phần sau dấu : là danh sách khởi tạo. Trong title(title), chữ bên ngoài ngoặc là thuộc tính, chữ bên trong ngoặc là tham số. Đề bài bắt buộc dùng cách này.

Hai lỗi hay gặp với constructor:

Viết Song b(); thay vì Song b;. Có cặp ngoặc rỗng, C++ hiểu dòng đó là khai báo một hàm tên b chứ không phải tạo đối tượng. g++ cảnh báo ở dòng khai báo, rồi báo lỗi ở chỗ gọi b.show() (dòng 20 trong code của bạn còn kèm chú thích, nên g++ in ra cả chú thích đó):

text
main.cpp: In function 'int main()':
main.cpp:20:11: warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
   20 |     Song b();
      |           ^~
main.cpp:20:11: note: remove parentheses to default-initialize a variable
   20 |     Song b();
      |           ^~
      |           --
main.cpp:20:11: note: or replace parentheses with braces to value-initialize a variable
main.cpp:22:7: error: request for member 'show' in 'b', which is of non-class type 'Song()'
   22 |     b.show();
      |       ^~~~

Gán trong thân hàm thay vì dùng danh sách khởi tạo:

cpp
    Song(const string& title, int seconds) : seconds(seconds) {
        title = title;             // SAI: gan tham so title cho chinh no
    }

Trong thân constructor, chữ title là tham số, không phải thuộc tính, nên câu đó gán tham số cho chính nó. Tham số lại là const, nên g++ báo một lỗi dài hàng chục dòng. Chỉ cần đọc mấy dòng đầu (trích):

text
main.cpp: In constructor 'Song::Song(const std::string&, int)':
main.cpp:12:17: error: no match for 'operator=' (operand types are 'const std::string' {aka 'const std::__cxx11::basic_string<char>'} and 'const std::string' {aka 'const std::__cxx11::basic_string<char>'})
   12 |         title = title;             // SAI: gan tham so title cho chinh no
      |                 ^~~~~

Sửa bằng danh sách khởi tạo như ví dụ phía trên; thân hàm để trống.

4. const ở cuối phương thức ​

void show() const hứa rằng show chỉ đọc, không sửa thuộc tính nào của đối tượng. Hàm in thông tin thì nên có const. Chữ const phải có ở cả hai nơi: nguyên mẫu trong .h và dòng định nghĩa trong .cpp. Thiếu ở .cpp, g++ báo không có khai báo nào khớp, rồi chỉ luôn bản đúng:

text
Song.cpp:10:6: error: no declaration matches 'void Song::show()'
   10 | void Song::show() {
      |      ^~~~
In file included from Song.cpp:1:
Song.h:14:10: note: candidate is: 'void Song::show() const'
   14 |     void show() const;
      |          ^~~~
Song.h:6:7: note: 'class Song' defined here
    6 | class Song {
      |       ^~~~

5. Chia một lớp thành .h và .cpp ​

Giống bài 3.0 của tuần 3, chỉ khác là file .h giờ chứa một lớp: .h khai báo, .cpp cài đặt, main.cpp dùng.

cpp
#ifndef SONG_H
#define SONG_H

#include <string>

class Song {
public:
    std::string title;
    int seconds;

    Song();
    Song(const std::string& title, int seconds);

    void show() const;
};

#endif
cpp
#include "Song.h"
#include <iostream>
using namespace std;

Song::Song() : title("Bai hat moi"), seconds(0) {}

Song::Song(const string& title, int seconds)
    : title(title), seconds(seconds) {}

void Song::show() const {
    cout << title << " (" << seconds / 60 << " phut " << seconds % 60 << " giay)" << endl;
}
cpp
#include "Song.h"

int main() {
    Song a("Hello Viet Nam", 263);
    Song b;
    a.show();
    b.show();
    return 0;
}
sh
g++ -std=c++17 -Wall Song.cpp main.cpp -o main.exe
text
Hello Viet Nam (4 phut 23 giay)
Bai hat moi (0 phut 0 giay)
  • File .h: chốt chặn #ifndef / #define / #endif, #include <string>, dùng std::string, không viết using namespace std;.
  • File .cpp: #include "Song.h"; mỗi hàm có Song:: trước tên. Song::Song là constructor, void Song::show() const là phương thức.
  • Lệnh g++ liệt kê mọi file .cpp, không liệt kê file .h.

6. Bốn lỗi hay gặp khi chia file ​

Quên ; sau } của lớp. Lỗi này hay báo ở chỗ rất lạ. Song.cpp include Song.h ngay trước <iostream>, nên g++ báo một loạt lỗi bên trong thư viện chuẩn trước, rồi mới tới dòng thật sự sai. Bỏ qua các dòng có đường dẫn kiểu C:/msys64/.../include/c++/..., tìm dòng nhắc tới file của bạn (trích, đường dẫn tuỳ máy):

text
C:/msys64/ucrt64/include/c++/15.2.0/exception:43:8: error: expected unqualified-id before string constant
   43 | extern "C++" {
      |        ^~~~~
...
In file included from main.cpp:1:
Song.h:15:2: error: expected ';' after class definition
   15 | }
      |  ^
      |  ;

Quên Song:: trước tên hàm trong .cpp. g++ tưởng đó là một hàm thường, không thuộc lớp nào, nên không thấy các thuộc tính (trích):

text
Song.cpp:10:13: error: non-member function 'void show()' cannot have cv-qualifier
   10 | void show() const {
      |             ^~~~~
Song.cpp: In function 'void show()':
Song.cpp:11:13: error: 'title' was not declared in this scope

Quên đưa Song.cpp vào lệnh g++. Phần biên dịch vẫn qua, nhưng bước liên kết không tìm thấy thân hàm (trích, đường dẫn đã rút gọn):

text
ld.exe: ...main.cpp:(.text+0x4c): undefined reference to `Song::Song(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)'
ld.exe: ...main.cpp:(.text+0x74): undefined reference to `Song::Song()'
ld.exe: ...main.cpp:(.text+0x80): undefined reference to `Song::show() const'
collect2.exe: error: ld returned 1 exit status

Thiếu chốt chặn trong .h. Không có #ifndef / #define / #endif, chỉ cần file .h bị include hai lần trong cùng một file .cpp là g++ báo lớp bị định nghĩa lại:

text
In file included from main.cpp:2:
Song.h:4:7: error: redefinition of 'class Song'
    4 | class Song {
      |       ^~~~
In file included from main.cpp:1:
Song.h:4:7: note: previous definition of 'class Song'
    4 | class Song {
      |       ^~~~

7. Vẽ UML từ mô tả bằng lời ​

Bài 4.2 đi ngược chiều: cho mô tả bằng lời, bạn vẽ sơ đồ trước rồi mới code. Ví dụ với mô tả sau:

Lớp Classroom (phòng học) cần có: số phòng, toà nhà, số chỗ ngồi, số chỗ đã có người đặt (luôn bắt đầu bằng 0); một constructor mặc định và một constructor nhận số phòng, toà nhà, số chỗ ngồi; một hàm hiển thị.

  1. Gạch các danh từ chỉ dữ liệu, mỗi danh từ thành một thuộc tính. Đặt tên tiếng Anh ngắn gọn, rồi chọn kiểu: chữ thì string, số đếm thì int.
  2. Gạch các hành động, mỗi hành động thành một phương thức. Constructor trùng tên lớp và không có kiểu trả về; hàm hiển thị có kiểu void.
  3. Kiểm tra quyết định thiết kế. Thuộc tính nào luôn bắt đầu bằng một giá trị cố định thì không có mặt trong tham số constructor. Sơ đồ thể hiện điều đó ngay ở dòng constructor, và nên có thêm một ghi chú cho rõ.
Sơ đồ UML lớp Classroom: bốn thuộc tính, constructor nhận ba tham số, ghi chú booked luôn bắt đầu bằng 0Sơ đồ UML lớp Classroom: bốn thuộc tính, constructor nhận ba tham số, ghi chú booked luôn bắt đầu bằng 0

Nhìn dòng + Classroom(room, building, seats) là biết booked không do người tạo đối tượng quyết định. Vì vậy trong code, constructor phải tự khởi tạo booked bằng 0 trong danh sách khởi tạo.

Vẽ bằng gì cũng được: vẽ trên giấy rồi chụp ảnh, hoặc dùng app.diagrams.net. Khung bài làm tuần 4 có sẵn file mau_so_do_lop.drawio là một hộp lớp trống: mở bằng File → Open from → Device, sửa chữ, rồi xuất ảnh bằng File → Export as → PNG.


Bài 4.1 — Dịch UML thành code (60 phút) ​

Mục tiêu 60 phút60:00Chưa bắt đầu

Sơ đồ UML của lớp Document:

Sơ đồ UML lớp Document: ba thuộc tính id, title, year; hai constructor và hàm displaySơ đồ UML lớp Document: ba thuộc tính id, title, year; hai constructor và hàm display

Trong đó id là mã tài liệu, title là tựa đề, year là năm xuất bản. Ký hiệu + nghĩa là public. Tuần này để public hết cho dễ; tuần 5 sẽ đóng lại.

Nhiệm vụ:

  1. Viết Document.h và Document.cpp đúng theo UML trên.
  2. Document() — constructor mặc định — đặt id = "???", title = "Chua dat ten", year = 0.
  3. Document(id, title, year) — dùng danh sách khởi tạo, không gán trong thân hàm:
    cpp
    Document::Document(const string& id, const string& title, int year)
        : id(id), title(title), year(year) {}
  4. display() in một dòng theo đúng định dạng ở phần kết quả mong đợi.
  5. Trong main.cpp: tạo 2 tài liệu bằng constructor có tham số và 1 tài liệu bằng constructor mặc định, rồi gọi display() cho cả ba.
Hướng dẫn từng bước — mở khi đã tự thử mà vẫn bí

Bước 1. Tạo thư mục <MSSV>_w04, hoặc giải nén khung bài làm rồi đổi tên thư mục MSSV_w04.

Bước 2. Trong Document.h: chốt chặn (khung có sẵn), #include <string>, rồi khai báo lớp Document với public:. Nhớ dấu ; sau dấu }.

Bước 3. Dịch tầng 2 của sơ đồ thành ba dòng thuộc tính, theo đúng cách bảng ở mục 2 đã dịch + title : string và + seconds : int. Trong file .h viết std::string.

Bước 4. Dịch tầng 3: hai constructor (không có kiểu trả về) và display (kiểu void, thêm const). Chỉ viết nguyên mẫu, không viết thân hàm.

Bước 5. Trong Document.cpp: #include "Document.h", #include <iostream>, using namespace std;. Viết constructor mặc định bằng danh sách khởi tạo với ba giá trị đề cho, constructor có tham số theo mẫu trong đề, và display với tiền tố Document::.

Bước 6. Trong main.cpp: tạo hai tài liệu bằng constructor có tham số và một tài liệu bằng constructor mặc định (viết không có cặp ngoặc, mục 3), in dòng tiêu đề, rồi gọi display() cho cả ba.

Bước 7. Biên dịch cả ba file như lệnh trong đề. Khung bài làm có sẵn Reader.h và Reader.cpp chưa có code, nên biên dịch được ngay dù chưa làm bài 4.2. Nếu tự tạo thư mục mà chưa có Reader, tạm biên dịch hai file Document.cpp main.cpp và bỏ dòng #include "Reader.h".

Bước 8. So bốn dòng đầu với kết quả mong đợi, đủ từng dấu cách và dấu |.

Nếu g++ báo lỗi lạ bên trong <iostream> hoặc ở đường dẫn include/c++: xem lại dấu ; sau lớp (mục 6).

Nếu gặp request for member 'display' in 'd3', which is of non-class type 'Document()': bạn đã viết Document d3(); (mục 3).

Nếu gặp no declaration matches 'void Document::display()': thiếu const ở file .cpp (mục 4).


Bài 4.2 — Tự vẽ UML rồi mới code (40 phút) ​

Mục tiêu 40 phút40:00Chưa bắt đầu

Lần này làm ngược lại: vẽ UML trước, code sau.

Lớp Reader (bạn đọc) cần có:

  • mã thẻ, họ tên, số tài liệu đang mượn (khởi tạo bằng 0);
  • một constructor mặc định (mã thẻ và họ tên mặc định tuỳ bạn đặt) và một constructor nhận mã thẻ + họ tên;
  • một hàm display().

Nhiệm vụ:

  1. Vẽ sơ đồ UML của Reader (vẽ tay chụp ảnh, hoặc draw.io — đều được).
  2. Viết Reader.h, Reader.cpp đúng theo sơ đồ mình vừa vẽ.
  3. Thêm vào main.cpp: tạo một bạn đọc và gọi display().

Chú ý: borrowing không nằm trong tham số của constructor — nó luôn bắt đầu từ 0. Đây là một quyết định thiết kế, và bạn phải thể hiện được nó trên sơ đồ UML.

Hướng dẫn từng bước — mở khi đã tự thử mà vẫn bí

Bước 1. Gạch các danh từ trong mô tả của đề: mã thẻ, họ tên, số tài liệu đang mượn. Đặt tên tiếng Anh cho từng thuộc tính (đề đã dùng sẵn tên borrowing cho số tài liệu đang mượn) và chọn kiểu (mục 7).

Bước 2. Vẽ hộp ba tầng. Tầng 3 có hai constructor và display. Dòng constructor có tham số chỉ ghi hai tham số: đó là cách sơ đồ thể hiện borrowing không do người tạo đối tượng quyết định. Ghi chú thêm cạnh borrowing cho rõ.

Bước 3. Xuất sơ đồ thành uml_reader.png, hoặc chụp ảnh bản vẽ tay, rồi đặt vào thư mục bài làm.

Bước 4. Viết Reader.h bằng cách dịch chính sơ đồ bạn vừa vẽ, như cách làm bài 4.1. Thang điểm dành 2 điểm cho việc sơ đồ và code khớp nhau, nên đừng sửa code mà quên sửa sơ đồ.

Bước 5. Viết Reader.cpp. Constructor có tham số phải khởi tạo cả borrowing bằng 0 trong danh sách khởi tạo, dù borrowing không phải tham số.

Bước 6. Thêm vào main.cpp: tạo một bạn đọc, in dòng tiêu đề, gọi display(). Biên dịch đủ ba file .cpp.

Bước 7. So dòng cuối với kết quả mong đợi: Dang muon: 0 tai lieu.

Nếu số đang mượn in ra một số lạ thay vì 0, bạn đã quên khởi tạo borrowing. Thuộc tính kiểu int không được khởi tạo sẽ mang giá trị rác, và g++ không cảnh báo gì. Một lần chạy thật với lỗi này:

text
--- BAN DOC ---
[BD] BD001 | Nguyen Van An | Dang muon: 3 tai lieu

Số rác trên máy bạn có thể khác, thậm chí có lúc tình cờ ra 0. Vì vậy phải khởi tạo, đừng trông vào may mắn.

Nếu gặp undefined reference nhắc tới Reader::Reader: lệnh g++ đang thiếu Reader.cpp (mục 6).


Biên dịch ​

sh
g++ -std=c++17 -Wall Document.cpp Reader.cpp main.cpp -o main.exe

Máy chưa cài được công cụ thì dùng tạm trình biên dịch online theo Phụ lục E.7 — nhớ tải code về máy trước khi đóng tab. Bài chấm điểm tuần 8 làm không có mạng, nên trước tuần 8 vẫn phải cài một công cụ trên máy.

Nếu quên một file .cpp bạn sẽ gặp lỗi kiểu undefined reference to Document::display(). Đó là lỗi liên kết, không phải lỗi cú pháp — hãy nhận ra nó, tuần nào cũng gặp lại.

Kết quả mong đợi ​

text
--- DANH MUC TAI LIEU ---
[TL] TL001 | Lap trinh huong doi tuong | 2019
[TL] TL002 | Vat ly dai cuong 1 | 2021
[TL] ??? | Chua dat ten | 0
--- BAN DOC ---
[BD] BD001 | Nguyen Van An | Dang muon: 0 tai lieu

Thư mục bài làm — giữ trên máy, không nộp ​

text
<MSSV>_w04/
  Document.h    Document.cpp
  Reader.h      Reader.cpp
  main.cpp
  uml_reader.png        (hoặc .jpg — ảnh sơ đồ bạn vẽ ở bài 4.2)

Bài tại lớp không nộp. Tuần 4 chỉ nộp bài về nhà ở cuối trang. Giữ nguyên thư mục này: tuần 5 làm tiếp trên chính nó, và các tuần sau cũng vậy cho tới cuối học kỳ.

Thang điểm — tự chấm bài tại lớp ​

Bảng này không dùng để lấy điểm; bạn tự soi bài mình trước khi xem đáp án.

Nội dungĐiểm
Chương trình chạy được2
Có đủ file .h và .cpp cho cả hai lớp, đặt tên đúng2
Nội dung .h đầy đủ (có #ifndef, khai báo đúng UML)1
Nội dung .cpp đầy đủ, dùng danh sách khởi tạo2
Constructor mặc định hoạt động đúng1
UML bài 4.2 khớp với code đã viết2

Thang điểm này giống hệt thang điểm đề thi thực hành cuối kỳ. Bài thi và hai bài lấy điểm (tuần 5, tuần 8) đều chấm theo khung đó, nên tự chấm theo đây là tập đúng cách.

Bài về nhà — phần duy nhất phải nộp ​

Thêm lớp Librarian (thủ thư): mã nhân viên, họ tên, ca trực ("Sang" / "Chieu"), một constructor mặc định, một constructor ba tham số dùng danh sách khởi tạo, và display(). Vẽ UML trước rồi mới viết code, như bài 4.2.

Nộp thẳng trên Moodle, trong hoạt động Phòng lập trình ảo: gõ code vào đó rồi bấm Đánh giá, máy chạy bốn bộ kiểm thử và chấm điểm ngay; sửa rồi nộp lại bao nhiêu lần cũng được. Hạn nộp: trước buổi lab tuần 5 của lớp bạn — lớp học thứ hai: 23:59 chủ nhật 11/10; lớp học thứ tư: 23:59 thứ ba 13/10. Ngày giờ chính xác luôn hiện ngay trong hoạt động trên Moodle.

Nộp ba thứ: Librarian.h, Librarian.cpp và ảnh sơ đồ UML bạn vẽ (uml_librarian.png hoặc .jpg, tải lên bằng nút Nhập dữ liệu trên thanh công cụ). Không nộp main.cpp — máy chấm có main.cpp riêng, nó tạo các thủ thư rồi gọi display(), nên bạn chỉ cần viết lớp cho đúng.

Máy chấm so output từng ký tự, nên display() phải in đúng một dòng theo mẫu này:

text
[TT] NV001 | Tran Thi Mai | Ca truc: Sang

Constructor mặc định đặt mã là ???, họ tên là Chua dat ten, ca trực là Sang, tức là in ra:

text
[TT] ??? | Chua dat ten | Ca truc: Sang

Hai lỗi làm chương trình không biên dịch được (0 điểm, nhưng sửa rồi nộp lại được): thiếu constructor mặc định, và display() thiếu const ở cuối — máy chấm in qua tham chiếu const nên bắt buộc phải có.

Thang điểm bài về nhà ​

Mười điểm chia đều cho bốn bộ kiểm thử, mỗi bộ 2,5 điểm.

Bộ kiểm thửĐiểm
Chỉ constructor mặc định2,5
Hai thủ thư, ca Sang và ca Chieu2,5
Họ tên dài, mã khác2,5
Ba thủ thư liên tiếp2,5

Sơ đồ UML là điểm cộng, tối đa 2 điểm, cộng thêm ngoài 10 điểm ở trên:

Sơ đồ UMLĐiểm cộng
Khớp với chính code bạn nộp: đúng tên thuộc tính, đúng ký hiệu +/-, đúng tên lớp+2
Có sơ đồ nhưng lệch tên hoặc lệch +/- so với code+1
Không nộp sơ đồ, hoặc nộp file không mở ra được sơ đồ0

Đáp án tuần này

Nộp bài xong rồi mở trang đáp án tuần 4 và gõ mã mở đáp án giảng viên đăng trên Moodle. Mã đó chỉ hiện ra sau khi bạn đã nộp bài: đọc đáp án trước khi tự làm thì buổi lab thành vô ích.

© 2026 Ngan V.T. Nguyen · Nội dung theo giấy phép CC BY-NC-SA 4.0 · Code ví dụ theo giấy phép MIT · Ảnh của bên thứ ba giữ giấy phép riêng, ghi ở Phụ lục E

Nội dung theo giấy phép CC BY-NC-SA 4.0 · Code ví dụ theo giấy phép MIT · Ảnh của bên thứ ba giữ giấy phép riêng, ghi ở Phụ lục E