# 物件導向分析與設計(OOAD)
###### tags: `courses-2022-spring`
## class 1
### lab 13
:::info
In C++, you can still use pass-by-value to pass an object to function, such as:
```cpp
void foo(X a) {
……
}
main() {
X b;
foo(b);
m = (Y)b;
}
```
Please explain
1. how C++ compiler implement the pass-by-value for you.
2. why it is not recommended to use pass-by-value to pass a object.
:::
實驗環境:
```
❯ g++ --version
g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
❯ gdb --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
```
code:
```c=
// lab13.cpp
#include <stdio.h>
class X {
public:
int a, b;
X() : a(1), b(2) {
printf("X's constructor\n");
}
X(const X& other) : a(other.a + 1), b(other.b + 1) {
printf("X's copy constructor\n");
}
};
void foo(X x2) {
printf("a: %d, b: %d\n", x2.a, x2.b);
return;
}
int main() {
X x1;
foo(x1);
return 0;
}
```
執行結果
```
❯ g++ -g ./lab13.cpp -o lab13.out && ./lab13.out
X's constructor
X's copy constructor
a: 2, b: 3
```
根據執行結果可以觀察出 `main()` 與 `foo(X)` 之間的 pass-by-value 確實是由 copy-constructor 來達成。
用 `objdump` 觀察組語
```asm
> objdump -d -M intel ./lab13.out > lab13.asm
# 擷取 main 底下的部份code
...
11d8: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
11dc: 31 c0 xor eax,eax
11de: 48 8d 45 e8 lea rax,[rbp-0x18]
11e2: 48 89 c7 mov rdi,rax
11e5: e8 4e 00 00 00 call 1238 <_ZN1XC1Ev>
11ea: 48 8d 55 e8 lea rdx,[rbp-0x18]
11ee: 48 8d 45 f0 lea rax,[rbp-0x10]
11f2: 48 89 d6 mov rsi,rdx
11f5: 48 89 c7 mov rdi,rax
11f8: e8 6f 00 00 00 call 126c <_ZN1XC1ERKS_>
11fd: 48 8d 45 f0 lea rax,[rbp-0x10]
1201: 48 89 c7 mov rdi,rax
1204: e8 80 ff ff ff call 1189 <_Z3foo1X>
...
```
`11e5` 行的 `<_ZN1XC1Ev>` 為 X constructor `X::X()`
`11f8` 行的 `<_ZN1XC1ERKS_>` 為 X copy constructor `X::X(const X&)`
`1204` 行的 `<_Z3foo1X>` 為 `void foo(X)`
可以發現,在組語中 `x1` 記憶體位置配置在 `rbp-0x18`
並在 `11f8` 行 呼叫 X copy constructor,用 `x1` 的資料初始化位於 `rbp-0x10` 的 class X object。
最後在呼叫 `void foo(X)` 時,將 `rbp-0x10` 的地址當作參數傳入。
使用 gdb 觀察執行過程:
> Note: this is a part of screen text from gdb with gef extention
```asm
$rdi : 0x00007fffffffe030 → 0x0000000300000002
...
0x00007fffffffe018│+0x0008: 0x00000001555552f0
0x00007fffffffe020│+0x0010: 0x0000000000000000
0x00007fffffffe028│+0x0018: 0x0000000200000001
0x00007fffffffe030│+0x0020: 0x0000000300000002 ← $rax, $rdi
0x00007fffffffe038│+0x0028: 0x99c7933f0eb14000
0x00007fffffffe040│+0x0030: 0x0000000000000000 ← $rbp
0x00007fffffffe048│+0x0038: 0x00007ffff7de20b3 → <__libc_start_main+243> mov edi, eax
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x5555555551f8 <main+60> call 0x55555555526c <X::X(X const&)>
0x5555555551fd <main+65> lea rax, [rbp-0x10]
0x555555555201 <main+69> mov rdi, rax
→ 0x555555555204 <main+72> call 0x555555555189 <foo(X)>
```
可以注意到位置 `0x00007fffffffe028` 便是變數 `x1` 所在的位置,
而位置 `0x00007fffffffe030` 的 `0x0000000300000002` 便是複製後的 Class X object
當作 `foo(X)` 參數得 `$rdi` 則是指向 `0x00007fffffffe030`
觀察 `foo(X)` function 部份 asm code
```asm
0000000000001189 <_Z3foo1X>:
1189: f3 0f 1e fa endbr64
118d: 55 push rbp
118e: 48 89 e5 mov rbp,rsp
1191: 48 83 ec 10 sub rsp,0x10
1195: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
```
可以注意到傳入的參數 `$rdi` 在完成 callee convention 後被放入 `rbp-0x8`,也就是說在 `foo(X)` 的 stack frame 之中。
:::success
#### lab 13 回答
- 1-1.) C++ class 在 function 間的 pass-by-value 會在 caller 的 stack frame 中新增一個暫時性變數,並將此變數的記憶體位置作為參數傳入 calling function
- 1-2.) code: `Y m = (Y)x;` 中的強治轉型會 call `Y::Y(const X&)` 的 copy constructor
- 2,) 不建議使用 pass-by-value 的原因應該是因為此舉會比起直接 pass-by-address 或 pass-by-reference 的方法多出 call copy constructor 的步驟,除非真的有複製 object 的需求,否則沒有必要使用 pass-by-value
:::
### lab 14
:::info
當你在撰寫 assign operator 時,其格式上大致如下 (以 String 型別為例)
```c
String& String::operator=(const String& rhs)
{
…
return *this ;
}
```
請問說明為何這個 method 要回傳自己?
:::
:::success
#### lab 14 回答
因為如果回傳的不是發起 assign operator 的 object 本身,而是其他 object(`rhs` 或其他再 `new` 出來的)。在執行完 assign operator method 後 object 指向的記憶體位置就會被修改,原本的記憶體空間便會被遺忘,如果此記憶體位置是在 stack 上到沒問題,但是如果是在 heap 上,便會造成 memory leak
:::
## class 2
觀察此 code 與執行結果
```cpp=
#include <stdio.h>
class Pet;
class Cat;
class Pet {
public:
int color;
Pet() : color(0) { printf("Pet constructor\n"); }
Pet(int c) : color(c) { printf("Pet constructor\n"); }
Pet(const Cat& c) { printf("Cat to Pet copy constructor\n"); }
~Pet() { printf("Pet destructor\n"); }
void speak() {
printf("Pet speak\n");
}
};
class Cat : public Pet {
public:
Cat() : Pet(1) { printf("Cat constructor\n"); }
~Cat() { printf("Cat destructor\n"); }
void speak() {
printf("Cat speak, nyan\n");
}
};
int main(int argc, char **argv) {
Cat nyanCat;
nyanCat.speak();
((Pet) nyanCat).speak();
return 0;
}
```
```
Pet constructor
Cat constructor
Cat speak, nyan
Cat to Pet copy constructor # A copy constructor has been called
Pet speak # just before the type casting
Pet destructor
Cat destructor
Pet destructor
```
在第 34 行的 `((Pet) nyanCat).speak()` 會先使用 copy constructor `Pet:Pet(const cat&)` 建立一個 `Pet` object,再以此 object 為參數呼叫 `Pet::speak()`
觀察組語
```asm=
# objdump -d -M intel ./a.out | less
# part of asm code
mov QWORD PTR [rbp-0x18],rax
xor eax,eax
lea rax,[rbp-0x20]
mov rdi,rax
call 12e2 <_ZN3CatC1Ev> ; use cat constructor to init rbp-0x20
lea rax,[rbp-0x20]
mov rdi,rax
call 1364 <_ZN3Cat5speakEv>
lea rdx,[rbp-0x20]
lea rax,[rbp-0x1c]
mov rsi,rdx ; rsi -> the first variable (nyancat)
mov rdi,rax ; rdi -> the temp variable
; (rbp-0x1c didnt exist in the CPP code. let's call it `tmp` for now)
; invoke `Pet::Pet(const Cat&)` with parameter: `nyancat` and `tmp`
call 127e <_ZN3PetC1ERK3Cat>
lea rax,[rbp-0x1c]
mov rdi,rax ; rdi -> tmp
call 12c2 <_ZN3Pet5speakEv> ; invoke `Pet::speak()` with variable tmp
lea rax,[rbp-0x1c]
mov rdi,rax
call 12a2 <_ZN3PetD1Ev> ; delete tmp with `Pet::~Pet()`
mov ebx,0x0
lea rax,[rbp-0x20]
mov rdi,rax
call 1338 <_ZN3CatD1Ev> ; delete nyancat with `Cat::~Cat()`
```
### 上課筆記
:::warning
> constructor 只需要做記憶體 init 和 allocate 就好,
> 不要做額外的事情
> [name=教授]
:::
### Chapt 7
:::info
Example:
> S#:
> 零件供應商的編號(Supplier no)
> SNAME:
> 零件供應商的姓名(supplier name)
> CITY1
> 零件供應商的城市(The city of a supplier)
> P# 零件編號 (part no.)
> PNAME
> 零件名稱 (part name)
> COLOR 零件色彩 (part color)
> WEIGHT 零件重量 (part weight)
> CITY2 零件所儲存的城市 (city where the parts are stored)
> QTY 零件的存量 (The quantity of the parts)
:::
my ans
> Supplier's parts
supplier no| part no
> Supplier
supplier no | supplier name | supplier city
> Parts
part no | part name | color | weight
part no | city
part no | QTY
## chapt 9
當一個名詞要真的能夠成為物件,他必須要有什麼基本條件?
具有 unique id
## chapt 10
Q: Modle that is rigid
unit test?

## chap 11

## UML
title UseCase A
```sequence
User->Button (create/useCase): clickAction
Button (create/useCase)->EditorBehaviorAgent: setBehavior(button.behavior)
User->Canvas: clickAction
Canvas->EditorBehaviorAgent: canvasMouseAction
EditorBehaviorAgent->concreteEditorBehavior: canvasClick
concreteEditorBehavior->Canvas: addObj
```
title UseCase B.1

actor User
```sequence
User->button(associationLine): click()
button(associationLine)->editorAgent: setBehavior(associationLineBehavior)
User->Canvas: press(x,y)
Canvas->+basicObj: *isInShape(x,y)
basicObj-->-Canvas: objClicked
alt objClicked != null
Canvas->editorAgent: objMouseAction(objClicked, event)
editorAgent->concreteBehavior(associationLine): objPress(objClicked, event)
concreteBehavior(associationLine)->+basicObj: getPort(event.point)
basicObj-->-concreteBehavior(associationLine): port
else
Canvas->editorAgent: canvasMouseAction(canvas, event)
editorAgent->concreteBehavior(associationLine): canvasPress(canvas, event)
note right of concreteBehavior(associationLine): do nothing
end
User->Canvas: relese(x,y)
Canvas->+basicObj: *isInShape(x,y)
basicObj-->-Canvas: objClicked
alt objClicked != null
Canvas->editorAgent: objMouseAction(objClicked, event)
editorAgent->concreteBehavior(associationLine): objRelese(objClicked, event)
concreteBehavior(associationLine)->basicObj: getPort(event.point)
basicObj-->-concreteBehavior(associationLine): port
concreteBehavior(associationLine)->a associationLine: New
a associationLine->port: connect
else
Canvas->editorAgent: canvasMouseAction(canvas, event)
editorAgent->concreteBehavior(associationLine): canvasRelese(canvas, event)
note right of concreteBehavior(associationLine): do nothing
end
```
## chap 12- uml class diagram
問題: Shape 與 basicObj
canvas 擁有 shape 那麼在後續處理像是移動物件等程式碼里,不就需要 down scaleing
讓 canvas.shapeList[x] 轉成 basicObj 做動作
所以在這種形況下 down scaleing is acceptable ?
control class