Chuyển tới nội dung chính

Đề 2015-2016

Xây dựng chương trình mô phỏng biên soạn nhạc với các mô tả ký kiệu âm nhạc như sau:

  • Nốt nhạc: là ký hiệu trong bản nhạc dùng để xác định cao độ (độ cao), trường độ (độ dài, độ ngân vang) của từng âm thanh được vang lên trong bản nhạc.

    • Có 7 ký hiệu nốt nhạc dùng để xác định cao độ theo thứ tự từ thấp đến cao, đó là Đô (C), Rê (D), Mi (E), Fa (F), Sol (G), La (A), và Si (B).

    alt text

    • Để xác định trường độ của nốt nhạc có cao độ kể trên, người ta cũng dùng 7 hình nốt để thể hiện, đó là:
      • Nốt tròn có trường độ tương đương với trường độ của 44 nốt đen
      • Nốt trắng có trường độ bằng 22 nốt đen
      • Nốt đen có trường độ bằng 1 phách (đơn vị thời gian trong âm nhạc- vd như 1 bước chân người đi trong không gian)
      • Nốt móc đơn có trường độ bằng 12\frac{1}{2} nốt đen
      • Nốt móc đôi có trường độ bằng 14\frac{1}{4} nốt đen
      • Nốt móc tam có trường độ bằng 18\frac{1}{8} nốt đen
      • Nốt móc tứ có trường độ bằng 116\frac{1}{16} nốt đen

    alt text

  • Dấu lặng (Z - Zero) là ký hiệu cho biết phải ngưng, không diễn tấu âm thanh (không có cao độ) trong một thời gian nào đó. Các dấu lặng trong thời gian tương ứng (giá trị trường độ) với dạng dấu nhạc nào, thì cũng có tên gọi tương tự.

alt text

Ví dụ: Ký hiệu bản nhạc

alt text

Áp dụng kiến thức lập trình hướng đối tượng (kế thừa, đa hình) thiết kế sơ đồ chi tiết các lớp đối tượng (1.5đ) và xây dựng chương trình thực hiện các yêu cầu sau:

  1. Soạn một bản nhạc (1.5đ)
  2. Tìm và đếm có bao nhiêu dấu lặng đen (𝄽) trong bản nhạc(1đ)
  3. Cho biết nốt nhạc có cao độ cao nhất trong bản nhạc (1đ)

Phân tích đề

Bước 1: Thiết kế lớp

Dựa vào đề hoặc nếu đề dài khó hiểu thì chúng ta có thể dựa vào các câu hỏi để mường tượng ra các đối tượng có trong lớp. Ví dụ, như ở câu b yêu cầu: "Tìm và đếm có bao nhiêu dấu lặng đen (𝄽) trong bản nhạc" thì chắc chắn trong bài chúng ta phải có đối tượng DauLang.

Như vậy bài này khi đọc vào chúng ta có thể phát hiện được sẽ có một lớp KyHieu sẽ là lớp cha của lớp NotNhacDauLang. Bên cạnh đó , để quản lí tốt hơn ta cần tạo thêm 1 lớp BanNhac để quản lí các KyHieu.

Bước 2: Xác định các thuộc tính

Ở đây ta dễ dàng xác định thuộc tính chung của DauLangNotNhactrường độ vì thế lớp KyHieu sẽ có thuộc tính là truongDo . Bên cạnh đó lớp NotNhac sẽ có thêm thuộc tính riêng là caoDo.

Bài này nhìn chung các thuộc tính khá dễ xác định nhưng có một điểm lưu ý là ở kiểu dữ liệu của caoDo vì đề bài không bắt buộc kiểu nào cố định nên vì thế thay vì kiểu kí tự là string ta sẽ trừu tượng nó thành kiểu dữ liệu int lưu các các cao độ tương ứng (1 – Đô ,2- Rê,…) .Từ đó giúp ta dễ dàng làm câu c.

Bước 3: Xác định phương thức

Tương tự như các bài ở trên, các đối tượng có các thuộc tính khác nhau nên việc nhập thông tin cho các đối tượng đó cũng khác nhau. Chính vì thế ở bài này, phương thức nhập lớp cơ sở KyHieu sẽ là phương thức ảo.

Tạo phương thức để kiểm tra dấu lặng đen là LaDauLangDen(). Nó sẽ là phương thức ảo và mặc định trả về false, chúng ta sẽ override nó ở lớp dẫn xuất DauLang khi đó nó sẽ trả về true nếu đó là dấu lặng đen.

Cũng tương tự phương thức LaDauLangDen(). Ta tạo một phương thức ảo để lấy cao độ là LayCaoDo() mặc định sẽ trả về 0, nhưng ta sẽ override nó trong lớp NotNhac và trả về cao độ cần tìm.

Sơ đồ thiết kế lớp

Lời giải tham khảo

#include<iostream>
#include<vector>
using namespace std;

class KyHieu {
protected:
float truongDo;
public:
virtual void Nhap() {
int loai;
cout << "Nhap vao loai hinh not(1-Tron, 2-Trang, 3-Den, 4-Moc don, 5-Moc kep, 6-Moc tam, 7-Moc tu): ";
cin >> loai;
switch (loai) {
case 1: { truongDo = 4; break; }
case 2: { truongDo = 2; break; }
case 3: { truongDo = 1; break; }
case 4: { truongDo = 0.5; break; }
case 5: { truongDo = 0.25; break; }
case 6: { truongDo = 0.125; break; }
case 7: { truongDo = 0.0625; break; }
}
}
virtual bool LaDauLangDen() {
return false;
}
virtual int LayCaoDo() {
return 0;
}
};

class NotNhac : public KyHieu {
private:
int caoDo;
public:
void Nhap() {
cout << "Nhap vao cao do(1-Do(C), 2-Re(D), 3-Mi(E), 4-F (F), 5-Sol(G), 6-La(A), 7-Si(B)): ";
cin >> caoDo;
}
int LayCaoDo() {
return caoDo;
}

};

class DauLang :public KyHieu {
public:
bool LaDauLangDen() {
if (truongDo == 1)
return true;
return false;
}
};

class BanNhac {
private:
vector<KyHieu*> ds;
public:
void Nhap() {
cout << "Nhap vao so ky hieu cua ban nhac: ";
int n;
cin >> n;
KyHieu* tmp;
for (int i = 0; i < n; i++) {
int loai;
tmp = NULL;
cout << "Nhap ky hieu thu " << i + 1 << " can them vao ban nhac(1 - NotNhac, 2 - DauLang) : ";
cin >> loai;
if (loai == 1) tmp = new NotNhac;
else tmp = new DauLang;
tmp->Nhap();
ds.push_back(tmp);
}
}
// cau b
int DemDauLangDen() {
int dem = 0;
for (int i = 0; i < ds.size(); i++) {
if (ds[i]->LaDauLangDen()) {
dem++;
}
}
return dem;
}
// cau c
int TimCaoDoMAX() {
int max = ds[0]->LayCaoDo();
int index = 0;
for (int i = 0; i < ds.size(); i++) {
if (ds[i]->LayCaoDo() > max) {
max = ds[i]->LayCaoDo();
index = i;
}
}
return index + 1;
}
};

int main() {
BanNhac b;
b.Nhap();
cout << "So dau lang den cua ban nhac la: " << b.DemDauLangDen();
cout << "\nVi tri not nhac co cao do lon nhat la :" << b.TimCaoDoMAX();
}