114 一般警察特考三等 警察資訊管理人員類別 物件導向程式設計 考古題詳解
第 一 題25 分
針對下列 C++ 程式碼,請說明其輸出與其程式 p.print()、q.split()(Line 28、29)執行之運作流程邏輯;並請說明在 testPattern() 中測試 Pattern 類別 split()(Line 27、29),對於「指令與條件判斷涵蓋度」不足之處。
#include <iostream> #include <string> #include <vector> using namespace std;
class Pattern { public: Pattern(string data) { this->data = data; }
void split(string symbol) {
int start = 0, stop = 0;
stop = data.find(symbol);
while (stop != string::npos) {
if (stop != start)
words.push_back(data.substr(start, stop - start));
start = stop + symbol.size();
stop = data.find(symbol, start);
}
if (start != length)
words.push_back(data.substr(start));
}
void print() {
for (auto& s : words)
cout << s << "#";
cout << endl;
}
private: string data; vector<string> words; int length; };
void testPattern() { Pattern p("this is a test"); Pattern q("that was so happle");
p.split(" ");
p.print();
q.split("ha");
q.print();
}
int main() { testPattern(); return 0; }
登入後即可作答並保存紀錄。
核心觀念
本題考查 C++ 字串與容器的基本操作,以及追蹤函式執行流程:
string::find(symbol, start)從指定位置尋找分隔字串,找不到時回傳string::npos。string::substr(start, count)取出從start起、長度為count的子字串;省略count時取到字串結尾。vector::push_back將切出的字串加入words。print依序輸出words中的字串,每個字串後接#,最後換行。
解題方法與執行流程
追蹤 split 時,先記錄 start,再找下一個分隔符位置 stop。找到分隔符後,將 data[start, stop) 放入 words,並把 start 移至分隔符之後;直到找不到分隔符,最後再處理剩餘部分。
p.split(" ") 與 p.print
p 的資料是 "this is a test",空白的位置依序為 4、7、9。
分隔符位置 stop | 切出的字串 | 更新後的 start |
|---|---|---|
| 4 | this | 5 |
| 7 | is | 8 |
| 9 | a | 10 |
接著找不到下一個空白,split 將 data.substr(10),也就是 test,加入 words。因此 p.print 輸出:
this#is#a#test#
q.split("ha") 與 q.print
q 的資料是 "that was so happle"。子字串 "ha" 出現在索引 2,以及索引 13。
- 第一次找到索引 2:切出索引 0 到 1 的
"th",接著start更新為 。 - 從索引 4 繼續搜尋,在索引 13 找到
"ha":切出"t was so ",接著start更新為 。 - 找不到下一個
"ha",最後將索引 15 之後的"pple"加入words。
第 二 題25 分
下列 Java 程式碼計算食物(Food)料理(cook)後,動物(Animal)攝取之卡路里量。請完成統一塑模語言 UML 圖;並說明其執行後的輸出,以及多型(Polymorphism)、封裝(Encapsulation)發生所在之程式碼行數。
🖼️【此處有附 UML 類別圖,請對照原卷】
abstract class Food { public Food(int cal, String type) { this.calorie = cal; this.type = type; }
abstract public void cook(int c);
public String show(int c) {
String result = type + ":";
cook(c);
result += calorie;
return result;
}
protected int calorie; // 卡路里
private String type;
}
class Bread extends Food { public Bread(int c, int a) { super(c, "Bread"); additives = a; }
public void cook(int cal) {
calorie += additives * cal;
}
private int additives;
}
class Beef extends Food { public Beef(int c) { super(c, "Beef"); condiments = c * 2; }
public void cook(int cal) {
calorie += condiments % cal;
}
private int condiments;
}
class Animal { public Animal(Food f) { food = f; }
public String getCal() {
return food.show(2);
}
private Food food;
}
public class FoodTest { public static void main(String[] args) { Food f1 = new Bread(3, 2); Food f2 = new Bread(2, 3); Food f3 = new Beef(3);
Animal a1 = new Animal(f1);
Animal a2 = new Animal(f2);
Animal a3 = new Animal(f3);
System.out.println(a1.getCal());
System.out.println(a2.getCal());
System.out.println(a3.getCal());
}
}
登入後即可作答並保存紀錄。
核心觀念
本題考查:
- UML 類別圖與類別繼承。
- 抽象類別與抽象方法。
- 多型(Polymorphism)與動態繫結。
- 封裝(Encapsulation)與存取修飾詞。
- Java 整數運算及方法執行流程。
Food 是抽象類別,不能直接建立物件;Bread 與 Beef 繼承 Food,並各自實作 cook(int) 方法。
UML 類別圖
1. Food 抽象類別
<<abstract>>
Food
--------------------------------
# calorie : int
- type : String
--------------------------------
+ Food(cal : int, type : String)
+ {abstract} cook(c : int) : void
+ show(c : int) : String
其中:
#表示protected。-表示private。+表示public。
2. Bread 類別
Bread
--------------------------------
- additives : int
--------------------------------
+ Bread(c : int, a : int)
+ cook(cal : int) : void
Bread 繼承 Food。
3. Beef 類別
Beef
--------------------------------
- condiments : int
--------------------------------
+ Beef(c : int)
+ cook(cal : int) : void
Beef 繼承 Food。
4. Animal 類別
Animal
--------------------------------
- food : Food
--------------------------------
+ Animal(f : Food)
+ getCal : String
Animal 持有一個 Food 型態的成員變數,因此具有 Animal 到 Food 的關聯:
Animal 1 ───────── 1 Food
完整的繼承關係為:
Food
<<abstract>>
/ \
/ \
Bread Beef
Animal ─────── Food
Bread 與 Beef 對 Food 是一般化(Generalization/繼承)關係;Animal 與 Food 是關聯(Association)關係。
解題方法與輸出推導
1. 建立 f1
Food f1 = new Bread(3, 2);
呼叫 Bread 建構子:
calorie = 3type = "Bread"additives = 2
執行:
a1.getCal
進入 food.show(2):
String result = type + ":";
得到:
Bread:
接著執行 cook(2)。由於實際物件是 Bread,因此呼叫 Bread.cook(2):
所以:
Bread:7
2. 建立 f2
Food f2 = new Bread(2, 3);
第 三 題25 分
針對下列 C++ 程式碼,請修正程式碼行數 58「Job jobs ...」的錯誤及修正錯誤後正確執行之輸出;並請說明 Employee 類別 candidate 資料的運作原理,以及程式碼行數 61「e1.arrange()」程式執行之流程。
#include <iostream> #include <string> #include <map> using namespace std;
class Job { public: Job(string _name, int _sa, int _sk) { name = _name; salary = _sa; skill = _sk; employee_name = "None"; }
string getName() { return name; }
int getSalary() { return salary; }
int getSkill() { return skill; }
void hire(string _name) { employee_name = _name; }
void print() {
cout << name << ":" << employee_name << endl;
}
private: string name; int salary; int skill; string employee_name; };
class Employee { public: Employee(string _name, int _sa, int _sk) { name = _name; salary = _sa; skill = _sk; }
void match(Job *m) {
if ((m->getSalary() >= salary) && (skill >= m->getSkill())) {
candidate[m->getName()] = m;
}
}
void arrange() {
int score = 0;
Job *jb, *jb_wanted;
for (auto c = candidate.begin(); c != candidate.end(); ++c) {
jb = c->second; // get value of MAP
if (jb->getSalary() >= score) {
score = jb->getSalary();
jb_wanted = jb;
}
}
jb_wanted->hire(name);
}
string getName() { return name; }
private: string name; int salary; int skill; map<string, Job *> candidate; };
int main() { Employee e1("Tom", 37, 85); Employee e2("John", 35, 75);
Job jobs[] = {
new Job("RD", 45, 75),
new Job("Sales", 35, 70),
new Job("Manager", 55, 80)
};
for (int i = 0; i < 3; i++)
e1.match(jobs[i]);
e1.arrange();
for (int i = 0; i < 3; i++)
e2.match(jobs[i]);
e2.arrange();
for (int i = 0; i < 3; i++)
jobs[i]->print();
return 0;
}
登入後即可作答並保存紀錄。
核心觀念
本題考查 C++ 的指標、動態配置物件、map 關聯容器,以及以條件篩選和比較分派工作。
new Job(...)會建立Job物件,並回傳指向該物件的Job*。jobs是陣列,因此宣告型別必須能存放Job*。candidate是map<string, Job*>:鍵是職務名稱,值是指向職務物件的指標。match先判斷員工是否符合職務條件,符合才將職務指標放入candidate。arrange從候選職務中選出薪資最高者,並把員工姓名記錄到該職務。
修正行數 58
原程式將 new Job(...) 產生的 Job* 放進 Job 陣列,型別不符。應將陣列宣告為 Job*:
Job* jobs[] = {
new Job("RD", 45, 75),
new Job("Sales", 35, 70),
new Job("Manager", 55, 80)
};
此時 jobs[i] 是指標,能傳給 match(Job *m),也能使用 jobs[i]->print 呼叫物件函式。
candidate 資料的運作原理
candidate 宣告如下:
map<string, Job *> candidate;
它以職務名稱作為鍵,以職務指標作為值。match 的判斷條件為:
(m->getSalary >= salary) && (skill >= m->getSkill)
也就是職務薪資必須不低於員工的薪資要求,且員工技能值必須不低於職務要求。符合時執行:
candidate[m->getName] = m;
這會以職務名稱作為鍵,將該職務的指標存入 candidate。map 的鍵會依字典順序排列,因此走訪順序為 Manager、RD、Sales;不過本題 arrange 比較的是薪資,並非依 map 順序決定錄用職務。
e1.arrange 執行流程
e1 是 Tom,薪資要求為 37,技能值為 85。
逐一執行 e1.match(jobs[i]):
| 職務 | 薪資條件 | 技能條件 | 是否加入 candidate |
|---|
第 四 題25 分
針對下列 C++ 程式碼,請說明其輸出與其 inpuScore()、computeAverage() 執行之運作流程邏輯;並請說明 computeAverage() 設計上的問題。
#include <stdexcept> #include <iostream> #include <string> using namespace std;
class Student { public: Student(string n) { name = n; math_score = eng_score = average = 0; }
void checkSmall(int s) {
if (s <= 0)
throw out_of_range("small");
cout << "ok,";
}
void inpuScore(int m_s, int e_s) {
try {
checkSmall(m_s);
checkSmall(e_s);
math_score = m_s;
eng_score = e_s;
}
catch (exception &e) {
cout << e.what() << endl;
}
}
void computeAverage(int n) {
try {
if (n <= 0)
throw out_of_range("zero");
average = (math_score + eng_score) / n;
}
catch (exception &e) {
cout << "exc" << endl;
}
catch (out_of_range &e) {
cout << e.what() << endl;
}
}
void print() {
cout << name << ":" << average << endl;
}
private: int math_score; // 數學成績 int eng_score; // 英文成績 int average; // 平均成績 string name; };
void test01() { Student stu("John");
stu.inpuScore(0, 0);
stu.inpuScore(100, -1);
stu.inpuScore(100, 90);
stu.print();
}
void test02() { Student stu("Tom");
stu.inpuScore(100, 90);
stu.computeAverage(0);
stu.print();
}
int main() { test01(); test02(); return 0; }
登入後即可作答並保存紀錄。
核心觀念
本題考查 C++ 的例外處理與控制流程,重點包括:
throw丟出例外後,該try區塊中尚未執行的敘述會略過,控制流程轉到第一個符合型別的catch。out_of_range繼承自exception。因此以catch (exception &e)捕捉時,也能捕捉out_of_range。computeAverage的兩個catch順序有問題:較一般的基底類別exception放在前面,會先捕捉out_of_range,後面的catch (out_of_range &e)無法執行,且編譯器通常會將它視為永遠無法到達的處理子句並發出警告。- C++ 的整數除法會捨去小數部分;本題未發生有效的正數除法,因此不需進行平均值計算。
解題方法
依序追蹤每次方法呼叫:先確認例外是否被丟出,再看例外發生前哪些敘述已執行、分數成員是否已更新,最後判斷 average 的值。每個 Student 物件建構時,math_score、eng_score 與 average 都先設為 。
test01 執行流程
- 建立
stu("John"),三個整數成員初值皆為 。 - 呼叫
stu.inpuScore(0, 0):
checkSmall(0)因為 ,丟出out_of_range("small")。- 例外發生後,第二次
checkSmall及兩個分數賦值都不會執行。 - 例外由
inpuScore的catch (exception &e)捕捉,印出e.what,也就是small。
- 呼叫
stu.inpuScore(100, -1):
checkSmall(100)通過,先印出ok,。checkSmall(-1)丟出out_of_range("small")。- 例外處理使兩個分數賦值都不執行;即使數學成績檢查通過,
math_score仍維持原值 。 catch印出small。
- 呼叫
stu.inpuScore(100, 90):
- 兩次檢查都通過,各印出一次
ok,。 - 接著才將
math_score設為 、eng_score設為 。
average從未在test01中計算,仍是 。print印出John:0。
test02 執行流程
- 建立
stu("Tom"),三個整數成員初值皆為 。 stu.inpuScore(100, 90)的兩次檢查都通過,印出ok,ok,,並將兩科成績設為 與 。