クラスの継承の問題

ここでは「クラスと継承の問題」とありますが、ここでは継承によるわかりづらい問題や発生するバグなどを挙げたいと思います。


目次

virtual

比較的わかりやすい問題は、継承元・継承クラスのデストラクタにvirtualを入れていない場合に起こるバグです。

この問題は書き方に気をつければ問題ないのですが、オブジェクト指向に慣れていない人や、計画性を持たずにクラスライブラリを製作していると起こりやすいと思います。

また、継承元と違う動作をする関数にも継承元クラスに関数にvirtualをつけないと正しく動作しない場合があります。

他には、C++言語の大きな特徴の「多重継承」による問題もあります。

例えば、以下のような構成の場合、


// 利便上、structで構成しています
struct A {int a;}
struct B : public A {int b;}
struct C : public B {int c;}
struct D : public B, public C {int d;}

クラスDは、クラスAを継承するクラスBCを多重継承しています。

このとき、クラスDからクラスAのメンバにアクセスする場合、B::aC::aのように指定しなければなりませんが、B::aで値を書き換えたとき、C::aでアクセスしたときは書き換えられていない問題が発生します。

これは、クラスBCそれぞれが単独でクラスAを所有しているためです。

この時、それぞれのクラスAを共有させたい場合、以下のようにします。


struct A {int a;}
struct B : virtual public A {int b;}
struct C : virtual public B {int c;}
struct D : public B, public C {int d;}

このように継承するクラスにvirtualを付けることにより、クラスAは共有されます。

なぜこのような問題があるのかというと、なにより「多重継承」と言う特有な機能のため、より多様性に構築を行えるようにするためだろう、と私は思います。

virtualは他のキーワードよりも使うところが多くあるので、混乱の元になる場合もありますが、使い方さえ理解できればそれほど問題は起きないと思います。


メンバ変数の位置

普段あまり気にすることが無いと思われるメンバ変数ですが、稀に問題が発生する場合があります。


// 簡略化して書いています
template<class T>
class callback{
public:
    typedef void (T::*Func)();
    callback(Func f,T* c):_f(f),_c(c){}
    void operator()(){(_c->*_f)();}
    void chenge(T* c){_c = c;}
private:
    Func _f;
    T* _c;
};

あまり使わない記法(_c->*_f)();(メンバ関数ポインタ_fとクラスポインタ_cを使用したメンバ関数呼び出し)がありますが、これを利用したクラス(遅延呼び出しとか)を作成する場合、途中でクラスポインタを変更する処理(chenge(T* c);)が正しく行われない場合があります。

この場合は、メンバ関数ポインタをクラスポインタの後におくと解消されるようです。

もっとも、書き方によって稀に起こる問題なので、コンパイラによっては起こらないかもしれないし、他の事例では入れ替えるだけで解決するかどうかはわかりませんけど。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

This blog is kept spam free by WP-SpamFree.

制作物とか日記とかプログラミングTipsとか。