topazの遊び場

いろいろやってみる

そろそろC++やるぞパート14 ポインター ~ ポインターの初期化とメンバーアクセス「->」と「.」の違い ~

そろそろC++やります

そろそろC++をやります。そろそろC++をやりたいからです。
何回やるかはわかりません。基礎を理解するまではやろうと思います。

という感じでやっています。

※ 初心者がメモレベルで記録するので、技術記事としてはお力になれないかもしれません。

内容

ポインターの初期化

初期化にはnewを使用します。ヒープ配列の場合はこのように書きます。

int *ar = new int[5]{3, 5, 6};

また、MyClassという自作クラスの場合は、new 型名()と書きます。

MyClass *mc = new MyClass();

この宣言で初期化した場合はヒープ領域にメモリが確保されるため、deleteでメモリの解放を行う必要があります。

delete[] ar;
delete(mc);

ポインターメンバーアクセス演算子

演算子ドキュメントを参考にします。
->演算子は、ポインターの間接参照と、メンバーアクセスが組み合わされます。pubicな変数Xと関数Printを持ったMyClassクラスについて考えます。

class MyClass
{
public:
    int X;
    void Print()
    {
        cout << "Hello" << endl;
    }
};

->演算子を使用して、このようにアクセスすることができます。

MyClass *mc = new MyClass();
mc->X = 124;
int x = mc->X;

また、ポインターの間接参照とメンバーアクセスを明示的に書くこともでき、以下の二つは同じことを表している。

int x = mc->X;
int x = (*mc).X;

今回扱っているクラスは、ポインターとして扱う場合ですが、以前触れたクラスでは、クラスを変数として扱っていました。

MyClass mcClass = MyClass();
mcClass.X = 13;

メンバー変数を->演算子でアクセスするか、.演算子でアクセスするかはこの違いです。

void*ポインター

voidへのポインターは、単に生のメモリの場所を指します。
(実際は違うかもしれませんが、自分のイメージは全てのポインター型の基本型です。)

様々な型のポインターvoid *に代入することができます。

int *intPtr = new int();
float *floatPtr = new float();
MyClass *mcPtr = new MyClass();
void *voidPtr = intPtr;
voidPtr = floatPtr;
voidPtr = mcPtr;

C#で基底クラスのobjectに変換することがないように、C++void *ポインターもあまり使うことはないように思います。

関数へのポインター

自分の認識では、C#でいう delegateActionのようなものです。

コードを見た方がわかりやすいので、先にコードを見ます。

void PrintHello(string name)
{
    cout << "Hello " << name << endl;
}

void PrintWorld(string name)
{
    cout << "World " << name << endl;
}

class MyClass
{
public:
    int X;
    void (*PrintX)(string);
};

int main()
{
    MyClass *mc = new MyClass();
    mc->PrintX = PrintHello;

    mc->PrintX("topaz");

    mc->PrintX = PrintWorld;

    mc->PrintX("zzz");

    return 0;
}
// Output:
// Hello topaz
// World zzz

MyClassのpublicに定義されている、void (*PrintX)(string);が関数ポインターです。stringを受け取りvoidを返すPrintXを定義しています。
関数ポインターへの代入(割り当て)は=で行います。

mc->PrintX = PrintHello;
mc->PrintX = PrintWorld;

呼び出しは、通常の関数と同じように( )で行います。

mc->PrintX("topaz");
mc->PrintX("zzz");

関数ポインターへ代入を行う前に実行すると、segmentation faultが起こるので注意が必要です。

コンストラクタでの代入も行えます。(クラスのコンストラクタ

class MyClass
{
public:
    MyClass(void (*f)(string)) : PrintX(f)
    {
    }
    void (*PrintX)(string);
};

参考記事

感想

クラスの要素へのアクセス方法で->.の違いが分からなかったのですが、->は間接参照とメンバーアクセスが組み合わされていると知れたのがよかったです。
また、C#Actionなどを多用するので関数へのポインターも必要機能だなと思いました。

次回は、いろいろなところで進められているスマートポインターについて行おうと思います。