Raspberry Pi 3BにMainsail OSをインストール
- ヤフオクで中古のRaspberry Pi 3B V1.2を購入
- Raspberry Pi Imagerから指定
- Mainsail OS 1.2.1 64-Bit
- Wifiドングルのドライバのインストール
- https://note.com/kf34/n/n7c2f9a3e9725
- この記事の内容に加えて
linux-kernel-headers
とbc
のインストールが必要だった- link
sudo apt install raspberrypi-kernel-headers
sudo apt install bc
- HDMI Screenのドライバのインストール
sudo git clone https://github.com/kedei/LCD_driver sudo chmod -R 777 LCD_driver/ cd LCD_driver/ sudo ./LCD35_hdmi
- klipper screenの導入
- https://picalittle.tech/klipper-screen_install/
- 再起動時に自動でKlipperScreenが表示されなかったので、下記サイトに記載の対応
- https://www.reddit.com/r/klippers/comments/zfjf8a/klipperscreen_does_not_boot_on_my_pi_screen/
systemctl status KlipperScreen
->(EE) xf86OpenConsole: Cannot open virtual console 2 (Permission d...
sudo bash -c "echo needs_root_rights=yes>>/etc/X11/Xwrapper.config"
4章 分割コンパイル、ビルド
4-1 識別子とC++のキーワード
- 識別子は、関数や型、変数といった名前に相当するもののこと
- 概ね下記のルール
- 大文字小文字は区別
- 数字で始めることはできない
- アンダースコアで始まっても良い
- いくつか使用できないキーワードがある
- 識別子の付け方の3例
- スネークケース: アンダースコアで単語区切りを示す。
my_test_function
- アッパーキャメル: 各単語の最初の文字を大文字にする。
MyTestFunction
- ロワーキャメル: 最初の文字だけ小文字に、残りは各単語の最初の文字を大文字にする。
myTestFunction
- スネークケース: アンダースコアで単語区切りを示す。
- Boostや標準ライブラリではスネークケースが、Qtでは両キャメルケースが使われる
- キーワードは変数、関数名などには使えない
true, if, int
など
- 文脈依存キーワードは変数、関数名などに使えるが、好ましくない
final, override
- 代替表現も変数、関数名などに使えるが、好ましくない
and, not, xor, or
など
- 暗黙的に定義される識別子は下記など
- デバッグに使える
識別子 | 型 | 内容 |
---|---|---|
__FILE__ |
文字列リテラル | ソースファイル名 |
__LINE__ |
数値リテラル | ソースコードの__LINE__ が出現した行番号 |
__func__ |
文字型の配列 | 関数名 |
__cplusplus |
数値リテラル | c++としてコンパイルされたときのバージョン |
4-2 宣言と定義
- 宣言とは、変数や関数、クラスなどのプログラムを構成する何かの存在を伝えるもの
return-type function-name(parameters, ...); //関数の宣言(プロトタイプ宣言) struct struct-name; class class-name; union union-name; enum class enum-name; enum class enum-bame : underlying-type;
- 関数を事前に宣言しておくことで、定義がプログラムの後ろのほうにあっても関数を呼び出すことができる
- プログラムの前の方で宣言することを前方宣言とよぶ
- 定義は関数やクタスの中身がどのようになっているかを記述すること
- インスタンス化にはそのクラスがどのくらいのサイズのメモリを使用するのか知る必要があるが、宣言だけでは分からない
- ポインタや参照はアドレス値のみなので、定義がなくても引数、戻り値にできる
- ただし、定義がないとメンバにアクセスできない
- ファイル分割を行う場合は、ヘッダファイルに宣言を、ソースファイルに定義を記述するといったテクニックが必要になる
- クラス定義中にメンバ関数の定義を記載することもできる
- クラス定義が長くなりやすいので、短い関数に限るべき
4-3 スコープ
- 変数の有効な範囲をスコープ、変数が有効になっている期間のことを生存期間という
- 関数定義の
{
から}
までを関数スコープという- 関数スコープの中で定義された変数と仮引数は関数から処理が戻るときに破棄される
- ブレースで囲めばスコープを作れる
- グローバルスコープは
main
関数が呼ばれる前から終了するまでglobal
変数はこのスコープが生存期間- 関数の外側で定義された変数
static
変数もプログラム終了するまで生存するglobal
変数もstatic
にできるが、その場合はファイルスコープな変数になる- つまり、そのソースファイルの中でしか使えない
static
変数は初期化することができるが、初期化はその宣言にたどり着いた一度目にしか行われないstatic
変数は寿命が長い以外はローカル変数と違いがない- つまり、宣言されたスコープでしか使用できない
- グローバル変数は、どこで使われているか見通しが立てづらいので、
static
変数でもうまく記述できない場合にのみ使うべき
- すでに破棄されてしまっている変数をさしたポインタをダングリングポインタとよぶ
4-4 初期化構文付き条件分岐
if (type_name var_name = init_value; condition) { ... } switch (type_name var_name = init_value; condition) { ... }
4-5 分割コンパイル
- 1ファイルの場合
- ソースファイルを実行形式ファイルに変換する手順をコンパイルと呼ぶ
- 複数ファイルの場合
extern
キーワードをつけると、実体を持たない変数を宣言できるglobal
変数を複数ソースやヘッダファイルで宣言すると、それぞれのオブジェクトで実体をもってしまう- グローバルスコープの変数でしか使えない
static
な変数なglobal
変数はそのソース内でしか使用できないのでextern
できない
static
変数はヘッダで定義してはいけない- ソースファイルごとに定義が行われてしまうから
extern type-name var-name; // in global scope
3章 クラス
3-1 const
メソッドについて
const
メソッドconst
変数- 書き換えができないメンバ変数
- メンバ初期化子、もしくはメンバ初期化リストでのみ初期化することができる
- https://wandbox.org/permlink/yIAc4PQpkqT6ji3p
3-2 コンストラクタ、デストラクタ
3-3 初期値を受け取るコンストラクタ
- 何も定義しないとデフォルトコンストラクタがコンパイラにより作成される
- コンストラクタ内で他のコンストラクタを呼び出す機能を移譲コンストラクタとよぶ
- 一般には、移譲先にコンストラクタの実装の大部分を任せる
- 移譲元ではメンバ変数の初期化も行えない
- 多くの場合はコンパイラが自動生成するコピーコンストラクタをそのまま使えば良い
- メンバ変数にポインタを持つ場合など、デフォルトのコピーコンストラクタだと、コピー先も同じオブジェクトを指し示すことになってしまう→デストラクタでの2重解放などを誘発
- そういう場合はユーザーが記述するコピーコンストラクタを自分で実装する
- https://wandbox.org/permlink/8Kh4ZxmMatTWr3ZB
- コピーコンストラクタがいつ呼ばれるのかについてはhttps://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/C++2/09/09-03.html
- 代入演算子と中括弧で暗黙のコンストラクタ呼び出しが行える
3-4 デフォルトの初期値
- クラス定義内でのメンバ変数の初期化の構文を非静的メンバ変数の初期化子(Non Static Data Member Initializer : NSDMI)と呼ぶ
- NSDMIと呼ばれることが多い
- 構文は下記
class A
{
type1 var1 = defVal1;
type2 var2 = {defVal2};
type1 var3(defVal3);
type1 var4{defVal4};
};
- メンバー初期化子リストも与えられた場合はメンバー初期化リストが優先される
3-5 継承
- ベースとなるクラスを基底クラス、基底クラスを継承したクラスを派生クラスとよぶ
- 「クラスAから派生したクラスBが、基底クラスの機能を継承する」といいた言い回し
- 構文は下記
class derived-class-name : access-specifier base-class-name { ... };
access-specifier
を省略するとprivate
になる- privateの場合、基底クラスのメンバに派生クラスからアクセスできなくなる
- 基底クラスのメンバ関数の処理内容を派生クラスで変更することができる
- 基底クラスのメンバ関数は
virtual
を指定し、仮想関数にすることで、派生クラスで処理内容の変更を許可する - 派生クラスでは
override
指定子を追加し、処理内容を書き換える- 派生クラスで何もoverrideしなければ、自動的に基底クラスの仮想関数を継承する
- overrideする場合は戻り値の型、関数名、
const
の有無なども一致させる必要がある - 実は基底クラスで
virtual
を指定しておけば、派生クラスのoverride
は省略できるが、わかりにくくなるので避ける
- 基底クラスのメンバ関数は
- 派生クラスで基底クラスと同名のメンバ関数を作成すると名前の隠蔽(name hiding)が生じる
- 派生クラスにオーバーライドを強制するメソッドを純粋仮想関数とよぶ
3-6 オブジェクトポインター
- 構造体と同じく、ポインタを経由してメンバにアクセスするときはアロー演算子
->
を使う - メンバ関数の仮引数とメンバ変数名が一致するとき、その変数名は仮引数として解釈される
- メンバ変数にアクセスするときには、
this
ポインタを経由してアクセスする this
ポインタは書き換え不可const
メンバ関数の中ではthis
ポインタはconstポインタとなる
- メンバ変数にアクセスするときには、
3-7 クラス、構造体、共用体の関係性
- 構造体
- クラスと構造体は、デフォルトのアクセス指定だけが異なる
- クラスは
private
、構造体はpublic
- 基底クラス(構造体)から派生するときのアクセス指定もクラスが
private
で構造体がpublic
- 他に違いは無いが、Cからの名残で単にデータの箱をつくるときは構造体を用いることが多い
- 共用体
- 無名共用体
class Hoge { public: union { int a; float b; } };
3-8 friend
関数
- メンバ関数でないにもかかわらず、
private
なメンバにアクセスできるような関数を用意したい場合は、フレンド関数を使用する- フレンド関数はクラス宣言内で
friend
をつけたものをプロトタイプ宣言する(フレンド宣言) - メンバ関数ではないので、引数としてクラスのポインタや参照を一つ以上受け取るのが一般的
- 複数クラスの非公開メンバにアクセスしたい場合は、複数クラスでフレンド宣言する
- 定義には
friend
は不要
- フレンド関数はクラス宣言内で
- フレンド宣言はクラスの外側では行えない
3-9 static
クラスメンバ
static
メンバ変数static
メンバ関数
class A { public: static int stVal; } ; int A::stVal = 3;
C++ 内容整理
背景
3年間機械系エンジニアとしてCAEに取り組んでいたが、一念発起してソフトウェアエンジニアに転身。 数年ぶりにC++を使い始めるも、業プロと趣味(?)プロのギャップに色々と苦労している。 入門書の内容を整理しながら内容を整理していく。
参考にする入門書
- 1章
- 2章
- 3章 クラス
- 4章 分割コンパイル、ビルド