LoginSignup
1
1

More than 5 years have passed since last update.

はじめてのC++【27日目】

Posted at

はじめに

今日は、新しい単元に進まず、復習と再確認をやっていこうと思う。てか、やらなきゃ私の頭がパンクする。
23日目あたりから。

uniform initialization

uniform initializationとは

「一様初期化 (uniform initialization)」は、コンストラクタの呼び出しを、リスト初期化と合わせて波カッコ { }で記述する構文である。
C++日本語リファレンス

std::initializer_list

初期化子リストという。
動的配列std::vectorを初期化するときはこのようにする。

std::vector<int> a{}; 

最初から箱を作っておくための初期化方法は

std::vector<int> a(10);//箱を10個作って初期化

また、数字をもとから入れて初期化する方法は

std::vector <int> a {5,2,7,8,3};

参照

char型

const char*の意味

const char* の変数は、定数データへのポインタです。中身の変更不可、アドレスの変更可です。

char* constの意味

char* const の変数は、定数ポインタです。中身の変更可、アドレスの変更不可です。

const char* const

const char* const の変数は、定数データへの定数ポインタです。中身の変更不可、アドレスの変更不可です。
参照
とてもわかりやすいサイトでした。

int const * const * const ** const * p;

以前の記事(23日目)でコメントで書いてくださったコードなんですが、
int* constint const*は同じ意味で、「アドレスは変えられないが、中身は変えられるということ」
なので、int***** const const const const pになる?
たぶん、意味は、アドレスは変えられないが中身は変えらる」。

見える 見えない

スコープ解決演算子

クラス名::メンバ名でメンバを呼べるのは、なんらかの基底クラスを継承したクラスで、明示的に基底クラスや派生クラスのメンバにアクセスする場合(オーバーライドされているかどうかは関係ない。自分自身のメンバもこの方法で呼べる)と、そのメンバが静的メンバであった場合のみである。
静的メンバ変数とは

これまで、クラス型のオブジェクトには全てのメンバが割り当てられました。
オブジェクトごとにメンバ変数が割り当てられ、メンバ変数はオブジェクト間で関係ありません。
しかし、あるクラスの全てのオブジェクトが、一つの変数を共有したいとすれば、困ります。
グローバル変数を使うこともできますが、それはオブジェクト指向プログラムに反した行為です。
そのような場合は、静的メンバ変数を用います。
静的メンバ変数は、全てのオブジェクトが共有するクラス内グローバル変数と呼べます。
これは、オブジェクトごとにコピーが作られるようなことはなく。
つねにクラスに一つしか存在しないメンバなのです。

つまり同じクラスで生成したインスタンスで、共通したメンバってことか。
PS4の例でいうと、「友達のPS4と自分のPS4のセーブデータをクラウドで共有する」といった感じだと思います。

静的メンバ変数の宣言には static キーワードを使う。
参照

静的メンバとthisの関係

メンバ関数からメンバ変数にアクセスするには、thisという、自分自身のクラスを指す特殊なポインタを利用します。これは暗黙のうちにメンバ関数を呼び出すときに0番目の引数として渡されています。

参照

2つのコードの違い

#include <iostream>
#include <memory>
struct A {
    virtual void f() { std::cout << "A" << std::endl; }
};
struct B : A {
    virtual void f() override { std::cout << "B" << std::endl; } 
    void g() { A::f(); }
};

int main()
{
    auto b = std::make_unique<B>();
    b->g();// => A
}
#include <iostream>
namespace foo{
    void f() { std::cout << "foo::f" << std::endl; }
}
int main()
{
    foo::f();
}

::を使えばどこでも、好きなメンバを呼び出せると考えていたがそれは違う。
mainの中でB::g()と呼べるわけではない。
クラスのメンバはインスタンスを通してではないと使えない(見えない)。
つまり、bBのインスタンスではなく、Bを入れているだけなので、b::g()とできない。
インスタンスがないときに、->を使って、b->g()g()を呼び出している。

と思っていたが、違うかもしれない。
もう少し考えてみる。

PS4で例えた見える見えない。

この例は非常にわかりやすかったので、こちらの記事でも紹介さしてもらいます。
まるまるコピペします。

たとえ話はあまり好きではありませんが、例えば PS4 を Game という名前空間でクラスにしたとして、そこにゲームを始めるというメンバ関数があったとします。


namespace Game {
    class PS4 {
        public:
            void play() { ... }
    }
}

この時、このPS4クラスにグローバル空間にあるmainからアクセスするためには、Game::PS4とスコープ解決演算子で名前空間を指定する必要があります。そうでないと、mainはどこからPS4クラスを見つけてくればいいかわからないのです。
これは、スコープ解決演算子がついていないあらゆる識別子(関数の場合はシグネチャ)は、ローカル変数、関数なら引数、その場所と同じかより外側にある名前空間1にあるクラス、関数、グローバル変数、クラス関数の定義の中であればそのクラスのメンバ、からしか探されない、という仕様によるものです(正確に規格になんて書いてあるかは存じません。)。したがって、mainの中でただPS4と書いても、mainのある空間であるグローバル空間(グローバル空間には外側に空間がないのでこれだけです)内しか「見え」ないため、見つからないのです。そのためにスコープ解決演算子でGame名前空間を指定しておくと、Game名前空間はグローバル空間に存在してmainから「見える」ので、じゃあそのGame名前空間からPS4を探そう、とやってくれるわけですね。
さて、ではGame::PS4::play()とできるか、という話ですが、これはできません。なぜでしょう。ちゃんというなら、メンバ関数は「どのインスタンスで実行されようとしているか」という情報が必要なので、インスタンスを介さないと呼べないのです。たとえ話で言うと、世界には PS4 は山ほど存在しているわけですから、単にGame::PS4::play()と呼んでも、どの PS4 でゲームを起動すればいいのかわからないわけです。

int main() {
    // この PS4 はうちにあるもの!
    Game::PS4 myPS4;
    // 「うちの PS4」で遊ぶ!
    myPs4.play();
}

と「どの PS4」かをインスタンス化してやることで、初めて「その PS4」でゲームできるわけですね(ちゃんと言うと、インスタンスを介してメンバ関数を呼ぶことで、そのメンバ関数にthisが渡される。)。
ものすごくざっくりとした説明でしたが、とにかく、クラスのメンバは、たとえスコープ解決演算子を用いても、

・そのメンバが静的である場合
・その場所がそのクラスのメンバ関数の定義の中である場合

でなければ呼べません。それは、抽象的に言うなら「見え」ないからですが、ちゃんと言うなら、メンバへのアクセスに必要不可欠なthisが存在しないからです。

終わりに

まだ復習は終わってないけど、眠いので寝ます。
明日は、復習と通常同里にすすめていけたらなと思っています。

1
1
11

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1