2014年4月26日土曜日

C++ キャスト演算子



もはやメモリが少なくとも8Gは

必要な時代が来ているのかもしれません。

起動しただけで2G以上使っており、

何か作業をしようとプログラムを起動するにも

あまりにモッサリした動きになるので



システムのパフォーマンスオプションをパフォーマンスを優先するにしてみました。

ただ4G程度では、まだ足りないというのか...

こういうのは、思い至った時は大抵高いのが嫌なところ。



今時期は安くてもこの程度か...

6K位まで落ちれば、買ってもいいと思うかもしれない。



さて、今回はキャストの話です。(^_^;)

C では、値や変数を特定の型に変換したい時は、次のようにしていました。

// とあるスレッド関数
void *something_thread_func( void *arg ) {
    // 引数をとある構造体にキャスト
    something_struct_t *pSomeStruct = (something_struct_t *)arg;
        :
        :

C++ では、このようなキャストを行ってもエラーにはなりませんが、

できるだけ C++ で追加したキャスト演算子を使うようにしたいものです。(^_^)

これらのキャスト演算子は、ポインタ型や参照型を、別の型に変換します。

※ここでは、 V という型を T型に変換することを意味しています。


const_cast<T>(V)          const を取り除くのに使います。

dynamic_cast<T>(V)      基底クラスのポインタ(あるいは参照)を派生クラスの
                                 ポインタ(あるいは参照)に変換するのに使います。

static_cast<T>(V)          C のキャストに近い使い方ができます。
                  const は取り除くことはできません。

reinterpret_cast<T>(V)  関数ポインタを別の関数のポインタへ変換したり
               ポインタをint型などへ変換するときに使います。
               全く違う型へキャストした場合は不正アクセスを
               引き起こしやすいため、注意が必要です。
               どうしても必要なときだけ使うようにした方が良いでしょう。


そして、先程の例をキャストすると、こうなります。(^_^;)

// とあるスレッド関数2回目
void *something_thread_func( void *arg ) {
    // 引数をとある構造体にキャストした2回目
    something_struct_t *pSomeStruct = static_cast<something_struct_t>(arg);
        :
        :

このように、C++ のキャスト演算子を使うと、間違ったキャストをした場合に

コンパイルエラーになります(´・ω・`)

そのため、C の書き方よりも安全なキャストとなります。

また、_cast が付いているので、キャストした場所がわかりやすくなります。

grep で探すときも便利ですね(^_^)


今日の名言
もし「時」がこの世で最も貴重なものだとすれば、時の浪費ほど大きな浪費は
あるまい。失われた時は二度と帰らないからだ。時はいくらあっても十分では
ないのが常だから、やるべきことはさっさと行おう。価値あることを行おう。
精を出してことに当たれば、これまでよりてきぱきとやれるはずだ。
                              ベンジャミン・フランクリン

私は明日を恐れない。なぜなら私は、昨日を知ったし今日を愛しているからだ。
                              ウィリアム・アレン・ホワイト

君の毎分毎分を大切にすることをおすすめする。時間のほうは時間が自分で
世話をするだろうから。
                              チェスターフィールド卿

一度に一歩ずつ登れば、高い山でも踏破できる。
                              ジョン・ワナメーカー

2014年4月10日木曜日

C++ 代入演算子のオーバーロード





前回の演算子のオーバーロードに続いて、

今度は代入演算子をオーバーロードします。







operator= ()関数は、オブジェクト同士の代入が行われたときに呼び出されますが、

演算子のオーバーロードと同様に、operator= ()関数をオーバーロードして

クラスを代入する新しい = (代入演算子)を定義してみます。

では、さっそくサンプルプログラムです。

#include <iostream>
#include <string.h>

using namespace std;

class Person {
public:
    Person() {
        m_StrName = NULL;
    }
    Person(char *StrName) {
        // 勝手に128文字までに制限
        int nLen = strnlen(StrName, 128);
        m_StrName = new char[nLen + 1];
        strncpy(m_StrName, StrName, nLen + 1);
    }
    ~Person() {
        if (m_StrName != NULL) {
            delete [] m_StrName;
            m_StrName = NULL;
        }
    }
    Person & operator=(const Person & Psn);
    char *GetPersonName() {
        return m_StrName;
    }
private:
    char *m_StrName;
};

// 代入演算子のオーバーロード
Person & Person::operator=(const Person &Psn)
{
    // 自分自身の代入では、何もしない
    if (this == &Psn) return *this;

    // それまでのメモリを解放
    if (m_StrName != NULL) {
        delete [] m_StrName;
        m_StrName = NULL;
    }

    // 新しくメモリを確保して、文字列をコピーする
    // やはりここでも128文字制限
    int nLen = strnlen(Psn.m_StrName, 128);
    m_StrName = new char[nLen + 1];
    strncpy(m_StrName, Psn.m_StrName, nLen + 1);

    return *this;
}

int main(void)
{
    Person Psn1((char *)"ABCDEFG");
    Person Psn2((char *)"TUVWXYZ");

    cout << Psn1.GetPersonName() << endl;
    cout << Psn2.GetPersonName() << endl;

    // 代入する 
    Psn1 = Psn2;

    cout << "---" << endl;

    cout << Psn1.GetPersonName() << endl;
    cout << Psn2.GetPersonName() << endl;

    return 0;
}

実行結果
# ./test
ABCDEFG
TUVWXYZ
---
TUVWXYZ
TUVWXYZ


ABCDEFGという名前が代入によってTUVWXYZに変わったという
例によって、あまりおもしろくないプログラムです。(^_^;)

ただ、デフォルトのoperator= ()ではメモリ確保などは行われないため
一応独自の動作が定義できています。

代入のところでは、Psn1オブジェクトのoperator= () が呼ばれ、
Psn2オブジェクトが引数に入るという動きになっています。

そしてoperator= ()が自分自身を返すのは、Psn1 = Psn2 という式が
さらに代入される場合があることを考慮しているからです。


ところで、Person Psn3 = Psn2; のようなオブジェクト生成時の
初期化の場合は、コピーコンストラクタが呼ばれます。
似ているので注意しておきたいところです。(^_^;)


今日の名言
常に現在の時間にしっかりしがみつけ。刻一刻すぎていく時間には、無限の価値がある。
私は現在に自己の全てを賭けている。一枚のトランプに人が大金をかけるように。
私は現在をそっくりそのままで、できるだけ高価なものにしようと努力しているのだ。
                            ヨハン・ウォルフガング・フォン・ゲーテ

今ここで征服できない時間は、永久に征服できない。今ここで楽しめない人生は、
永久に楽しめない。今ここで賢明な生活を送らなければ、永久に賢明な生活は
できない。過去はもはや存在せず、未来は誰にも分からないのだから。
                            デイヴィッド・グレイソン

もし、悩みに関して古今の大哲学者が書いた言葉の全てを要約すれば、次の
二句に尽きる。
「橋のたもとに行き着くまでは橋を渡るな」、「覆水盆に返らず」
                            デール・カーネギー

ありていに言えば、現在に生きる者はごく少ない。誰も彼も、現在以外の時に
生きるつもりなのだ。
                            ジョナサン・スウィフト

2014年4月1日火曜日

C++ 演算子のオーバーロード




演算子をオーバーロードすると、クラスごとに

演算子の機能を定義できるようになります(^^)


因みにオーバーロードは、日本語で書くと

多重定義です。



一方、言葉が似ているため、とても間違いやすく混同しがちなオーバーライドは

機能の上書き、あるいは再定義となります。(^^;)

何度も繰り返し唱えて、覚えてしまいたいものです...(^^ゞ

さて、演算子のオーバーロードですがC++では、 + や - といった既にある

演算子に対して、独自の機能を定義することができます。

使いどころとしては、単純に四則演算しただけでは期待する結果に

ならないだとか、2つ以上のあるデータの組に対して演算を行いたい場合

などが当てはまるでしょう(^^)

ここでは、+演算子をオーバーロードして、オブジェクト同士の加算を

行ってみたいと思います。 

時刻の加算結果を求めるサンプルです。

#include <iostream>

using namespace std;

class ClockTime {
public:
    ClockTime() {};
    ClockTime(int nHour, int nMinute) {
        m_nHour = nHour;
        m_nMinute = nMinute;
    }
    ClockTime operator+ (const ClockTime &Ct2);

    int m_nHour;
    int m_nMinute;
};

// +演算子のオーバーロード
ClockTime ClockTime::operator+ (const ClockTime &Ct)
{
    int nHourBuf = 0;
    int nMinuteBuf = 0;
    int nTotal = 0;

    nTotal = (m_nHour * 60 + m_nMinute) + (Ct.m_nHour * 60 + Ct.m_nMinute);
    nHourBuf = nTotal / 60;
    nMinuteBuf = nTotal % 60;

    // 自分自身と同じクラスのオブジェクトを戻す
    return ClockTime(nHourBuf, nMinuteBuf);
}

int main(void)
{
    ClockTime Ct1(1, 23);  // 1時間23分
    ClockTime Ct2(4, 56);  // 4時間56分
    ClockTime Ct3;

    // オブジェクト同士を加算
    Ct3 = Ct1 + Ct2;

    // この例で期待通りとなる値は 6時間19分
    cout << Ct3.m_nHour << "時間 " << Ct3.m_nMinute << "分" << endl;

    return 0;
}

実行結果
# ./test
6時間 19分

いつものようにたいしておもしろくないプログラムですが、

期待通りの結果となりました。(^_^;)

ClockTimeクラスの中で、 operator+() 関数を定義しているため

Ct3 = Ct1 + Ct2; の式での + は、Ct1オブジェクトの operator+()が

呼ばれるようになります。

また、ClockTime ClockTime::operator+ (const ClockTime &Ct) の

引数として渡されるているのが、Ct2オブジェクトとなっています。

結果、時刻を加算するという +演算子のオーバーロードができました(^_^;)

ちなみに、 -演算子では operator-()関数をオーバーロードします。

*, / といった他も同様です。

オブジェクト指向の素晴らしさを垣間見ることができますね。

今日の名言
現在の一瞬はこのうえなく素晴らしい一瞬である。今、夕食に5分遅れることは、
十年間の大きな悲しみより重要である。
                              サミュエル・バトラー

いくら苦しくても現実をしっかり見つめることだ。目標をしっかり定める。
いったん目標が定まったら、ありとあらゆる時間をその実現のために
注ぎ込む。自分の決心が正しいかどうか心配して、貴重な時間を浪費
するな。あくまでやり通せ!
                              デール・カーネギー

過ぎ去りし麗しき一日は、再び我がもとに帰り来たらず。
                              アルフレッド・テニソン卿

我々はここにいる。そしてそれは今だ。それより先のことについての人間の
知識は、すべて月光のごとくとりとめがない。
                              ヘンリー・L・メンケン