C/C++ 一堆名詞解釋

紀錄一下 C/C++ 一堆名詞解釋。

覆載 override

就是子類覆蓋掉父類的實作叫覆載 override,就像例子中的bbb

1
2
3
4
5
6
7
8
9
class Base {
virtual void bbb () {}
}

class Derived : public Base {
virtual void bbb override () {
cout << "bbb" << endl;
}
}

多載 overload

就是相同函式名,但參數不一樣,叫多載 overload

1
2
void add(int a, int b);
void add(float a, float b);

其他雜類

繼承:inheritance
多型:polymorphism
建構子:constructor
複製建構子:copy constructor
解構子:destructor
函子/函式物件:functor
抽象類別:abstract class
父類:base class
子類:derived class

參考
C++ Glossary
https://gcallah.github.io/OOP2/glossary.html
C++ Glossary - Step by Step Programming
https://sites.google.com/site/simplestjava/c-glossary

其它相關文章推薦
C/C++ 新手入門教學懶人包
C++ virtual 的兩種用法
C++ 設計模式 - 單例模式 Singleton Pattern
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別
C++ std::sort 排序用法與範例完整介紹
std::queue 用法與範例
std::thread 用法與範例

Ubuntu apt update 的一堆 error 問題與解決方法

Ubuntu 久久用 apt update 很常遇到一些 error,這些問題長久累積起來已經可以寫成一篇問題集了,今天就順手記錄下來。

ubuntu apt update 遇到 NO_PUBKEY 的 GPG error 的解法

參考這篇
Linux APT 遇到 NO_PUBKEY 的 GPG error 解法 | Tsung’s Blog
https://blog.longwin.com.tw/2019/05/linux-apt-no-pubkey-gpg-error-fix-2019/

例如遇到下列錯誤訊息

1
NO_PUBKEY 9334A25F8507EFA5

就將該 KEY 透過下列指令直接重新取得

1
sudo apt-key adv --keyserver keys.gnupg.net --recv-keys 9334A25F8507EFA5

ubuntu apt update 遇到 The following signatures were invalid: KEYEXPIRED 1544811256

參考這篇
Fix Ubuntu/Debian apt-get “KEYEXPIRED: The following signatures were invalid”
https://futurestud.io/tutorials/fix-ubuntu-debian-apt-get-keyexpired-the-following-signatures-were-invalid

sudo apt-key list 找到過期的 key
再用下列指令去 renew

1
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys <KEY>`

但我曾經遇到 mogodb renew 後也沒變,索性只好移除 mogodb 的 ppa 了,

移除 ppa

這邊介紹怎麼移除 ppa,
參考這篇
Ubuntu Linux 新增與移除 PPA 個人套件庫指令教學 - G. T. Wang
https://blog.gtwang.org/linux/ubuntu-linux-add-and-remove-ppa-command-tutorial/

在 Ubuntu 作業系統下移除不要的 PPA 倉庫 - Clay-Technology World
https://clay-atlas.com/blog/2020/02/04/linux-ubuntu-chinese-note-remove-ppa/
講的很仔細,不錯

其它相關文章推薦
使用 Unetbootin 建立 Ubuntu Live USB 且可以儲存資料

Python OpenCV 圖片轉字元圖畫

本篇將介紹如何使用 OpenCV 與 Python 來將圖片轉換成字元畫,最後並將這些字元圖畫顯示在 console 終端機上或者輸出寫入到檔案裡,
在學影像處理時一些枯燥乏味的基礎理論若是能夠應用出來的話,更能加深學習的印象與提昇學習的意願,以下就來開始寫個影像處理的小專案吧!

本篇文章中將會學習到知識以及需要具備的基礎,

  • Python 基礎
  • OpenCV 函式庫的使用
  • argparse 函式庫的使用
  • Linux 命令列操作

小試身手. img2char 將圖片轉成字元圖畫

以下為 python 搭配 opencv 寫成的一個 img2char 小程式,能將一張圖片轉成字元圖畫輸出,

簡單介紹一下重點吧~
一開始使用 opencv 的 cv2.imread 將圖片讀進來並且轉成灰階格式,
接著使用 cv2.resize 縮放到指定的大小,
最後用迴圈遍歷整個影像,將每個灰階 pixel 轉成對應的 ascii 的字元,再將其結果輸出,
重點程式碼如下,完整程式碼分享在Github上,歡迎 fork 與支持點擊星星star~

img2char.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import cv2
import numpy as np

def gray2char(gray):
length = len(ascii_char)

unit = (256.0 + 1)/length
return ascii_char[int(gray/unit)]

if __name__ == '__main__':
img = cv2.imread(in_image_filename, 0)
img = cv2.resize(img, (width,height), interpolation=cv2.INTER_NEAREST)

txt = ""
for i in range(height):
for j in range(width):
txt += gray2char(img[i,j])
txt += '\n'
print(txt)

不想寫 code,那就直接使用 img2char 吧

img2char程式碼下載下來,首先假設你已經安裝好 python3 與 opencv 環境後,
img2char 的使用指令如下,
-W 是指定輸出的字元圖畫的寬度, -H 則是高度,預設會將字元圖畫輸出在 console 上,同時也會輸出到 output.txt 檔案裡

1
$ python3 img2char.py opencv-logo.png -W=60 -H=74

先看看原圖

再看看 img2char 執行後的結果輸出如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
                            ZZZ                            
ZZZZZZZZZZ
ZZZZZZZZZZZZZZ
ZZZZZZZZZZZZZZZZZ
ZZZZZZZZZZZZZZZZZZZZ
ZZZZZZZZZZZZZZZZZZZZZ
ZZZZZZZZZZZZZZZZZZZZZZZ
ZZZZZZZZZZZZZZZZZZZZZZZZ
ZZZZZZZZZZZZZZZZZZZZZZZZZ
ZZZZZZZZZZZ ZZZZZZZZZZZ
ZZZZZZZZZZ ZZZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZZ
ZZZZZZZZZZ ZZZZZZZZZZ
ZZZZZZZZZZZ ZZZZZZZZZZ
ZZZZZZZZZ ZZZZZZZZZ
ZZZZZZZZ ZZZZZZZZ
ZZZZZZZ ZZZZZZZ
ZZZZZ ZZZZZZ
ZZZZ ZZZZ
ZZ ZZ
Z Z

///////
////////////
////////////// MM M
/////////////// MMM MMM
/////////////// MMMMM MMMMM
//////////////// MMMMMM MMMMMM
//////////////// MMMMMMMM MMMMMMMM
///////////////// MMMMMMMM MMMMMMMMM
//////////////// MMMMMMMMMM MMMMMMMMMM
/////////// MMMMMMMMMM MMMMMMMMMM
////////// MMMMMMMMM MMMMMMMMMM
///////// MMMMMMMMM MMMMMMMMM
//////// MMMMMMMMM MMMMMMMMM
//////// MMMMMMMMM MMMMMMMM
//////// ///////// MMMMMMMMM MMMMMMMM
//////// ///////// MMMMMMMMM MMMMMMMMM
///////// //////// MMMMMMMMM MMMMMMMMM
///////// ///////// MMMMMMMMM MMMMMMMMMM
////////// ////////// MMMMMMMMMM MMMMMMMMMM
//////////// ///////////// MMMMMMMMMMMMMMMMMMMMMMMMMM
///////////////////////// MMMMMMMMMMMMMMMMMMMMMMMMM
//////////////////////// MMMMMMMMMMMMMMMMMMMMMMMM
/////////////////////// MMMMMMMMMMMMMMMMMMMMMM
///////////////////// MMMMMMMMMMMMMMMMMMMM
/////////////////// MMMMMMMMMMMMMMMMMM
//////////////// MMMMMMMMMMMMMMMM
///////////// MMMMMMMMMMMM
//////// MMMMMMMM



$$$$$$ $$$$$$ $$ $$
$$$$$$$$$ $$$$$$$$$ $$ $$
$$$ $$$ $$ $$ $$$ $$$
$$ $$ $$ $$ $$ $$$
$$ $$ $$$$$$$ $$$$$$ $$$$$$ $$ $$ $$
$$ $$ $$ $$ $$ $$ $$$ $$ $$ $$$ $$$
$$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$$
$$ $$ $$ $$ $$$$$$$ $$ $$ $$ $$ $$
$$ $$ $$ $$ $$ $$ $$ $$ $$ $$$ $$
$$$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$$$$
$$$$$$$$$$ $$$ $$$ $$$ $$$ $$ $$ $$$$$$$$$ $$$$
$$$$$$$$ $$$$$$ $$$$$ $$ $$ $$$$$$$ $$$$
$$
$$
$$

要看 img2char 指令說明可以後面接上 -h--help 參數,如下所示

1
$ python3 img2char.py -h

看起來效果相當地不錯!
如果Github的星星數破100的話,我再來寫個加強版與惡搞版XD~

其它相關文章推薦
Python OpenCV 彩色轉灰階(RGB/BGR to GRAY)
Python OpenCV resize 圖片縮放
Python OpenCV 影像平滑模糊化 blur
Python OpenCV 影像二值化 Image Thresholding

使用 Unetbootin 建立 Ubuntu Live USB 且可以儲存資料

本篇介紹在 Ubuntu 下使用 Unetbootin 建立 Ubuntu Live USB 且可以儲存資料,一旦建立好這個 Live USB 就可以拿到任何一台電腦上執行裡面的作業系統,
而且所做的修改與新增的檔案都能保存下來,是個很方便的工具技巧,馬上就來開始準備吧!

首先你需要一個容量夠大的 USB,起碼要有4G,以及 Ubuntu 的映像檔,可以到官網下載

再來就是使用 Unetbootin 這個軟體了,還沒安裝的可以透過下列指令安裝,
已經安裝過的,久久沒用 Unetbootin 也需要更新一下,以確保可能有支援到最新的 Ubuntu 版本,也是透過下列指令,

1
2
sudo apt update
sudo apt install unetbootin

通常我會先將 USB 重新格式化過成 FAT32,即使裡面有前一次的 Ubuntu Live 檔案,
記得在空間填入,我都填 4096(單位MB)

以下為我測試過可以用的版本

1
2
3
Ubuntu 20.04.1 ?
Ubuntu 18.04.5 O
Ubuntu 16.04 ?

參考
Ubuntu Live USB With Persistent Data
https://youtu.be/jGthtXxW8nk
Creating a persistent live USB using Unetbootin - Tips, Tricks and Tutorials / Tried & Tested - Ubuntu MATE Community
https://ubuntu-mate.community/t/creating-a-persistent-live-usb-using-unetbootin/20105

其它相關文章推薦
Ubuntu 安裝 TP-Link Archer T4U Driver 筆記
Ubuntu apt update 的一堆 error 問題與解決方法

C/C++ 取得 shell 指令的螢幕輸出字串

本篇介紹在 C/C++ 程式用 popen 來取得外部執行 shell command 指令結果(螢幕輸出字串),例如在程式裡執行 ls 指令,那我們怎麼透過 popen 獲得 ls 指令的輸出結果,請參考下面內容。

同樣是在程式裡執行 ls,來看看這幾個方式有什麼不同,
使用 system 來執行會等待system執行完(blocking),就接著往下執行了,
使用 popen 來執行不會等待執行完(non-blocking),才接著往下執行,
但是如果要讀取 popen 的執行結果就必須等待執行完成(blocking),才接著往下執行,
要記得這幾個之前差異,在使用上才能依情境挑選適合的 api 來使用。

範例. 用 popen 取得外部執行的結果

以下範例為執行一個外部指令 ls 並取讀取 ls 的輸出,將其輸出用 cout 印出來,

使用 popen 去建立一個管道 pipe,透過 fork 產生新的子行程,然後執行 ls 指令,
popen 回傳的是 FILE 類型,可以用讀取文件的方式去操作它,最後不使用時要 pclose,

其中 fgets 是每次讀取一行,也就是讀取到’\n’換行字元就會返回,
也可以換成 fread,視不同情形而定,
不斷地將讀取到的內容疊加在 result 裡最後返回,

cpp-get-exec-cmd-output.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// g++ cpp-get-exec-cmd-output.cpp -o a.out -std=c++11 -pthread
#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>

using namespace std;

bool shellCmd(const string &cmd, string &result) {
char buffer[512];
result = "";

// Open pipe to file
FILE* pipe = popen(cmd.c_str(), "r");
if (!pipe) {
return false;
}

// read till end of process:
while (!feof(pipe)) {
// use buffer to read and add to result
if (fgets(buffer, sizeof(buffer), pipe) != NULL)
result += buffer;
}

pclose(pipe);
return true;
}

int main() {
string result;
shellCmd("ls", result);
cout << result;

return 0;
}

有一定經驗的人都知道 popenpclose 只有在 unix 系統下有,在 windows 下是沒有 popenpclose,但是有對應的函式,
popenpclose 在 Windows 裡對應的函式為 _popen_pclose, 詳情請看微軟官方文件

使用 C++11 的寫法

cpp-get-exec-cmd-output-cpp11.cpp
1
TBD

進階. 寫個像 tail 或 tee 指令的程式

tail -f指令可以持續監控對某個檔案,該檔案一旦有新增便會將內容印出來,
tee 指令是將某指令的標準輸出重新導到檔案也順便導到標準輸出,

其訣竅在持續讀取,

cpp-get-exec-cmd-output2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// g++ cpp-get-exec-cmd-output2.cpp -o a.out -std=c++11 -pthread
#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>
#include <thread>

using namespace std;

bool stop = false;

bool shellCmd(const string &cmd) {
char buffer[512];

// Open pipe to file
FILE* pipe = popen(cmd.c_str(), "r");
if (!pipe) {
return false;
}

// read till end of process:
while (!feof(pipe) || stop) {
// use buffer to read and add to result
if (fgets(buffer, sizeof(buffer), pipe) != NULL)
cout << buffer;
}

pclose(pipe);
return true;
}

int main() {
thread th1 = thread(shellCmd, "tail -f /var/log/syslog");

std::this_thread::sleep_for(std::chrono::seconds(10));
stop = true;
th1.join();

return 0;
}

不過在結束時可能會卡在 plcose 結束不了有點小缺陷,之後有時間再把它改寫吧~

參考
[1] process - How do I execute a command and get the output of the command within C++ using POSIX? - Stack Overflow
https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
[2] How to execute a command and get the output of command within C++ using POSIX?
https://www.tutorialspoint.com/How-to-execute-a-command-and-get-the-output-of-command-within-Cplusplus-using-POSIX
有效
[3] c++11 - How to execute a command and get return code stdout and stderr of command in C++ - Stack Overflow
https://stackoverflow.com/questions/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-c

其它相關文章推薦
C/C++ 新手入門教學懶人包
C++ std::sort 排序用法與範例完整介紹
std::queue 用法與範例
std::thread 用法與範例
C++ virtual 的兩種用法
C/C++ 判斷檔案是否存在
C++ 設計模式 - 單例模式 Singleton Pattern
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別

C/C++ 字串反轉 reverse

今天 ShengYu 要介紹的是如何將 C/C++ 字串反轉 reverse,算是個很常考的考題,熟悉以後也可以將 vector 容器內容元素進行反轉。

自己動手寫一個

字串反轉其實就是用了 swap 的技巧來達成,用迴圈將第一個跟最後一個交換,
接著將第二個跟倒數第二個交換,依此類推,所以可以寫成這樣,

string-reverse.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// g++ string-reverse.cpp -o a.out
#include <iostream>
#include <string>
using namespace std;

void swap(char *x, char *y) {
char tmp = *x;
*x = *y;
*y = tmp;
}

void reverse(char *first, char *last) {
--last;
while (first < last) {
swap(first, last);
++first;
--last;
}
}

int main() {
char str[] = "Hello World";
reverse(&str[0], &str[11]);
cout << str << endl;

/*std::string str2 = "Hello World";
reverse(str2.begin(), str2.end());
cout << str2 << endl;*/

return 0;
}

輸出如下

1
dlroW olleH

經過樣板修改後,就可以處理任意型態的反轉了,

string-reverse-template.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// g++ string-reverse-template.cpp -o a.out
#include <iostream>
#include <string>
using namespace std;

template <typename T>
void swap(T *x, T *y) {
T tmp = *x;
*x = *y;
*y = tmp;
}

template <typename T>
void reverse(T *first, T *last) {
--last;
while (first < last) {
swap(first, last);
++first;
--last;
}
}

int main() {
char str[] = "Hello World";
reverse(&str[0], &str[11]);
cout << str << endl;

int arr[] = {4, 5, 6, 7};
reverse(&arr[0], &arr[4]);
for(auto i : arr)
std::cout << i;
std::cout << '\n';

/* not work
std::string str2 = "Hello World";
reverse(str2.begin(), str2.end());
cout << str2 << endl;*/

return 0;
}

輸出如下

1
2
dlroW olleH
7654

使用 std::reverse

STL 已經有提供 reverse 函式可以使用了,C++11 才加入的,只需 #include <algorithm> 並且加入 -std=c++11 編譯選項就可使用,使用的範例如下,

std-reverse.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// g++ std-reverse.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main() {
char str[] = "Hello World";
std::reverse(&str[0], &str[11]);
// or
//std::reverse(&str[0], &str[sizeof(str)-1]);
cout << str << endl;

std::string str2 = "Hello World";
std::reverse(str2.begin(), str2.end());
cout << str2 << endl;

int arr[] = {4, 5, 6, 7};
std::reverse(std::begin(arr), std::end(arr));
for(auto i : arr)
std::cout << i;
std::cout << '\n';

std::vector<int> vec{1, 2, 3};
std::reverse(std::begin(vec), std::end(vec));
for (auto i : vec)
std::cout << i;
std::cout << '\n';

return 0;
}

輸出如下

1
2
3
4
dlroW olleH
dlroW olleH
7654
321

以上就是 C/C++ 字串反轉 reverse 的介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

參考
std::reverse - cppreference.com
https://en.cppreference.com/w/cpp/algorithm/reverse
reverse - C++ Reference
http://www.cplusplus.com/reference/algorithm/reverse/

其它相關文章推薦
C/C++ 新手入門教學懶人包
C++ std::sort 排序用法與範例完整介紹
std::queue 用法與範例
C++ virtual 的兩種用法
C/C++ 判斷檔案是否存在
C/C++ 取得 shell 指令的螢幕輸出字串
C++ 設計模式 - 單例模式 Singleton Pattern
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別

C++ virtual 的兩種用法

C++ 的 virtual 關鍵字分成虛擬函式(virtual function)與純虛函式(pure virtual function)兩種用法,以下將說明這兩種的差異與使用時機。

虛擬函式 virtual function

C++ virtual 關鍵字第一種用法為 virtual function,中文翻譯為虛擬函式,
不強制子類一定要實作,子類不實作的話會以父類的實作為主,子類實作的話會以子類的實作為主,
另外子類在覆載 override 父類時,加上 override 關鍵字是個好習慣,使用 C++11 時編譯器在編譯階段可確保子類的函式的覆寫 override 是否成功,
使用時機:在子類不覆寫 override 時,有個預設實作(父類的實作)。

cpp-virtual.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// g++ cpp-virtual.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Base {
public:
virtual void func1() {
cout << "Base func1\n";
}
};

class Derived : public Base {
public:
virtual void func1() override {
cout << "Derived func1\n";
}
};

int main() {
Base b;
b.func1();
Derived d;
d.func1();
Base *d2 = new Derived();
d2->func1();
return 0;
}

輸出如下:

1
2
3
Base func1
Derived func1
Derived func1

這邊另外舉個多型 polymorphism 的例子,有個父類叫 Animal 類別並且提供了 eat() 的預設實作內容,Dog 與 Cat 分別都繼承 Animal 類別,但各自的 eat() 內容都不一樣,

cpp-virtual2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// g++ cpp-virtual2.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Animal {
public:
virtual void eat() {
cout << "I eat food\n";
}
};

class Dog : public Animal {
public:
virtual void eat() override {
cout << "I eat meat\n";
}
};

class Cat : public Animal {
public:
virtual void eat() override {
cout << "I eat fish\n";
}
};

class Unknown : public Animal {
};

int main() {
Animal* animal;
Dog dog;
Cat cat;
Unknown u;
animal = &dog;
animal->eat();

animal = &cat;
animal->eat();

animal = &u;
animal->eat();
return 0;
}

輸出如下,根據多型的特性,使得我們可以在 runtime 程式執行期間再依據各種情形去選擇我們要用哪種子類,就像例子中的 dog 與 cat,如果有個新類別 Unknown 繼承了 Animal 類別沒有實作 eat() 時也能由父類的預設實作來代替,

1
2
3
I eat meat
I eat fish
I eat food

純虛函式 pure virtual function

C++ virtual 關鍵字第二種用法為 pure virtual function,中文翻譯為純虛函式,
如下例子,Base 類別的 func1 為純虛函式,純虛函式寫法為 virtual void func1() = 0;,然後沒有實作,
Base 是抽象類別不能被實體化,
強制子類一定要覆寫實作,在某種情況下有這種設計需求。
子類實作的話當然就會以子類的實作為主。
使用時機:設計一些介面 interface 時,強制別人一定要這樣實作。

cpp-virtual3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// g++ cpp-virtual3.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Base {
public:
virtual void func1() = 0;
};

class Derived : public Base {
public:
virtual void func1() override {
cout << "Derived func1\n";
}
};

int main() {
//Base b; // error: cannot declare variable ‘b’ to be of abstract type ‘Base’
Derived d;
d.func1();
Base *d2 = new Derived();
d2->func1();
return 0;
}

這邊舉個例子,假設今天我就是要規定繼承 Animal 的子類都一樣要給我實作 eat() ,那麼我就可以這樣寫,把 Animal 類別的 eat 改成純虛函式 pure virtual function,這樣每個繼承 Animal 的子類都必須要實做 eat(),假如有個新的 Unknown 類別繼承 Animal 類別但沒有實作 eat() 的話,就會收到編譯器大哥的編譯錯誤訊息,

cpp-virtual4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// g++ cpp-virtual4.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Animal {
public:
virtual void eat() = 0;
};

class Dog : public Animal {
public:
virtual void eat() override {
cout << "I eat meat\n";
}
};

class Cat : public Animal {
public:
virtual void eat() override {
cout << "I eat fish\n";
}
};

class Unknown : public Animal {
};

int main() {
Animal* animal;
Dog dog;
Cat cat;
//Unknown u; // error: cannot declare variable ‘u’ to be of abstract type ‘Unknown’
animal = &dog;
animal->eat();

animal = &cat;
animal->eat();
return 0;
}

以上就是 C++ virtual 的兩種用法介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

參考
虛擬函式 - 維基百科,自由的百科全書
https://zh.m.wikipedia.org/zh-tw/%E8%99%9A%E5%87%BD%E6%95%B0
虛擬函式 | Microsoft Docs
https://docs.microsoft.com/zh-tw/cpp/cpp/virtual-functions?view=vs-2019
虛擬函式
https://openhome.cc/Gossip/CppGossip/VirtualFunction.html
純虛擬函式(一)
https://openhome.cc/Gossip/CppGossip/PureVirtualFunction.html
純虛擬函式(二)
https://openhome.cc/Gossip/CppGossip/PureVirtualFunction2.html
C++中virtual(虚函数)的用法_foreverhuylee的专栏-CSDN博客
https://blog.csdn.net/foreverhuylee/article/details/34107615
比較安全的 C++ 虛擬函式寫法:C++11 override 與 final – Heresy’s Space
https://kheresy.wordpress.com/2014/10/03/override-and-final-in-cpp-11/

其它相關文章推薦
C/C++ 新手入門教學懶人包
C++ 設計模式 - 單例模式 Singleton Pattern
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別
C++ std::sort 排序用法與範例完整介紹
std::queue 用法與範例
std::thread 用法與範例
C/C++ 一堆名詞解釋

Visual Studio Code 常用快捷鍵

本篇記錄一下 Visual Studio Code 常用快捷鍵,特別是在用 vscode trace code 時,常常需要跳至定義處 go to definition,善用 VS Code 快捷鍵會快很多,效率大幅提升。

名稱 Windows macOS Linux 備註
跳至定義處 (Go to Definition) F12 F12 F12
返回 (Go Back/Navigate Back) Alt+ Ctrl+- Ctrl+Alt+- (不是數字鍵盤的減號)
前進 (Go Forward/Navigate Forward) Alt + Ctrl+Shift+- Ctrl+Shift+- (不是數字鍵盤的減號)
Find All References Alt+Shift+F12 Option+Shift+F12 Alt+Shift+F12
左右括號之間跳轉 (Go to Bracket) Ctrl+Shift+\ Cmd+Shift+\ Ctrl+Shift+\
顯示搜尋 (Show Search) Ctrl+Shift+F Cmd+Shift+F Ctrl+Shift+F

顯示所有指令(Show All Commands) Ctrl+Shift+p

Find All References 在 Visual Studio Code 1.27.x cpptool 0.18.1 有問題, 貌似 1.36.x 才能用, 我用 Visual Studio Code 1.49.2 搭配 cpptool 1.0.1 確定可用。

透過下列方式可以搜尋查詢更多的快捷鍵或更換,

1
Visual Studio Code 左下角的 管理/Manage > 鍵盤快捷方式/Keyboard Shortcuts

參考
Go to definition and back to reference in visual Studio Code - Stack Overflow
https://stackoverflow.com/questions/42492925/go-to-definition-and-back-to-reference-in-visual-studio-code
Visual Studio Code Key Bindings
https://code.visualstudio.com/docs/getstarted/keybindings
官方 Visual Studio Code 快速鍵一覽表
https://blog.poychang.net/vscode-shortcuts/
VS Code 快捷鍵(中英文對照版)_weixin_34358365的博客-CSDN博客
https://blog.csdn.net/weixin_34358365/article/details/89133959
VSCode 返回上一个光标 (上一个浏览位置)_还是上会网吧~-CSDN博客
https://blog.csdn.net/M_N_N/article/details/84581840

其它相關文章推薦
Visual Studio Code 安裝 C/C++11 環境教學
VS Code 新增 C++ 標頭檔路徑
C/C++ clang-format 對程式碼風格排版格式化
Qt Creator 常用快捷鍵
vim 常用快捷鍵

Visual Studio Code 找對應大括號的快捷鍵

先把游標移到大括號右邊,再按 Ctrl + Shift + \ 就可以跳到另一個大括號了。

參考
How do I jump to a closing bracket in Visual Studio Code? - Stack Overflow
https://stackoverflow.com/questions/30097634/how-do-i-jump-to-a-closing-bracket-in-visual-studio-code

相關主題
Visual Studio Code 常用快捷鍵
Eclipse 常用快捷鍵
Eclipse 找對應大括號的快捷鍵
Qt Creator 常用快捷鍵

Linux tmux 終端機管理工具用法

本篇將介紹如何使用 Linux 下的 tmux 終端機管理工具,

分割視窗

左右垂直分割:Ctrl+b %
上下水平分割:Ctrl+b "
分割完後要切換分割視窗要按 Ctrl+b <方向鍵上/下/左/右>

其他參考
tmux 快速入門筆記 | DEVLOG of andyyou
https://andyyou.github.io/2017/11/27/tmux-notes/
tmux:管理終端 Session、分割視窗的神器
https://noob.tw/tmux/

其它相關文章推薦
Linux 常用指令教學懶人包
Linux sed 字串取代用法與範例
Linux find 尋找檔案/尋找資料夾用法與範例
Linux cut 字串處理用法與範例
Linux tail 持續監看檔案輸出用法與範例
Linux grep/ack/ag 搜尋字串用法與範例
Linux tee 同時螢幕標準輸出和輸出到檔案用法與範例
Linux xargs 參數列表轉換用法與範例
Linux du 查詢硬碟剩餘空間/資料夾容量用法與範例
Linux wget 下載檔案用法與範例