So-net無料ブログ作成

gccとg++ [C++]

前回の記事の内容を確認するために次のようなコードを書きました。

virtual.cpp
#include 

class Base {
public:
	void normal_func() {
		std::cout << "Base::normal_func()" << std::endl;
	};
	
	virtual void virtual_func() {
		std::cout << "Base::virtual_func()" << std::endl;
	};
};

class Sub : public Base {
public:
	void normal_func() {
		std::cout << "Sub::normal_func()" << std::endl;
	};
	
	virtual void virtual_func() {
		std::cout << "Sub::virtual_func()" << std::endl;
	};
};

int main()
{
	Base base;
	Base *pBase1 = new Base();
	Base *pBase2 = new Sub();
	
	Sub sub;
	Sub *pSub = new Sub();
	
	base.normal_func();
	base.virtual_func();
	pBase1->normal_func();
	pBase1->virtual_func();
	pBase2->normal_func();
	pBase2->virtual_func();
	
	sub.normal_func();
	sub.virtual_func();
	pSub->normal_func();
	pSub->virtual_func();
	
	return 0;
}


これを「gcc -o virtual virtual.cpp」でコンパイルしようとしたところ、「undefined reference to ...」と言うエラーがたくさん出て、「collect2: ld はステータス 1 で終了しました」とのこと。コンパイルできません。これはNetWalkerでのお話ですが、同じことをMacで実行したところ「Undefied symbols:」と出た後にずらずら〜っとメッセージが表示され、「ld: symbol(s) not found」「collect2: ld returned 1 exit status」だとのこと。同じくコンパイル出来ませんでした。iostreamに関連するライブラリのリンクがうまく行っていない様子で、検索していたところ、明確なお答えが・・・。

「g++ -o virtual virtual.cpp」と実行したところ、問題なくコンパイルが通り、実行結果も得ることができました。

*

う〜ん、昔は、gccが拡張子を見て適切に処理してくれたような気がしますが、いつからこうなったんでしょうか・・?


【参考】
undefined reference to `std::cout' - Ubuntu Forum




   

flagsと派生クラス・親クラスの関数の呼び出し方 [C++]

さて、QPainterを派生しようとしていましたが、この方法は根本的に不適当である可能性が高くなってきたので、このまま行っても大丈夫なのかを確かめるため、またC++の基礎的な仕様を調べておこうと思います。

前回出たのは「namespace」、「flags」、そして、今回の試みの根本的なところとなると思われる「派生クラスと親クラスの関数の呼び出し方」ですね。「namespace」は一本の記事に出来そうなので、おいおい別記事にすることにします。

*

「flags」・・・。ちょっと検索してみましたが、引っかかりませんね。こちらも追々必要になったら調べることにします。

残るは「派生クラスと親クラスの関数の呼び出し方」ですね。すごく基本的な事項だとは思うんですが・・。QPainterの派生クラスを作成した際、ほとんど何も考えずにさくさくと作って言ったんですが、QPainterの公開関数はすべて、「virtual」指定されていませんね。基本的にこれらの関数はオーバーライドされることを考慮していない(と言うか、望んでいない?)と言うことでしょうか。

こちらの説明で全て網羅されている様ですが、ここの情報を元に呼び出し関係をまとめてみます。サンプルとするクラスのソースは下記の通りです。

#include 

class Base {
public:
    void normal_func() {
        std::cout << "Base::normal_func()" << std::endl;
    }
	
    virtual void virtual_func() {
        std::cout << "Base::virtual_func()" << std::endl;
    }
};

class Sub : public  Base {
    void normal_func() {
        std::cout << "Sub::normal_func()" << std::endl;
    }
	
    virtual void virtual_func() {
        std::cout << "Sub::virtual_func()" << std::endl;
    }
};


これらのインスタンスとその呼び出し結果は下記のとおりとなりました。

クラスインスタンス呼び出し出力
class BaseBase base;base.normal_func()Base::normal_func()
base.virtual_func()Base::virtual_func()
Base *pBase = new Base();pBase->normal_func()Base::normal_func()
pBase->virtual_func()Base::virtual_func()
Base *pBase = new Sub();pBase->normal_func()Base::normal_func()
pBase->virtual_func()Sub::virtual_func()
class SubSub sub;sub.normal_func()Sub::normal_func()
sub.virtual_func()Sub::virtual_func()
Sub *pSub = new Sub();pSub->normal_func()Sub::normal_func()
pSub->virtual_func()Sub::virtual_func()


前回の記事で問題になっているのは、緑色の箇所ですね。インスタンスの実体はサブクラスのものなのですが、それを格納している変数が親クラスとなっていて、しかもオーバーライドしている関数が仮想関数で無いため、意図しているサブクラスの関数ではなく、親クラスの関数が呼ばれています。


う〜ん、困りましたね。


【参考】
C++ Reference - cplusplus.com
7. 仮想関数 - 目指せプログラマー! C++言語入門






constメンバ関数 [C++]

前回、知らない表記に出会ったので、調べておきます。以前のC++の記事同様、C++の基礎中の基礎の様な気もしてますが・・。

気になったのは、下記の様な関数の宣言です。

class QPainter
{
    const QBlush & background () const;    // ←末尾の「const」が気になる
}


気になるのは、関数末尾の「const」です。何これ?と思ったら、どうやら、「この指定がついている関数はメンバ変数の変更ができない(と言うか「しない」?)」と言うものらしいです。

こんな感じでしょうか・・。

class Test
{
public:
    int val;

    int set_get (int i) const {
        val = i;            // ←エラーになる?
        
        return val;
    }
}


でも、メンバ変数の宣言時に「mutable」と言う指定をつければ、その変数については、const メンバ関数でも書き込みができるそうです。

class Test
{
public:
    mutable int val;       // ←「mutable」で宣言

    int set_get (int i) const {
        val = i;            // ←大丈夫?
        
        return val;
    }
}


なんか、それ(mutableの方)って、どうなの?と言う仕様なのですが、ま、そう言うものの様です・・・。


【参考】
定数のconstメンバ関数をクラスに宣言 - C言語とC++入門勉強ルーム
第20章 constメンバ - C++編(言語解説)
C++ - const - ためになるホームページ
constの使い方。 関数・メンバ関数編 - akatukisidenのメモ帳






   

NULL の delete [C++]

前回の修正でビルドが通ったので、ちょちょっと動かして閉じたところ、「Application Output」のウィンドウで「The program has unexpectedly finished.」と言うメッセージが出てました。Macで異常終了していましたが、NetWalkerでも異常終了しているようですね・・。先に進む前にこれの修正をしておきたいと思います。

10062900.png
こちらでも異常終了してますね・・


コードをなめてみたところ、ポインタを delete した後に NULL をセットするのを忘れていた箇所がありました。デストラクタでポインタの解放の要否の判定をしているのですが、判定はその変数が NULL かどうかで行っているため、delete したポインタを再度 delete することが発生していたんですね。修正したら、エラーは出なくなりました。

で、改めて、delete した際に自動で NULL に設定することはできないのか探してみたところ、「NULL を delete しても特に何も起こらない」という記述が・・。これは、deleteの仕様の様です・・。これに従うと、delete 時に自動で NULL を設定できるかどうかはともかく、delete 時にポインタ変数が NULL かどうかはチェックする必要はないということですね。


【参考】
第12章 new/delete - C++編(言語解説)
delete NULL; - オタクのブ。





   

クラスのメンバ変数の初期化 [C++]

とりあえず、Qt開発のとっかかりまでは来たのですが、C++自体について全く素人なので、ここで気になったものを取り上げていこうと思います。レベル的には初心者だと思っていますが、一応、C++については、大昔に少しだけ触ったことがあるのと、CとRubyはそこそこの経験があると言う程度のスキルです。

今回は、クラスのメンバ変数の初期化。普通にコンストラクタの中でやってもいいんですが、確かメンバ変数だけは特殊な方法があったような気がするなぁと思って調べてみました。

*

とりあえず、C++の仕様の場所を調べておこうかと思ったんですが、有償でしか見れないようですね・・・。普通に見れるページとしては、ISO向けの古いドラフトSTLのリファレンスっぽいページは見つけました。

さて、調べていたところ、下記の様にコンストラクタにメンバーイニシャライザというのをつけるのが正解の様です。

class Hoge {
public:
    Hoge() : a(1), b(2) {    //  a(1), b(2) がメンバーイニシャライザ
        ....
    }

private:   
    int a;
    int b;
};


あんまり使用する意味が感じられなかったので、この様な定義ができた意図が知りたかったんですが、とりあえず、このメンバーイニシャライザによって、「static指定されていないconstメンバ変数の初期化ができる」とのこと。なるほどですね・・。また、「クラス内に他のクラスのインスタンスをメンバとする場合に、デフォルトコンストラクタ以外を呼ぶことができる」とのこと。なるほど・・。後者はわかりにくいと思うので、リンク先の例を少し真似てみます。

こんな感じです。

class Hoge {
public:
    Hoge()  {    //  デフォルトコンストラクタ
        a = 0;
    }
    
    Hoge(int i) { // 引数intを持つコンストラクタ
        a = i;
    }

private:   
    int a;
};

class Foo {
public:
    Foo() {    // デフォルトコンストラクタ
        // イニシャライザが指定されていない場合は、
        // hogeにデフォルトコンストラクタが適用される
    }

    Foo(int i) : hoge(i) { // 引数にintを持つコンストラクタ
        // イニシャライザが指定されているため、
        // hogeには引数に従ったコンストラクタが適用される
    }

private:
    Hoge hoge;
}


んん〜〜〜。わかった気になってましたが、ちゃんとわかってないかも・・。先程のページの実行結果を見る限りは、メンバ変数に他クラスのインスタンスがある場合、自クラスのコンストラクタが呼ばれる前にメンバ変数のコンストラクタが呼ばれる様ですね。この為、コンストラクタの内部ではメンバ変数のコンストラクタを選択することはできないが、メンバーイニシャライザならば、その際のコンストラクタを指定できると言うことかな・・。これは、処理系依存でなくて、言語仕様なんですかね・・。

なお、ついでに下記の様な情報も入りました。




う〜ん、やっぱりわかってないことばかりですね・・。ま、おいおい調べていきます。


【参考】
C++ - Wikipedia
Standard Template Library Programmer's Guide - sgi
C++ Final Draft International Standard
C++入門:5章 クラスの包含-> メンバーイニシャライザ - 目指せプログラマー!
C++のコンストラクタとObjective-Cのイニシャライザの違い
メンバイニシャライザでの初期化順序 - G.ISHIHARA
第20章 constメンバ - C++編(言語解説)






ブログを作る(無料) powered by So-netブログ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。

×

この広告は1年以上新しい記事の更新がないブログに表示されております。