cpp-module
c++におけるクラス
C++におけるインスタンス作成する時のコンストラクタの挙動は、以下のようになる。
1.オブジェクトをインスタンス化する。
2.クラスが持つメンバ変数のコンストラクタが起動し、中に書かれている処理が実行される。
3.コンストラクタの内容が実行される。
C++において組み込み型、いわゆるintとかstringなど既に定義済みの型においてはそれぞれコンストラクタが存在する。
C++のコンストラクタの挙動において2がそれにあたり、コンストラクタの内容、つまりコンストラクタ関数の中身が実行される前に、メンバ変数のコンストラクタが起動する。
なのでメンバ変数を初期化したいときに、メンバ変数のイニシャライザを使わずにコンストラクタの処理として各メンバ変数に代入を行うと、2で0初期化し3で代入をするという、余計なことをしていることになるので、なるべくメンバイニシャライザを用いてメンバ変数の初期化を行った方がよい
ちなみに速度に違いはこのようになっている
for it みたいなやつは範囲for文といって、指定した値からスタートして, first/secondのような形でイテレーションを回すことが可能になる。
配列のオブジェクトをデストラクトする方法
delete []変数名
参照
C++における新しい参照演算子&の使い方。
関数などに渡すことでオブジェクトの実態そのものを渡す事ができる。
つまりポインタと同じような使い方をすることができる。
ただし、ポインタは値のアドレスを指すのに対し、参照は値そのものを指す。
そのためあるクラスの関数を別の関数で使用するときに、ポインタで渡すのであれば、アロー演算子で操作するのに対し、参照渡しではピリオド、つまりローカル変数と同じ使い方のような対応をする。
参考
https://programming.pc-note.net/cpp/reference.html#:~:text=参照は何かしらのオブジェクト,こと)%E3%82%92%E6%8C%87%E3%81%99%E3%82%82%E3%81%AE%E3%81%A7%E3%81%99%E3%80%82&text=%E5%A4%89%E6%95%B0%E5%90%8D%E3%81%AE%E9%A0%AD%E3%81%AB,%E3%81%84%E3%82%8B%E5%A4%89%E6%95%B0%E3%81%8C%E5%8F%82%E7%85%A7%E3%81%A7%E3%81%99%E3%80%82&text=%26%E8%A8%98%E5%8F%B7%E3%81%AF%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E3%81%AE%E9%A0%85,%E5%88%9D%E6%9C%9F%E5%8C%96)%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82
関数の後ろにconstをおくことにより、その関数がメンバ変数を修正できないことを保証する
コピーコンストラクタ
代入演算子との違い
コピーコンストラクタは初期化時に呼ばれる。
呼び方はA a = A(b); or A a = b;のどちらでも良い。つまり初期化的なものでも代入的なものでもどちらでもコピーコンストラクタが呼ばれる。初期化の場合は。
しかし、一度初期化した値に対して、代入をすると代入演算子オーバーロードが呼ばれる。
cosst objectを生成したらconst 関数じゃないと呼び出せない
実は派生クラスではoverrideやvirtualを記述しなくてもオーバーライドは可能です。
しかしこれらのキーワードがないと、シグネチャ(引数などの定義)が異なる場合にオーバーライドにならず、新しい関数を定義したことになってしまいます。
これらのキーワードを付けていれば記述ミスなどがあればキチンとエラーになってくれますから、バグの混入を防ぐことができます。
クラステンプレートの書き方
pedanticオプション
厳密なANSI CおよびISO C++により要求される警告をすべて出力します.禁止されている拡張機能を使うプログラムをすべて拒絶します.正当なANSI CプログラムおよびISO C++プログラムであれば,このオプションの指定の有無にかかわらず正しくコンパイルされるはずです.しかし,このオプションが指定されないと,特定のGNU拡張機能もまたサポートされることになります.
npos findの結果が-1だったときに返す値 。実値としては-1を取る
継承修飾子
cppにおいて class Hogehoge : 継承修飾子
Fugafugaとしたときに、継承修飾子によって継承されたメンバ関数、メンバ変数の使い方が変わってくる。
- public・・・基底クラスで設定したアクセス修飾子の設定をそのまま引き継ぐ
- protected・・・基底クラスでpublicだったものを、protectedにして引き継ぐ。他はそのまま。
- private・・・基底クラスのメンバを全てprivateで引き継ぐ。
参考
wshadowオプション
別の名前空間で同じ変数名の変数を初期化したときにコンパイルエラーとするフラグ。
同名変数初期化はコーダーが意図しない初期化などがされるケースがあり、避けるべきである
ex)
int a = 5;
{
int a = 2;
std::cout << "a: " << a << std::endl;
}
std::cout << "a: " << a << std::endl;
virtual func const = 0;の仕組み
const = 0をメンバ関数に一つでも含む場合、そのクラスは抽象クラスになる。
コピーコンストラクタが実行される条件
- インスタンスに代入したとき
Dog a;
Dog b = a;
- 関数に値渡しをしたとき
#include <iostream>
class Cat
{
private:
public:
Cat(){}
~Cat(){}
Cat(const Cat &other)
{
std::cout << "cp kita" << std::endl;
}
Cat &operator=(const Cat &other);
void makeSound(Cat hi){
std::cout << "meow" << std::endl;
}
};
int main() {
Cat a;
a.makeSound(a);
}
cp kita
meow
- インスタンス作成の引数に指定したとき
#include <iostream>
class Cat
{
private:
public:
Cat(){}
~Cat(){}
Cat(const Cat &other)
{
std::cout << "cp kita" << std::endl;
}
Cat &operator=(const Cat &other);
void makeSound(Cat hi){
std::cout << "meow" << std::endl;
}
};
int main() {
Cat a;
Cat b(a);
}
- 戻り値にインスタンスを指定したとき
#include <iostream>
class Cat
{
private:
public:
Cat(){}
~Cat(){}
Cat(const Cat &other)
{
std::cout << "cp kita" << std::endl;
}
Cat &operator=(const Cat &other);
Cat getCat(Cat *hi){
return *hi;
}
};
int main() {
Cat a;
a.getCat(&a);
}
cp kita
逆にコピーコンストラクタが呼ばれずに、Assigned Operatorのみがよばれているケース
Cat a;
Cat b;
b = a;
ポイントは**コンストラクタは一回しかよばれないという点。
デフォルトのコンストラクタが呼ばれた場合は、コピーコンストラクタはもうよばれない。
inf, -inf, NaN
少数を表現するfloat, doubleにおけるオーバーフローはinf, -infで表現する
NaNは0で割ったりするときの、値ではない(Not a number)を表現する
キャスト
static_cast: 基本的なキャスト
dynamic_cast: 派生元→派生先にキャストするときに使用する
const_cast: const, volatileを外したいときに使用する
reinterpreter_cast: ポインタ型を他のポインタに変換する。数値をポインタ型にしたいときも有効
reinterpreter_castは型安全かどうかを考慮しないので、最も非安全である。使用しなくていいのであれば他のキャストを使うべきである
isstringsteam 使えそうで使えない関数
getterのconst参照渡しはやめたほうが良いらしい
以下の文章を見る感じconstには穴があって工夫とconstの定義の仕方によっては穴をつくようなプログラミングができる。
そのためprivate変数の中身が書き換わってしまう場合が存在する。
本当にメモリの容量が少ないハードのときに改めて考えるべきである。
クラスの判定dynamic_castとstatic_cast
findでautoを使わないときは
typename T::iteratorと記述すればいけた。なんで?
上記の方法では、値が全て同じ場合でないと初期化できない。
「std::vector<型> オブジェクト名(型* first, 型* last);」と記述すると、first から last が指す先までのデータで動的配列を初期化する。 厳密に言うと、last は最後の元データの次を指す。[first, last) の範囲を元に、動的配列を初期化する。
通常配列でデータを指定し、それを元に動的配列を構築し初期化出来る。
resizeはvector.size()の戻り値を変更する。その際に値が入っていない場所は初期値埋めをする。
reserveはvector.capacity()の戻り値を変更する。初期値埋めはしない。
vectorは初期化のときに要素数を指定することにより動的メモリを予め確保することができる。
しかし要素数を指定していないvectorに対してもpush_backなどで要素を追加するときに、動的メモリを確保することができる。
つまりreserveや要素数の指定はあくまで処理速度の優先度を変えただけ。
getterを参照にする場合、swapなどの関数を外部から呼び出したとき、private変数の値が書き換わってしまう。その場合private変数が外部から変更を加えられてしまうのはまずいので、constなどをprivate変数に入れる必要がある。
逆にそうすると今度はsetterで値が入れられなくなる。(constは再代入できない)
以下のようなconstructorの呼び出しのときに設定できるようになる
c++におけるgetterのベストプラクティスは何なのだろうか
getterで扱うときに通常→constに変換してあげる
const std::string &GetData() const;