Rename
@@ -0,0 +1,140 @@
|
||||
---
|
||||
slug: "[C++ 筆記] 智慧指標:unique_ptr & shared_ptr"
|
||||
title: "[C++ 筆記] 智慧指標:unique_ptr & shared_ptr"
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- c++
|
||||
- memory
|
||||
categories:
|
||||
- Programming
|
||||
series:
|
||||
- C++ 筆記
|
||||
date: 2019-12-01T00:00:00
|
||||
lastmod: 2019-12-01T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
[`unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)與[`shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr)都是智慧指標,箱對於原本的raw pointer,智慧指標使用起來更方便,也不用擔心delete的問題。
|
||||
|
||||
<!--more-->
|
||||
|
||||
## unique_ptr
|
||||
`unique_ptr` 的特點是,它保證在一個時間內,只會有一個指標的擁有者,也就是這個指標不能被複製跟移動,當 `unique_ptr` 離開它的scope時候,它所擁有的指標也隨之被delete。這讓你不用擔心memory leak的問題。
|
||||
假設我們有一個class叫 `BigBuffer` ,原本分配記憶體的方法:
|
||||
```cpp
|
||||
BigBuffer* bigBuf = new BigBuffer(bufferSize);
|
||||
// Use buffer here
|
||||
delete bigBuf;
|
||||
```
|
||||
|
||||
用 `unique_ptr`:
|
||||
```cpp
|
||||
auto bigBuf = std::make_unique<BigBuffer>(bufferSize);
|
||||
// Use buffer here
|
||||
// bigBuf will be released when exiting scope
|
||||
```
|
||||
|
||||
我們統一用[`std::make_unique<>`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)這個template function來建立 `unique_ptr` ,角括號 `<>` 裡面要帶入你要建立的型別,後面的括號 `()` 就是型別的constructor,使用起來跟 `new` 是一樣的。
|
||||
因為 `std::make_unique<>` 裡面已經有表明型別了,所以變數就用 `auto` 就可以了,不用再寫一次型別。
|
||||
|
||||
一旦 `unique_ptr` 建立之後,使用起來就跟一般指標沒有兩樣,都是用 `->` 來操作,例如:
|
||||
```cpp
|
||||
bigBuf->setXXX();
|
||||
bigBuf->getXXX();
|
||||
```
|
||||
|
||||
但是別忘記 `unique_ptr` 本身還是一個local variable,所以我們可以用 `.` 來操作 `unique_ptr` ,例如我們可以用 `.reset()` 重新配一個指標:
|
||||
```cpp
|
||||
BigBuffer* pBuffer = new BigBuffer();
|
||||
bigBuf.reset(pBuffer);
|
||||
```
|
||||
這時候舊指標會自動delete,如果記憶體分配有成功的話,bigBuf會接管剛剛new出來的指標,或者變成 `nullptr` (記憶體分配失敗)。
|
||||
|
||||
如果單純想要釋放指標,那就單純的呼叫 `reset()` 就好。
|
||||
```cpp
|
||||
bigBuf.reset(); // Now I'm nullptr
|
||||
```
|
||||
|
||||
如果要分配陣列的話:
|
||||
```cpp
|
||||
auto intArray = std::make_unique<int[]>(1024);
|
||||
```
|
||||
|
||||
使用方式也是一樣的:
|
||||
```cpp
|
||||
intArray[5] = 555;
|
||||
```
|
||||
|
||||
不過對於陣列的操作更建議使用 `std::array` 。
|
||||
|
||||
如果有什麼特殊原因讓你決定不再讓 `unique_ptr` 來幫你管理指標,可以用 `release()` 來讓出指標:
|
||||
```cpp
|
||||
auto intArray = std::make_unique<int[]>(1024);
|
||||
int* intArrayRaw = intArray.release(); // Now I don't care anymore
|
||||
```
|
||||
但是這時候呼叫 `delete[]` (或 `delete` )的責任又回到你身上了。所以千萬不要把 `release()` 跟 `reset()` 搞混了。
|
||||
|
||||
`unique_ptr` 不能被複製跟移動,所以下列的寫法都編不過:
|
||||
```cpp
|
||||
auto ptr1 = std::make_unique<int>(5);
|
||||
std::unique_ptr<int> ptr2(ptr1); // Error
|
||||
std::unique_ptr<int> ptr2 = ptr1; // Error
|
||||
```
|
||||
在Visual Studio 2017上,錯誤訊息是這樣:`error C2280: 'std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)': attempting to reference a deleted function`。
|
||||
其實就是`unique_ptr`的copy constructor跟assignment operator都被標記為delete了。
|
||||
|
||||
### Move a unique_ptr
|
||||
如果一定要把 `unique_ptr` 指定給別人可以嗎?可以的,用 `std::move()` 來轉移:
|
||||
```cpp
|
||||
auto ptr1 = std::make_unique<int>(5);
|
||||
// do something
|
||||
auto anotherPtr = std::move(ptr1);
|
||||
```
|
||||
|
||||
`ptr1` 原本所管理的指標會轉移給 `anotherPtr``,ptr1` 會變成 `nullptr。`
|
||||
|
||||
|
||||
## shared_ptr
|
||||
建立一個 `shared_ptr` 是使用[`std::make_shared()`](https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared):
|
||||
```cpp
|
||||
auto myBuf = std::make_shared<BigBuffer>(bufferSize);
|
||||
```
|
||||
|
||||
但是 `shared_ptr` 可以被複製與移動,這是跟 `unique_ptr` 的差別:
|
||||
```cpp
|
||||
auto myBuf = std::make_shared<BigBuffer>(bufferSize);
|
||||
|
||||
std::shared_ptr<BigBuffer> bufCopy = myBuf;
|
||||
```
|
||||
|
||||
現在 bufCopy 跟 myBuf 都指向同一個指標,他們都可以操作這個指標:
|
||||
```cpp
|
||||
myBuf->setZero(startAddr, endAddr);
|
||||
bufCopy->setOne(startAddr, endAddr);
|
||||
```
|
||||
|
||||
`shared_ptr` 內部有一個參考記數(reference count)來紀錄它所擁有的指標已經分享給幾個變數了,只要有變數離開了他的scope,參考記數就會減少,反之,要是像上面那樣有人複製指標,參考記數就會增加,參考記數歸0的時候,指標就會被釋放。
|
||||
|
||||
有了 `shared_ptr` 我們就不必擔心 delete 的責任問題:
|
||||
```cpp
|
||||
std::shared_ptr<BigBuffer> getBuffer(int32_t bufferSize) {
|
||||
return std::make_shared<BigBuffer>(bufferSize);
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto myBuf = getBuffer(1024); // new(malloc) memory
|
||||
// use myBuf
|
||||
|
||||
return 0;
|
||||
} // myBuf delete memory here
|
||||
```
|
||||
|
||||
`shared_ptr` 有一個問題是可以會「循環參考」(cyclic references),也就是 share_ptr A 指向另一個 share_ptr B ,然後 share_ptr B 又指向 share_ptr A,這造成參考記數(reference count)不會減少而永遠無法釋出指標。這個是需要注意的。
|
||||
|
||||
但是 `shared_ptr` 還是讓記憶體的管理問題大大減少,應該用 `shared_ptr` 來代替 `new` & `delete` 。
|
||||
234
content/posts/2019/2019-12-08_[C++ 筆記] rvalue reference/index.md
Normal file
@@ -0,0 +1,234 @@
|
||||
---
|
||||
slug: "[C++ 筆記] rvalue reference"
|
||||
title: "[C++ 筆記] rvalue reference"
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- c++
|
||||
categories:
|
||||
- Programming
|
||||
series:
|
||||
- C++ 筆記
|
||||
date: 2019-12-08T00:00:00
|
||||
lastmod: 2019-12-08T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
|
||||
rvalue 是指:
|
||||
- 等號右邊的值
|
||||
- 臨時的值,例如運算的結果
|
||||
- 無法被取址(address-of)的物件
|
||||
|
||||
<!--more-->
|
||||
|
||||
## rvalue reference
|
||||
一般的參考只能參考[[lvalue]],如下的程式是ok的:
|
||||
```cpp
|
||||
int a = 10;
|
||||
int& b = a;
|
||||
```
|
||||
|
||||
但是像這樣就不行了:
|
||||
```cpp
|
||||
int a = 10;
|
||||
int b = 5;
|
||||
int& c = a + b;
|
||||
```
|
||||
|
||||
因為`a+b`是一個 rvalue(臨時的值,沒辦法取址),所以無法參考。
|
||||
但是可以用`&&`來參考 rvalue。例如:
|
||||
```cpp
|
||||
int a = 10;
|
||||
int b = 5;
|
||||
int&& c = a + b; // c = 15
|
||||
```
|
||||
|
||||
而不用這樣:
|
||||
```cpp
|
||||
int a = 10;
|
||||
int b = 5;
|
||||
int r = a + b;
|
||||
int& c = r;
|
||||
```
|
||||
|
||||
了解 rvalue reference 之後,就可以實作類別的 move constructor 跟 move assignment operator。這可以減少複製的成本。
|
||||
|
||||
## Move constructor
|
||||
假設我們有一個 class 叫 BigBuffer,定義如下:
|
||||
```cpp
|
||||
class BigBuffer {
|
||||
public:
|
||||
BigBuffer(int size=100*1024*1024) :
|
||||
bufferSize(size)
|
||||
{
|
||||
std::cout << "BigBuffer constructor\n";
|
||||
this->buffer = std::make_unique<uint8_t[]>(bufferSize);
|
||||
}
|
||||
|
||||
~BigBuffer() {
|
||||
std::cout << "BigBuffer destructor\n";
|
||||
}
|
||||
|
||||
BigBuffer(const BigBuffer& src) {
|
||||
std::cout << "BigBuffer copy constructor\n";
|
||||
bufferSize = src.bufferSize;
|
||||
buffer = std::make_unique<uint8_t[]>(bufferSize);
|
||||
std::memcpy(buffer.get(), src.buffer.get(), bufferSize);
|
||||
}
|
||||
|
||||
BigBuffer& operator= (BigBuffer& src) {
|
||||
std::cout << "BigBuffer copy operator\n";
|
||||
bufferSize = src.bufferSize;
|
||||
buffer = std::make_unique<uint8_t[]>(bufferSize);
|
||||
std::memcpy(buffer.get(), src.buffer.get(), bufferSize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int bufferSize = 0;
|
||||
std::unique_ptr<uint8_t[]> buffer = nullptr;
|
||||
};
|
||||
```
|
||||
|
||||
這個 class 的特色就是每一次使用都會佔用100MB的記憶體空間,想像下面的程式的動作:
|
||||
```cpp
|
||||
BigBuffer buf1;
|
||||
// Do something with buf1
|
||||
// Assign to buf2
|
||||
BigBuffer buf2 = buf1;
|
||||
```
|
||||
|
||||
執行訊息:
|
||||
```
|
||||
BigBuffer constructor // create buf1
|
||||
BigBuffer copy constructor, copy 104857600Bytes // copy buf1 to buf2
|
||||
...
|
||||
```
|
||||
|
||||
這會先產生 `buf1`,然後把 `buf1` copy 給 `buf2`。如果我們想要省下 copy 的成本,這時候 Move constructor 就可以派上用場了。
|
||||
幫 `BigBuffer` 加一個 Move constructor:
|
||||
```cpp
|
||||
class BigBuffer {
|
||||
public:
|
||||
...
|
||||
|
||||
BigBuffer(BigBuffer&& src) noexcept {
|
||||
std::cout << "BigBuffer move constructor\n";
|
||||
bufferSize = src.bufferSize;
|
||||
buffer = std::move(src.buffer);
|
||||
|
||||
src.buffer.reset();
|
||||
src.bufferSize = 0;
|
||||
}
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
這個 move constructor 的參數就是一個 rvalue reference,我們把來源的 bufferSize 跟 buffer 指標「移到」我們這邊,而不是完整的複製一份。在轉移之後呢,當然也要把來源清空,讓轉移更加明確。
|
||||
|
||||
有了 Move assignment operator 之後,在執行一次原本的程式,你會發現訊息......沒有變,還是一樣呼叫 copy constructor 來複製了100MB 的 buffer,這時我們需要明確的告訴 compiler 我們要「移動」物件,而不是複製它,把原本的程式改為:
|
||||
```cpp
|
||||
BigBuffer buf1;
|
||||
// Do something with buf1
|
||||
// Assign to buf2
|
||||
BigBuffer buf2 = std::move(buf1);
|
||||
```
|
||||
|
||||
我們用 `std::move()` 來「移動」物件,這時輸出變成
|
||||
```
|
||||
BigBuffer constructor // create buf1
|
||||
BigBuffer move constructor // move buf1 to buf2, buf1 has nullptr now
|
||||
...
|
||||
```
|
||||
|
||||
另外一個情形也可以受益於此,假如我們有個 function 會產生 `BigBuffer`,如下:
|
||||
```cpp
|
||||
BigBuffer BigBufferCreator() {
|
||||
std::cout << "BigBufferCreator: Create a BigBuffer!\n";
|
||||
BigBuffer tempb;
|
||||
// do something
|
||||
std::cout << "BigBufferCreator: return\n";
|
||||
return tempb;
|
||||
}
|
||||
|
||||
BigBuffer b = BigBufferCreator(); // copy tempb to b
|
||||
```
|
||||
|
||||
在沒有 Move constructor 的情況下,上面的程式會先產生一個 `tempb`,然後複製給 `b`,訊息:
|
||||
```
|
||||
BigBufferCreator: Create a BigBuffer!
|
||||
BigBuffer constructor
|
||||
BigBufferCreator: return
|
||||
BigBuffer copy constructor, copy 104857600Bytes // Copy 100MB!
|
||||
...
|
||||
```
|
||||
|
||||
在有 Move constructor 的情況下,訊息就變成:
|
||||
```
|
||||
BigBufferCreator: Create a BigBuffer!
|
||||
BigBuffer constructor
|
||||
BigBufferCreator: return
|
||||
BigBuffer move constructor // Use MOVE!
|
||||
BigBuffer destructor
|
||||
BigBuffer destructor
|
||||
```
|
||||
|
||||
因為 `BigBufferCreator()` 產生的就是一個 `BigBuffer` rvalue,所以 compiler 會使用 move constructor(`BigBuffer(BigBuffer&& src)`) 而不是 copy constructor。
|
||||
|
||||
## Move assignment operator(`=`)
|
||||
Move assignment operator 的行為跟 move constructor 是一樣的,幫 `BigBuffer` 加入 move assignment operator:
|
||||
```cpp
|
||||
class BigBuffer {
|
||||
public:
|
||||
...
|
||||
|
||||
BigBuffer& operator=(BigBuffer&& src) noexcept {
|
||||
std::cout << "BigBuffer move operator\n";
|
||||
bufferSize = src.bufferSize;
|
||||
buffer = std::move(src.buffer);
|
||||
|
||||
src.buffer.reset();
|
||||
src.bufferSize = 0;
|
||||
return *this;
|
||||
}
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
測試程式:
|
||||
```cpp
|
||||
BigBuffer b1, b2;
|
||||
b2 = b1;
|
||||
```
|
||||
|
||||
訊息:
|
||||
```
|
||||
BigBuffer constructor
|
||||
BigBuffer constructor
|
||||
BigBuffer copy operator, copy 104857600Bytes
|
||||
```
|
||||
|
||||
還是使用 copy assignment operator 來複製,理由是一樣的,需要一個明確的 `std::move()` 來表示「轉移」的行動,把程式改成:
|
||||
```cpp
|
||||
BigBuffer b1, b2;
|
||||
b2 = std::move(b1);
|
||||
```
|
||||
|
||||
這樣就可以了。訊息:
|
||||
```
|
||||
BigBuffer constructor
|
||||
BigBuffer constructor
|
||||
BigBuffer move operator // Use MOVE!
|
||||
```
|
||||
|
||||
## 參考
|
||||
- [Value categories - cppreference.com](https://en.cppreference.com/w/cpp/language/value_category)
|
||||
- [rvalue 參考](https://openhome.cc/Gossip/CppGossip/RvalueReference.html)
|
||||
- [Move constructors - cppreference.com](https://en.cppreference.com/w/cpp/language/move_constructor)
|
||||
- [Move assignment operator - cppreference.com](https://en.cppreference.com/w/cpp/language/move_assignment)
|
||||
237
content/posts/2019/2019-12-15_[C++ 筆記] Lambda/index.md
Normal file
@@ -0,0 +1,237 @@
|
||||
---
|
||||
slug: "[C++ 筆記] Lambda"
|
||||
title: "[C++ 筆記] Lambda"
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- c++
|
||||
categories:
|
||||
- Programming
|
||||
series:
|
||||
- C++ 筆記
|
||||
date: 2019-12-15T00:00:00
|
||||
lastmod: 2019-12-15T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
一個簡單的 Lamdba 運算式:
|
||||
```cpp
|
||||
[] (int x, int y) -> bool {
|
||||
return x < y;
|
||||
}
|
||||
```
|
||||
|
||||
<!--more-->
|
||||
|
||||
- 以中括號開頭,中括號被稱為*lamdba 導入器(lamdba introducer)*
|
||||
- 小括號裡面是*lamdba 參數列表(lambda parameter list)*
|
||||
- 如果沒有參數,小括號可以省略,`[] () {...}` 可以簡寫成 `[] {...}`
|
||||
- 箭號(`->`)後面是回傳的型別,如果沒寫就由 `return` 自動推斷
|
||||
|
||||
將 Lamdba 運算式指定給變數:
|
||||
```cpp
|
||||
auto comapre = [] (int x, int y) -> bool {
|
||||
return x < y;
|
||||
};
|
||||
```
|
||||
|
||||
## Lamdba的擷取子句
|
||||
以中括號開頭的 *lamdba 導入器* 可以將外部的變數傳給 Lamdba 運算式,正式名稱是「擷取子句(capture clause)」。
|
||||
`[=]` 表示它們會以值擷取(captured by value)。Scope內的變數可以在 lamdba 內使用,但是不可以改變。
|
||||
`[&]` 表示它們會以參考擷取(captured by reference)。Scope內的變數可以在 lamdba 內使用,可以改變。
|
||||
|
||||
## 以值擷取(captured by value)
|
||||
假設有一段程式如下:
|
||||
```cpp
|
||||
void testLambda() {
|
||||
float notUsed = 1.0f;
|
||||
std::vector<int32_t> numlist{10, 20, 30, 50, 60};
|
||||
auto findInRange = [=](int32_t start, int32_t end) {
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
std::cout << "Result: " << findInRange(25, 35) << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
用`[=]`可以用來擷取 lamdba scope 範圍所及的變數,沒有在 Lamdba 運算式裡面被用到的變數就不會被擷取,例如 `float notUsed = 1.0f;`。
|
||||
另一個重點是:**被擷取的變數是不可以更改的**。例如,不能在 lambda 裡面這樣寫:
|
||||
```cpp
|
||||
auto findInRange = [=](int32_t start, int32_t end) {
|
||||
numlist.push_back(5); // ERROR!
|
||||
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
```
|
||||
|
||||
如果一定要在 lambda 內改變擷取的變數,那必須指名 lambda 為 `mutable`:
|
||||
```cpp
|
||||
auto findInRange = [=](int32_t start, int32_t end) mutable { // <-- assign mutable
|
||||
numlist.push_back(5);
|
||||
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
```
|
||||
|
||||
根據書上解釋 ,可以裡解為 compiler 會將 lamdba 編為一個 class,像是:
|
||||
```cpp
|
||||
class __Lambda8C1A5 {
|
||||
public:
|
||||
__Lambda8C1A5(const std::vector<int32_t>& arg1) : numlist(arg1) {}
|
||||
auto operator()(int32_t start, int32_t end) const { // const!
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int32_t> numlist;
|
||||
};
|
||||
```
|
||||
|
||||
這也解釋了 lamdba 的擷取範圍與原理。而 `mutable` 則是讓 `operator()` 不為 `const`,如下:
|
||||
```cpp
|
||||
auto findInRange = [=](int32_t start, int32_t end) mutable { // <-- assign mutable
|
||||
numlist.push_back(5);
|
||||
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
class __Lambda8C1A5 {
|
||||
public:
|
||||
__Lambda8C1A5(const std::vector<int32_t>& arg1) : numlist(arg1) {}
|
||||
auto operator()(int32_t start, int32_t end) { // No const here
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int32_t> numlist;
|
||||
};
|
||||
```
|
||||
|
||||
## 以值擷取特定的變數
|
||||
若只需要擷取特定的變數,那就直接在 lamdba 導入器(就是`[]`)寫入變數名稱,例如:
|
||||
```cpp
|
||||
int var1 = 10;
|
||||
int var2 = 20;
|
||||
int var3 = 30;
|
||||
|
||||
auto afunc = [var1, var2] () {
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## 以參考擷取(captured by reference)
|
||||
`[&]` 會擷取 scope 內的所有外部變數,而且可以修改:
|
||||
```cpp
|
||||
void testLambda() {
|
||||
float notUsed = 1.0f;
|
||||
std::vector<int32_t> numlist{ 10, 20, 30, 50, 60 };
|
||||
auto findInRange = [&](int32_t start, int32_t end) { // Use & here
|
||||
numlist.push_back(100); // OK
|
||||
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
std::cout << "Result: " << findInRange(25, 35) << "\n";
|
||||
std::cout << "numlist: ";
|
||||
for (auto n : numlist) {
|
||||
std::cout << n << " ";
|
||||
}
|
||||
std::cout << "\n"; // Output numlist: 10 20 30 50 60 100
|
||||
}
|
||||
```
|
||||
|
||||
## 以參考擷取特定的變數
|
||||
但是直接參考全部的外部變數不是好的作法,這讓你有機會做出一些意外的修改,所以請擷取有需要的變數就好:
|
||||
```cpp
|
||||
void testLambda() {
|
||||
float notUsed = 1.0f;
|
||||
std::vector<int32_t> numlist{ 10, 20, 30, 50, 60 };
|
||||
|
||||
auto findInRange = [&numlist](int32_t start, int32_t end) {
|
||||
numlist.push_back(100); // OK
|
||||
|
||||
for (auto num : numlist) {
|
||||
if (num >= start && num <= end) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
如果有多個變數需要擷取,那就用 `,` 分開:
|
||||
```cpp
|
||||
auto findInRange = [&numlist, &var1, &var2](int32_t start, int32_t end) {
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
## 混合擷取
|
||||
以值擷取跟參考擷取也可以寫在一起:
|
||||
```cpp
|
||||
auto findInRange = [=, &numlist](int32_t start, int32_t end) {
|
||||
...
|
||||
};
|
||||
```
|
||||
上面的例子中,`numlist` 會是參考擷取,其他的外部變數則是以值擷取。
|
||||
|
||||
或是:
|
||||
```cpp
|
||||
auto findInRange = [&, numlist](int32_t start, int32_t end) {
|
||||
...
|
||||
};
|
||||
```
|
||||
上面的例子中,`numlist` 會以值擷取,其他的外部變數則是參考擷取。
|
||||
|
||||
但是,如果已經使用了 `=` ,就不可以再以值擷取其他變數,像是 `[=, numlist]` 就是不合法的。
|
||||
反之,如果已經使用了 `&`,就不可以再參考擷取其他變數,像是 `[&, &var1]` 就是不合法的。
|
||||
|
||||
## 存取 class
|
||||
Lamdba 寫在 class 裡面的時候,不論[「以值擷取 」](#以值擷取captured-by-value)或是[「以參考擷取」](#以參考擷取captured-by-reference)都沒辦法傳遞成員變數(member variable),只能傳遞 `this`,透過 `this` 來存取成員變數。例:
|
||||
```cpp
|
||||
class BigBuffer {
|
||||
public:
|
||||
void modify(int x, int y, ...) {
|
||||
auto modifyBuffer = [this] () { // Use this
|
||||
if (buffer) { // equal to this->buffer
|
||||
// do something with buffer
|
||||
}
|
||||
};
|
||||
...
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t bufferSize = 0;
|
||||
std::unique_ptr<uint8_t[]> buffer = nullptr;
|
||||
};
|
||||
```
|
||||
130
content/posts/2019/2019-12-22_[C++ 筆記] 好用的 optional/index.md
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
slug: "[C++ 筆記] 好用的 std::optional"
|
||||
title: "[C++ 筆記] 好用的 std::optional"
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- c++
|
||||
categories:
|
||||
- Programming
|
||||
series:
|
||||
- C++ 筆記
|
||||
date: 2019-12-22T00:00:00
|
||||
lastmod: 2019-12-22T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
> since C++17
|
||||
|
||||
[`std::optional`](https://en.cppreference.com/w/cpp/utility/optional) 讓 function 的回傳值多了一個選擇:**有值**或是 `nullopt`。
|
||||
|
||||
<!--more-->
|
||||
|
||||
例如我們有一個 function 要從某個檔案讀值出來(或是去 DB 查一個東西之類的需求),就假設我們要讀一個 int,我們也許會這樣寫:
|
||||
```cpp
|
||||
int readData(std::string filePath) {
|
||||
...
|
||||
int data = readFromFile(...);
|
||||
...
|
||||
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
但如果要讀的檔案不存在呢?這時候要 return 什麼呢?所以這時候我們會改成這樣寫:
|
||||
```cpp
|
||||
bool readData(std::string filePath, int& readData) {
|
||||
FILE* file = fopen(...)
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
readData = readFromFile(...);
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
我們用一個回傳值代表檔案讀取失敗與否,要是檔案存在且開啟成功,那麼 `readData` 這個回傳值就代表我們讀到的值。要是回傳值是 `false`,那 `readData` 就沒有意義。
|
||||
但是這樣會讓傳入跟傳出的參數混在一起,第一時間也不容易直覺的分辨出來,像這種情況 Python 就清楚很多:
|
||||
```python
|
||||
def readData(filePath):
|
||||
data = None
|
||||
try:
|
||||
with open(filePath, "rb") as f:
|
||||
data = f.read(...)
|
||||
return data
|
||||
except FileNotFoundError as e:
|
||||
return None
|
||||
|
||||
value = readData("123.txt")
|
||||
if value:
|
||||
pass # Do something...
|
||||
```
|
||||
可以像 Python 那樣都從回傳值來判斷嗎? `std::optional` 可以達到這個目的:
|
||||
```cpp
|
||||
std::optional<int> readData(std::string filePath) {
|
||||
FILE* file = nullptr;
|
||||
fopen_s(&file, filePath.c_str(), "rb");
|
||||
if (!file) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int readData;
|
||||
fread(&readData, 1, sizeof(readData), file);
|
||||
fclose(file)
|
||||
|
||||
return readData;
|
||||
}
|
||||
|
||||
auto result = readData("123.txt");
|
||||
if (result) {
|
||||
auto value = result.value();
|
||||
// Use value here...
|
||||
}
|
||||
|
||||
// 或是這樣寫也可以
|
||||
if (result == std::nullopt) {
|
||||
// Error handle here
|
||||
} else {
|
||||
auto value = result.value();
|
||||
// Use value here...
|
||||
}
|
||||
```
|
||||
|
||||
雖然用起來沒辦法讓 C++ 像 Python 那麼簡單,但是 `std::optional` 確實讓整段 code 看起來更清楚了。
|
||||
除了 [`std::optional<T>::value()`](https://en.cppreference.com/w/cpp/utility/optional/value) 以外,還有其他的取值方法:
|
||||
```cpp
|
||||
std::optional<std::string> name;
|
||||
|
||||
// Some process...
|
||||
|
||||
if (name)
|
||||
{
|
||||
printf("name = %s\n", (*name).c_str());
|
||||
}
|
||||
```
|
||||
|
||||
如果 `std::optional` 包含的是 struct 或是 class,也可以用 `->` 來直接存取 member(或 member function):
|
||||
```cpp
|
||||
struct User {
|
||||
uint32_t id;
|
||||
std::string name;
|
||||
int32_t age;
|
||||
};
|
||||
|
||||
std::optional<User> user;
|
||||
|
||||
// Some process...
|
||||
|
||||
if (user) {
|
||||
printf("id = %d\n", user->id);
|
||||
printf("name = %s\n", user->name.c_str());
|
||||
printf("age = %d\n", user->age);
|
||||
}
|
||||
```
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,47 @@
|
||||
---
|
||||
slug: 用 oh-my-posh 美化 Git bash
|
||||
title: 用 oh-my-posh 美化 Git bash
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- terminal
|
||||
categories:
|
||||
- Tool setup
|
||||
series:
|
||||
- Windows
|
||||
date: 2022-06-14T00:00:00
|
||||
lastmod: 2022-06-14T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
讓 [Windows Terminal](https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701) 中的 Git bash 也可以美美的。
|
||||
|
||||
<!--more-->
|
||||
|
||||
## 步驟
|
||||
以下步驟都是在 [Windows Terminal](https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701) 中的 Git bash[^1] 執行。
|
||||
1. 先下載一個你喜歡的theme: [https://ohmyposh.dev/docs/themes](https://ohmyposh.dev/docs/themes)
|
||||
2. 下載並安裝字型:[Caskaydia Cove Nerd Font](https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/CascadiaCode.zip?WT.mc_id=-blog-scottha)
|
||||
3. Install OhMyPosh: `winget install JanDeDobbeleer.OhMyPosh`
|
||||
4. 建立並修改 `~/.profile`,然後加入
|
||||
`eval "$(oh-my-posh --init --shell bash --config ~/montys.omp.json)"`
|
||||
注意最後的 `montys.omp.json` 就是第一步下載的theme,這邊要改成你自己的路徑。
|
||||
5. 修改 Windows Terminal 的 `setting.json`,將字型改為 `CaskaydiaCove NF`
|
||||

|
||||
6. 重開 Windows Terminal
|
||||
|
||||
[^1]: 在 [Windows Terminal](https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701) 中設定 Git bash 可以參考:[Windows Terminal's 設定 Git Bash 和 SSH @ 傑克! 真是太神奇了! :: 痞客邦 ::](https://magicjackting.pixnet.net/blog/post/225162505-windows-terminal's-%E8%A8%AD%E5%AE%9A-git-bash-%E5%92%8C-ssh)
|
||||
|
||||
## 成果
|
||||

|
||||
|
||||
## 參考
|
||||
- [Oh My Posh](https://ohmyposh.dev/)
|
||||
- [How to make the ultimate Terminal Prompt on Windows 11 - This video is LONG and WORDY and DETAILED - YouTube](https://www.youtube.com/watch?v=VT2L1SXFq9U)
|
||||
- [My Ultimate PowerShell prompt with Oh My Posh and the Windows Terminal - Scott Hanselman's Blog](https://www.hanselman.com/blog/my-ultimate-powershell-prompt-with-oh-my-posh-and-the-windows-terminal)
|
||||
- [Windows-Terminal配置OhMyPosh来美化GitBash_偕臧x的博客-CSDN博客](https://blog.csdn.net/qq_33154343/article/details/120661945)
|
||||
BIN
content/posts/2023/2023-02-15_福岡 Nu Gundam/featured.jpg
Normal file
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 638 KiB |
|
After Width: | Height: | Size: 634 KiB |
|
After Width: | Height: | Size: 592 KiB |
25
content/posts/2023/2023-02-15_福岡 Nu Gundam/index.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
slug: 福岡 Nu Gundam
|
||||
title: 福岡 Nu Gundam
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- Gunpla
|
||||
categories:
|
||||
- Hobby
|
||||
series:
|
||||
- Scale Model
|
||||
date: 2023-02-15T00:00:00
|
||||
lastmod: 2023-02-15T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
繼台場 RX-78 與 Unicorn 之後,很開心終於收集到了 LaLaport 的 Nu Gundam,下一個目標,當然就是橫濱的 RX-78 F00 啦!
|
||||

|
||||

|
||||

|
||||
BIN
content/posts/2023/2023-04-08_Fujifilm X-T5 入手感想/featured.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
62
content/posts/2023/2023-04-08_Fujifilm X-T5 入手感想/index.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
slug: Fujifilm X-T5 入手感想
|
||||
title: Fujifilm X-T5 入手感想
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- photography
|
||||
- fujifilm
|
||||
- x-t5
|
||||
categories:
|
||||
- Hobby
|
||||
series: []
|
||||
date: 2023-04-08T00:00:00
|
||||
lastmod: 2023-04-08T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
買了 X-T5 也一陣子了,機器功能什麼的是很OK的,尤其跟前一台比起來,性能、感光度什麼的都是大幅度的進步,滿滿的升級感。
|
||||
|
||||
只是使用上還是不習慣,這一篇來講一下不習慣的點,還有解決方法。
|
||||
|
||||
<!--more-->
|
||||
|
||||
雖然 X-T5 的轉盤很漂亮很有型,但是要一直轉來轉去也是有點煩人,像是你將設定快門之後(快門先決),要改成A模式(光圈先決),先得把快門轉回A,然後再把光圈轉到你要的位置,如果這時候要回到P模式,對,快門轉盤跟光圈環都要調回A才行。也不是不好,就是有點煩人。
|
||||
|
||||
第二個不習慣的點是我還是習慣用觀景窗來取景,要調整轉盤的話,變成要將相機放下來,轉動轉盤,再把相機拿起來取景,尤其是ISO轉盤,它在左手邊,要由本來扶著鏡頭的左手來調整,一整個就很不順手。我還是希望可以在取景的時候,用右手就可以改變設定,不用把相機拿上拿下的。
|
||||
|
||||
另外一個問題是X-T5的握把實在太淺了,有夠難握,這時候就很懷念前一台那厚實的握把,可以牢牢握住的那種踏實感。
|
||||
|
||||
這邊紀錄一下這幾個問題的解決方法。
|
||||
|
||||
## 握把
|
||||
關於握把,是買[Smallrig 3870](https://www.smallrig.com/SmallRig-Retro-Cage-for-FUJIFILM-X-T5-3870.html),這個兔籠可以加深握把,而且也把快門鈕的位置往前移,但是它往前移的機構並沒有做的很好,必須按連桿中間部份才可以有效的對焦。看下面的影片,一開始按連桿的前面(也就是它原本預計要把快門前移的位置),你會發現按下去之後,連桿沒辦法順利的拉動原本的快門鈕,必須按中間一點的位置才行。
|
||||
|
||||
{{< video src="images/IMG_6330.mp4" type="video/mp4" preload="auto" >}}
|
||||
|
||||
雖然不完美,但是握把真的舒服太多了,所以這點瑕不掩瑜,是一個可以適應的小問題。
|
||||
|
||||
## 滾輪控制
|
||||
第二個問題是希望可以把光圈、快門、ISO的調整都改為用右手的前後滾輪來控制,這樣就可以在取景的時候直接調整了。要達到這目標有幾個地方要調整:
|
||||
1. 光圈環調成 "A"
|
||||
2. ISO 轉盤調成 "C"
|
||||
3. EV 轉盤調成 "C"
|
||||
4. 設定選單,進入「設定」頁面的「按鈕/轉盤設定」,將前轉盤的3個功能改為「F」、「EV」、「ISO」
|
||||
5. 同樣的選單位置,將後轉盤改為「S.S」
|
||||
|
||||
| | | |
|
||||
|:-------------------------:|:-------------------------:|:-------------------------:|
|
||||
|  |  |  |
|
||||
|
||||
這樣設定之後,後轉盤可以用來控制快門,前轉盤可以控制光圈、EV、ISO這3樣。
|
||||
現在相機會變為「P模式」,將快門轉盤轉到 "T" 之後就會變成「S模式」,這時候可以用後轉盤來調整快門。
|
||||
將快門轉盤調回 A,將前轉盤「向左轉」,這時候會出現藍色的光圈數值,這時候就是「A模式」了。
|
||||
按下前轉盤可以在光圈、EV、ISO之中切換。如果快門轉盤沒有調回 A,那麼就是「M模式」,總之一切都可以在這2個轉盤中搞定了。
|
||||
唯一還是需要用到轉盤的就是快門轉盤了,不過從A調到T只要2格,這不容易弄錯,很快就可以熟悉了。
|
||||
|
||||
後來我發現X-H2就是我想要的這種操作模式,如果這樣的設定還是用不習慣,看來只能......🤣。
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
slug: Dokcer Compose 遇到 Python 3.6 不再支援問題
|
||||
title: Dokcer Compose 遇到 Python 3.6 不再支援問題
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags: []
|
||||
categories: []
|
||||
series: []
|
||||
date: 2023-05-09T00:00:00
|
||||
lastmod: 2023-05-09T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
解決 `docker-compose` 警告 Python 3.6 不再支援問題。
|
||||
|
||||
<!--more-->
|
||||
|
||||
可能已經發生一時間了,但是最近才注意到 XD。
|
||||
|
||||
在使用 `docker-compose down` 或是 `docker-compose up`,都會出現下面這個訊息:
|
||||
|
||||
```bash
|
||||
/snap/docker/2746/lib/python3.6/site-packages/paramiko/transport.py:32: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography. The next release of cryptography (40.0) will be the last to support Python 3.6.
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
```
|
||||
|
||||
雖然已經確定使用 Python 3.10,查看 docker-compose 版本也確實發現它還是使用 Python 3.6:
|
||||
|
||||
```bash
|
||||
xxxx@xxxx:~/oooo/ooxx$ sudo docker-compose version
|
||||
/snap/docker/2746/lib/python3.6/site-packages/paramiko/transport.py:32: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography. The next release of cryptography (40.0) will be the last to support Python 3.6.
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
docker-compose version 1.29.2, build unknown
|
||||
docker-py version: 5.0.3
|
||||
CPython version: 3.6.9
|
||||
```
|
||||
|
||||
這是因為 `docker-compose` 這個命令已經過時了,請改用 `docker compose` 就可以了。<br>
|
||||
要是習慣改不過來,不妨就加個alias吧:
|
||||
|
||||
```bash
|
||||
alias docker-compose='docker compose'
|
||||
```
|
||||