2015年5月20日水曜日

virtual function(C++)

virtual function(仮想関数)について

継承関係のあるクラスの関数をオーバーライドしたい場合にポリモーフィズム実現のために利用する 仮想関数 だが(実装を持たない場合、純粋仮想関数)、なぜ 仮想関数 を定義したクラスのデストラクタは virtualでなくてはならないのか。

 【復習】
仮想関数
//
// Stream.h
// CplusplusPractice
//
// Created by masai on 2015/05/20.
// Copyright (c) 2015年 masai. All rights reserved.
//
#ifndef __CplusplusPractice__Stream__
#define __CplusplusPractice__Stream__
class Stream{
public:
double Get() const;
bool Set();
protected: //派生クラスで使用可能
virtual void SetBase() = 0;
double m_n;
};
#endif /* defined(__CplusplusPractice__Stream__) */
view raw Stream.hpp hosted with ❤ by GitHub
//
// Stream.cpp
// CplusplusPractice
//
// Created by masai on 2015/05/20.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
#include "Stream.hpp"
using namespace std;
double Stream::Get() const{
return m_n;
}
bool Stream::Set(){
cout << "Stream::Set" << endl;
SetBase();
return m_n >= 0;
}
// 必ず子供で実装するので実装が不要→純粋仮想関数
//void Stream::SetBase(){
// cout << "Stream::SetBase" << endl;
// m_n = -1;
//}
view raw Stream.cpp hosted with ❤ by GitHub
//
// ArrayStream.h
// CplusplusPractice
//
// Created by masai on 2015/05/20.
// Copyright (c) 2015年 masai. All rights reserved.
//
#ifndef __CplusplusPractice__ArrayStream__
#define __CplusplusPractice__ArrayStream__
#include "Stream.hpp"
class ArrayStream : public Stream{
public:
ArrayStream(const double* array); //コンストラクタ
protected:
virtual void SetBase();
private:
const double* m_array; //配列
int m_i; //現在のインデックス
};
#endif /* defined(__CplusplusPractice__ArrayStream__) */
view raw ArrayStream.hpp hosted with ❤ by GitHub
//
// ArrayStream.cpp
// CplusplusPractice
//
// Created by masai on 2015/05/20.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
#include "ArrayStream.hpp"
using namespace std;
ArrayStream::ArrayStream(const double* array){
m_array = array;
m_i = 0;
}
void ArrayStream::SetBase(){
m_n = m_array[m_i];
++m_i;
}
view raw ArrayStream.cpp hosted with ❤ by GitHub
//
// InputStream.hpp
// CplusplusPractice
//
// Created by masai on 2015/05/20.
// Copyright (c) 2015年 masai. All rights reserved.
//
#ifndef CplusplusPractice_InputStream_hpp
#define CplusplusPractice_InputStream_hpp
#include "Stream.hpp"
class InputStream : public Stream{
protected:
virtual void SetBase();
};
#endif
view raw InputStream.hpp hosted with ❤ by GitHub
//
// InputStream.cpp
// CplusplusPractice
//
// Created by masai on 2015/05/20.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
#include "InputStream.hpp"
using namespace std;
void InputStream::SetBase(){
cin >> m_n;
}
view raw InputStream.cpp hosted with ❤ by GitHub
//
// inheritance.cpp
// CplusplusPractice
//
// Created by masai on 2015/05/20.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
#include "Stream.hpp"
#include "InputStream.hpp"
#include "ArrayStream.hpp"
using namespace std;
bool Average(Stream& stream){
int count;
double avr = 0;
for(count = 0; stream.Set(); ++count){
avr += stream.Get();
}
if(count == 0){
return false;
}
avr /= count;
cout << "average is " << avr << endl;
count = 0;
return true;
}
int main(){
// ArrayStream
static const double array1[] = {1, 2, 3, 4, 5, 6, -1};
static const double array2[] = {2, 4, 5, 6, 1, -1};
static const double* const array[] = {array1, array2};
// sizeは全体のメモリサイズ / 要素(最初の一つ目)のメモリサイズ
static const int size = sizeof array / sizeof *array;
for(int i = 0; i < size; ++i){
ArrayStream stream(array[i]);
if(! Average(stream)){
//break;
}
}
// InputStream
while(true){
InputStream stream;
if(! Average(stream)){
break;
}
}
}

仮想関数テーブル
virtual function table, vtable という隠しメンバ変数を使って実現される。
ある仮想関数を呼び出したとき、実装にはどの関数かを管理するテーブル 
仮想関数テーブルは実行時、コンストラクタで初期化されてデストラクタで破棄されるので、不要な場合はvirtualにしないほうが仮想関数テーブルの分だけメモリ使用量は節約できるし、効率もあがる。

仮想関数を定義したクラスのデストラクタ
仮想関数の利用シーンは、「子クラスのポインタ」を「親クラスのポインタ」にキャストして使って、仮想関数の実装だけ入れ替えるようにして利用(ポリモーフィズム)がほとんどのはずである。

このとき、親クラスのポインタ(実体は子クラス)に対して delete した際、親クラスのデストラクタがvirtualでない場合、子クラスのデストラクタが呼ばれず、メモリリークが発生する。

また、親クラスが暗黙的に作成するデストラクタはvirutalでは無いので、内容がなかったとしても以下のように明示的に宣言すること。
virtual ~ParentClazz() {}

0 件のコメント:

コメントを投稿