2012年8月13日月曜日

オーバライド (override)

前回の 継承 (Inheritance)ができたので、これを応用すればスーパクラスで定義されている関数をサブクラス側で上書きする、オーバライドができる。


Integerクラスのサブクラス LoggableIntegerクラスには、スーパクラスの getValue関数をオーバライドする。この関数は Integerのそれど同様 int型の値を返すが、この関数を呼び出した回数を変数 counterに記録し、getAccessCount関数で読み出せるようにする。








ヘッダファイル (header file)

LoggableInteger.hはつぎの通り。
/* 13.Aug.2012 kei */

#ifndef _Included_LoggableInteger
#define _Included_LoggableInteger

typedef struct LoggableInteger* LoggableInteger;

typedef struct interface_LoggableInteger {

        void (*finalize)(LoggableInteger this);
        int (*getValue)(LoggableInteger this);

        int (*getAccessCount)(LoggableInteger this);
}* interface_LoggableInteger; 

extern void static_LoggableInteger(interface_LoggableInteger interface_methods);

extern LoggableInteger new_LoggableInteger(int value);

extern void LoggableInteger_init(LoggableInteger this, int value);

extern void LoggableInteger_finalize(LoggableInteger this);

extern int LoggableInteger_getValue(LoggableInteger this);

extern int LoggableInteger_getAccessCount(LoggableInteger this);

#endif /* _Included_LoggableInteger */

変数や関数が違う点以外は IntegerValueクラスのヘッダと同様。

ソースファイル (source file)

LoggableInteger.cはつぎの通り。
/* 13.Aug.2012 kei */

#include <LoggableInteger.h>
#include <Integer.h>

/****************************************************************
 private
****************************************************************/

#include <malloc.h>

struct LoggableInteger {
        interface_LoggableInteger interface_methods;

        /* Integer class */
        int value;

        /* LoggaableInteger class */
        int counter;
};

static struct interface_LoggableInteger interface_methods;
static struct interface_Integer super;

static int getValue(LoggableInteger this) {
        ++this->counter;
        return Integer_getValue((Integer)this);
}

static int getAccessCount(LoggableInteger this) {
        return this->counter;
}

/****************************************************************
 public
****************************************************************/

void static_LoggableInteger(interface_LoggableInteger interface_methods) {
        /* ToDo: lock interface_methods */
        if (interface_methods->finalize == NULL) {
                static_Integer(&super);
                static_Integer((interface_Integer)interface_methods);

                interface_methods->getValue = getValue;
                interface_methods->getAccessCount = getAccessCount;
        }
}

LoggableInteger new_LoggableInteger(int value) {
        static_LoggableInteger(&interface_methods);

        LoggableInteger this =
                (LoggableInteger)malloc(sizeof(struct LoggableInteger));

        if (this != NULL)
                LoggableInteger_init(this, value);

        return this;
}

void LoggableInteger_init(LoggableInteger this, int value) {
        Integer_init((Integer)this, value);
        this->interface_methods = &interface_methods;
}

void LoggableInteger_finalize(LoggableInteger this) {
        this->interface_methods->finalize(this);
}

int LoggableInteger_getValue(LoggableInteger this) {
        return this->interface_methods->getValue(this);
}

int LoggableInteger_getAccessCount(LoggableInteger this) {
        return this->interface_methods->getAccessCount(this);
}

これも IntegerVariableクラスとほぼ同じだが、getValue関数をオーバライドするため、LoggableIntegerクラスで改めて定義し、static_LoggableIntegerで getValue関数を設定している。
static_Integer関数を呼び出した時点で、interface_methodsには一旦スーパクラスの getValue関数が設定される。その後でサブクラスの関数を上書きしている。 概念だけでなく実際に上書きしているので、如何にも "override"の感じがする。

関数テーブルは interface_methodsのほかに Integerクラス用の superも定義している。
これは、Integerクラスの getValueを呼び出すために必要となる。

getValue関数の中では、 super.getValue((Integer)this); を呼び出すことにより、スーパクラスの取得関数を利用している。

動作確認 (unit test)

テストケース LoggableIntegerTest.cはつぎの通り。
/* 14.Aug.2012 kei */

#define import_lwd_unit_Assert
#define import_lwd_unit_CUnit

#include <lwd/unit/Assert.h>
#include <lwd/unit/CUnit.h>
#include <LoggableInteger.h>

/****************************************************************
 test cases
****************************************************************/

static LoggableInteger instance;

void setUp() {
        instance = new_LoggableInteger(813);
}

void tearDown() {
        LoggableInteger_finalize(instance);
}

void testNewAndFinalize() {
        /* setUp, tearDown */
}

void testGetValue() {
        int value = LoggableInteger_getValue(instance);
        Assert_equalsInt("A value must be 813.", 813, value);
}

void testGetAccessCount() {
        Assert_equalsInt("must be zero.",
                0, LoggableInteger_getAccessCount(instance));

        LoggableInteger_getValue(instance);

        Assert_equalsInt("must be 1.",
                1, LoggableInteger_getAccessCount(instance));
}

/****************************************************************
 main
****************************************************************/

int main(int argc, char** argv) {
        return CUnit_test(argc, argv);
}

テストのビルドと実行結果も残しておく。
$ make
cc -g -finstrument-functions -I. -I /export/home/kei/c/core/include -I /export/home/
kei/c/experimental/include -I /export/home/kei/c/io/include -I /export/home/kei/c/lang
/include -I /export/home/kei/c/message/include -I /export/home/kei/c/unit/include -I
 /export/home/kei/c/util/include -I /export/home/kei/c/x11/include -I ../../char/
include -I ../../class1/include  -c -o LoggableIntegerTest.o LoggableIntegerTest.c
$ ./LoggableIntegerTest
[CUnit] ./LoggableIntegerTest: testGetAccessCount
[CUnit] ./LoggableIntegerTest: testGetValue
[CUnit] ./LoggableIntegerTest: testNewAndFinalize
[SUCCEED] testGetAccessCount in 1(ms)
[SUCCEED] testGetValue in 2(ms)
[SUCCEED] testNewAndFinalize in 2(ms)
successes:    3 (100.0%)
failures:     0 (0.0%)
errors:       0 (0.0%)
total:        3 in 5(ms)
$ 

竜巻情報が出された。豹が降る恐れがあるとか言っている。

0 件のコメント:

コメントを投稿