<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>株式会社エス・スリー・フォー</title>
	<atom:link href="http://www.s34.co.jp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.s34.co.jp</link>
	<description>株式会社エス・スリー・フォーは、一人一人のスキルを重視したシステム・ネットワーク構築のプロフェッショナル集団です。蓄積されたノウハウと最新技術により、お客様が考えられているシステムを明確にし、ご提案させて頂きます。</description>
	<lastBuildDate>Mon, 23 Jan 2012 07:59:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2</generator>
		<item>
		<title>抽象データ型と Java/C++ そして COM/CORBA</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/adt/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/adt/#comments</comments>
		<pubDate>Mon, 02 Apr 2007 05:59:48 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=3439</guid>
		<description><![CDATA[抽象データ型とは&#8230; 抽象データ型 継承 多態 をオブジェクト指向の三本柱などと称しています。その中でも抽象データ型(あるいはデータの抽象化)はオブジェクト指向の最も基本的で重要な概念ではないかと考えます。 &#34;データを抽象化する&#34;とは、データをそれに対して適用できる操作の集合で定義することです。 簡単な例として&#34;カウンタ&#34;を考えてみましょう。カウンタには3つの操作: +1する (increment) -1する (decrement) 現在値を取得する を提供させることにします。 さて、このカウンタをCで実現するとどうなるでしょうか&#8230; typedef struct { long value_; } Counter; Counter* counter_create(); void counter_incr(Counter*); void counter_decr(Counter*); long counter_value(const Counter*); void counter_release(Counter*); /* ---- ここから実装部。Counterの利用者には見せない ---- */ Counter* counter_create() { Counter* c = (Counter*)malloc(sizeof(Counter)); c-&#62;value_ = 0; return c; } void counter_incr(Counter* c) { ++c-&#62;value_; [...]]]></description>
			<content:encoded><![CDATA[<h3>抽象データ型とは&#8230;</h3>
<ul>
<li>抽象データ型</li>
<li>継承</li>
<li>多態</li>
</ul>
<p>
をオブジェクト指向の三本柱などと称しています。その中でも抽象データ型(あるいはデータの抽象化)はオブジェクト指向の最も基本的で重要な概念ではないかと考えます。
</p>
<p>
&quot;<strong>データを抽象化する</strong>&quot;とは、データを<strong>それに対して適用できる操作の集合で定義する</strong>ことです。
</p>
<p>
簡単な例として&quot;カウンタ&quot;を考えてみましょう。カウンタには3つの操作:
</p>
<ul>
<li>+1する (increment)</li>
<li>-1する (decrement)</li>
<li>現在値を取得する</li>
</ul>
<p>を提供させることにします。</p>
<p>さて、このカウンタをCで実現するとどうなるでしょうか&#8230;</p>
<p><span id="more-3439"></span></p>
<pre>
typedef struct {
  long value_;
} Counter;

Counter* counter_create();
void     counter_incr(Counter*);
void     counter_decr(Counter*);
long     counter_value(const Counter*);
void     counter_release(Counter*);

/* ---- ここから実装部。Counterの利用者には見せない ---- */

Counter* counter_create() {
  Counter* c = (Counter*)malloc(sizeof(Counter));
  c-&gt;value_ = 0;
  return c;
}

void     counter_incr(Counter* c) { ++c-&gt;value_; }

void     counter_decr(Counter* c) { --c-&gt;value_; }

long     counter_value(const Counter* c) { return c-&gt;value_; }

void     counter_release(Counter* c) { free(c); }
</pre>
<p>&#8230;こんな感じになりますか。C++の入門書の中には、</p>
<p><strong>データ(構造体) + アルゴリズム(関数) = オブジェクト(クラス)</strong></p>
<p>と説明されたものもあります。つまり、構造体に、それに適用される操作を付け加えたものがクラスだ、というのです。</p>
<pre>
class Counter {
private:
  long value_;
public:
  Counter();
  ~Counter();
  void incr();
  void decr();
  long value() const;
};

/* --- ここから実装部。Counterの利用者には見せない --- */

Counte::Counter() : value_(0) {}

Counte::~Counter()  {}

void Counter::incr() { ++value_; }

void Counter::decr() { --value_; }

long Counter::value() const { return value_; }
</pre>
<p>Counterのメンバ変数value_は、利用者に勝手にアクセスさせないようprivate部に置きました。</p>
<p>カウンタの利用者にしてみればprivateメンバに何が定義されているかなんて(どうせアクセスできないのだから)知る必要もありません。+1できて、-1できて、値を取得できるということだけで十分です。</p>
<p>カウンタの実装者にしても同様です。+1する、-1する、値を返す、の3つの機能をきちんと実装できるのなら、privateメンバはその実現のためになら何を定義しようがかまわない。メソッドが呼ばれるたびに電話回線を通してはるか遠くにあるデータベースを更新・参照したっていいでしょうよ。</p>
<p>結局カウンタについて利用者と実装者との間できめておかなければならないのは、カウンタに対する操作だけなんです。</p>
<p><strong>&quot;Xに対して何ができるか&quot;で定義したX</strong>、それが<strong>抽象データ型</strong>なんですね。</p>
<h4>Javaにおける抽象データ型</h4>
<p>Javaは、抽象データ型を直接サポートするキーワード<strong>interface</strong>を持っています。</p>
<pre>
public interface Counter {
  void incr();
  void decr();
  int  value();
}
</pre>
<p>interfaceはclassではありません。実体が存在しません。Counterの実装者はinterfaceを実装(implements)するclassを作ります。</p>
<pre>
class CounterImpl implements Counter {
  private int value_;
  public CounterImpl() { value_ = 0; }
  // interface Counter を実装する
  public void incr() { ++value_; }
  public void decr() { --value_; }
  public int value() { return value_; }
}
</pre>
<p>利用者にCounterImplの存在を気づかせないために、Counterを生成するクラスCounterFactoryを用意しましょう。</p>
<pre>
public class CounterFactory {
  public static Counter create() { return new CounterImpl(); }
}
</pre>
<p>CounterFactory.create()はCounterImplをnewし、それをCounterとして返します。利用者にはCounterFactory.create()で手に入れたオブジェクトが実際にはCounterImplだということを隠しています。それでいいんです。利用者が欲しがっているのはCounterImplではなく、Counterに対する操作なのですから。</p>
<pre>
public class Client {
  public static void main(String[] arg) {
    Counter c = CounterFactory.create();
    for ( int i = 0; i &lt; 3; ++i ) {
      c.incr();
      System.out.println("value= "+c.value());
    }
  }
}
</pre>
<p>Clientをアプレット化するとこんな感じになります。<a id="reference_1" href="#footnote_1" class="reference">[1]</a></p>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/java.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<h4>C++における抽象データ型</h4>
<p>残念ながらC++ではJavaのinterfaceのような抽象データ型を直接にはサポートしていません。が、それと同等のことなら可能です。一切のメンバ変数を持たず、そして全メンバ関数を純粋仮想関数とするのです。</p>
<pre>
class Counter {
public:
  virtual ~Counter() {}
  virtual void incr() =0;
  virtual void decr() =0;
  virtual long value() const =0;
};
</pre>
<p>そしてこのCounterから派生したCounterImpl、およびCounterFactoryを作りましょう。</p>
<pre>
class CounterFactory {
public:
  static Counter* create();
  static void     release(Counter*);
};

/* --- ここから実装部。Counterの利用者には見せない --- */

class CounterImpl : virtual public Counter {
private:
  long value_;
public:
   CounterImpl() : value_(0) {}
   virtual void incr() { ++value_; }
   virtual void decr() { --value_; }
   virtual long value() const { return value_; }
};

Counter* CounterFactory::create() {
  return new CounterImpl;
}

void CounterFactory::release(Counter* c) {
   delete c;
}
</pre>
<p>利用者(Client)は以下のようになります。</p>
<pre>
using namespace std;

int main() {
  adt::Counter* c = adt::CounterFactory::create();
  for ( int i = 0; i &lt; 3; ++i ) {
    c-&gt;incr();
    cout &lt;&lt; "value= " &lt;&lt; c-&gt;value() &lt;&lt; endl;
  }
  adt::CounterFactory::release(c);
  return 0;
}
</pre>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/cpp.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<h3>言語を越えて&#8230;</h3>
<p>抽象データ型はその利用者と実装者との間の申し合わせです。これを発展させると、抽象データ型を利用者と実装者双方が認識できる、すなわち実装者はそのインタフェースをサポートするオブジェクトを生成し、利用者はそのインタフェースを提供するオブジェクトを手に入れて利用することができるなら、利用者と実装者が互いに異なる言語で書かれていてもいいはずです。それを実現したのがCOM、そしてCORBAです。</p>
<h4>COM (Component Object Model)</h4>
<p>Visual C++ 6.0 ATLを使ってCOMサーバ(実装側)を作ってみましょう。</p>
<p>Visual C++の&#8221;ATL COMAppWizard&#8221;を使えばCOMサーバをとっても簡単に作れます。</p>
<p>CounterのインタフェースICounterを作ってできたファイルadt.idlを以下に示します。</p>
<pre>
...
  interface <strong>ICounter</strong> : IDispatch {
    [id(1), helpstring("+1する")] HRESULT <strong>incr</strong>();
    [id(2), helpstring("-1する")] HRESULT <strong>decr</strong>();
    [propget, id(3), helpstring("値を返す")] HRESULT <strong>value</strong>([out, retval] long *pVal);
  };
...
  coclass <strong>Counter</strong> {
    [default] interface <strong>ICounter</strong>;
  };
...
</pre>
<p>adt.idlの中に、incr,decr,valueをサポートするインタフェースICounterと、ICounterをインタフェースとして持つクラスCounterがあることがわかるでしょう。</p>
<p>以下に示すのは最終的にできあがったCounterの実装部CCounterです。</p>
<pre>
class ATL_NO_VTABLE <strong>CCounter</strong> :
  public CComObjectRootEx&lt;CComSingleThreadModel&gt;,
  public CComCoClass&lt;CCounter, &amp;CLSID_Counter&gt;,
  public IDispatchImpl&lt;<strong>ICounter</strong>, &amp;IID_ICounter, &amp;LIBID_ADTLib&gt; {
public:
  CCounter() { value_ = 0; }

DECLARE_REGISTRY_RESOURCEID(IDR_COUNTER)
DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CCounter)
  COM_INTERFACE_ENTRY(ICounter)
  COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

public:
  STDMETHOD(<strong>get_value</strong>)(long *pVal);
  STDMETHOD(<strong>decr</strong>)();
  STDMETHOD(<strong>incr</strong>)();
private:
  long value_;
};
/* --- implementation --- */

STDMETHODIMP CCounter::<strong>incr</strong>() {
  ++value_;
  return S_OK;
}

STDMETHODIMP CCounter::<strong>decr</strong>() {
  --value_;
  return S_OK;
}

STDMETHODIMP CCounter::<strong>get_value</strong>(long *pVal) {
  *pVal = value_;
  return S_OK;
}
</pre>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/com.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<p>これをコンパイルすればadt.dllができあがり、レジストリに登録されます。これでCOMをサポートする言語ならなんであれ、利用者はCounterを使うことができるはずです。</p>
<p>Visual BasicからCounterを使ってみましょうか。VisualBasicのメニューから&#8221;プロジェクト|参照設定&#8221;ることで、VisualBasicがCounterのインタフェースを認識します。</p>
<p>フォーム上にテキストボックス(txtValue)とボタンをふたつ(btnIncr,btnDecr)配置して:</p>
<pre>
Option Explicit

Private c As Counter

Private Sub btnDecr_Click()
  c.decr
  txtValue = c.Value
End Sub

Private Sub btnIncr_Click()
  c.incr
  txtValue = c.Value
End Sub

Private Sub Form_Load()
  Set c = New Counter
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Set c = Nothing
End Sub
</pre>
<p>なんとまぁ、たったこれだけで動いてしまうんです。</p>
<h4>CORBA (Common Object Request Broker Architecture</h4>
<p>CORBAはインタフェースを利用者と実装者が共有するという考えをもう一歩すすめ、異なるマシン/異なるOS/異なるプロセス/異なる言語による利用者/実装者を可能にしてくれます。</p>
<p>Orbixを使ってCounterを作ります。まずはインタフェースの定義から。</p>
<pre>
module adt {
  interface <strong>Counter</strong> {
    void <strong>incr</strong>();
    void <strong>decr</strong>();
    readonly attribute long <strong>value</strong>;
  };
};
</pre>
<p>avaのinterfaceとそっくりです。このインタフェース定義ファイルadt.idlをIDLコンパイラに食わせると、利用者/実装者それぞれのためにスタブ/スケルトンを吐いてくれます。実装者はスケルトンを基にCounterを実装し、利用者はスタブを使ってCounterにアクセスします。</p>
<pre>
/*
 * interface Counter の実装
 */
#include "Counter.hh"

namespace adt {

  class CounterImpl : public virtual CounterBOAImpl {
  private:
    CORBA::Long value_;

  public:
    CounterImpl() : value_(0) {}

    virtual void <strong>incr</strong>(CORBA::Environment &amp;IT_env=CORBA::default_environment) ;
    virtual void <strong>decr</strong>(CORBA::Environment &amp;IT_env=CORBA::default_environment) ;
    virtual CORBA::Long <strong>value</strong>(CORBA::Environment &amp;IT_env=CORBA::default_environment) ;
  };

}

namespace adt {

  void CounterImpl::incr(CORBA::Environment &amp;IT_env) {
    ++value_;
  }

  void CounterImpl::<strong>decr</strong>(CORBA::Environment &amp;IT_env) {
    --value_;
  }

  CORBA::Long CounterImpl::<strong>value</strong>(CORBA::Environment &amp;IT_env)  {
    return value_;
  }

}
</pre>
<p>実装者(サーバ)は(COMをレジストリに登録するように)あらかじめOrbixデーモンに登録しておきます。利用者はデーモンの仲介でCounterを手に入れ、利用します。</p>
<pre>
/*
 * クライアント
 */
#include "Counter.hh"
#include &lt;iostream.h&gt;

int main() {
  adt::Counter_var c;
  c = adt::Counter::_bind(":adt");
  for ( int i = 0; i &lt; 3; ++i ) {
    c-&gt;incr();
    cout &lt;&lt; "value= " &lt;&lt; c-&gt;value() &lt;&lt; endl;
  }
  return 0;
}
</pre>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/corba.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<h5>注釈</h5>
<ol>
<li id="footnote_1"><a href="#reference_1" class="footnote">^</a>弊社のWebサイト公開ポリシーによりアプレットは削除しております。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/adt/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>sizeofの不思議</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/sizeof/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/sizeof/#comments</comments>
		<pubDate>Mon, 02 Apr 2007 05:59:48 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=458</guid>
		<description><![CDATA[はじめに Cではsizeofによって構造体がメモリ上で占める大きさ(バイト数)を知ることができます。 struct s{ int x; int y; }; ... cout &#60;&#60; sizeof(s) &#60;&#60; endl; 僕の愛用する処理系、Visual C++ 6.0では 8が得られました。intひとつにつき4byteを消費するからでしょう。 それではC++でのclassの大きさはどうでしょう。内包するメンバ変数それぞれの占めるバイト数の総和になるのでしょうか。 class c { int x; int y; }; ... cout &#60;&#60; sizeof(c) &#60;&#60; endl; 答は&#8230;やはり 8 です。でもね、C++ではいつもこうなるとは限らないのですよ。 「C++はCよりデカい」と言われることがあります。その理由のひとつがここに明らかになります。 仮想関数 上記class cにメンバ関数を追加します。 class c { int x; int y; public: int sum(); }; ... cout &#60;&#60; [...]]]></description>
			<content:encoded><![CDATA[<h3>はじめに</h3>
<p>Cではsizeofによって構造体がメモリ上で占める大きさ(バイト数)を知ることができます。</p>
<pre>
struct s{
  int x;
  int y;
};
...
cout &lt;&lt; <strong>sizeof</strong>(s) &lt;&lt; endl;
</pre>
<p>僕の愛用する処理系、Visual C++ 6.0では <strong>8</strong>が得られました。intひとつにつき4byteを消費するからでしょう。</p>
<p>それではC++でのclassの大きさはどうでしょう。内包するメンバ変数それぞれの占めるバイト数の総和になるのでしょうか。</p>
<p><span id="more-458"></span></p>
<pre>
class c {
  int x;
  int y;
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>答は&#8230;やはり <strong>8</strong> です。でもね、C++ではいつもこうなるとは限らないのですよ。</p>
<p>「C++はCよりデカい」と言われることがあります。その理由のひとつがここに明らかになります。</p>
<h3>仮想関数</h3>
<p>上記class cにメンバ関数を追加します。</p>
<pre>
class c {
  int x;
  int y;
public:
  int sum();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>結果は <strong>8</strong> でした。メンバ関数を追加してもサイズに変化はないようです。</p>
<p>ところが、仮想関数を追加すると&#8230;</p>
<pre>
class c {
  int x;
  int y;
public:
  virtual int sum();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>結果は<strong>12</strong>。4バイト増加しています。まるでメンバ関数へのポインタがクラス内にこっそり追加されたかのようです。<br />
ならば仮想関数が2つあったら8バイト増えるのかな&#8230;</p>
<pre>
class c {
  int x;
  int y;
public:
  virtual int sum();
  virtual int sub();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>依然として <strong>12</strong> のままです。実体を伴わない純粋仮想関数を追加したら&#8230;</p>
<pre>
class c {
  int x;
  int y;
public:
  virtual int sum() =0;
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>&#8230;ふむ、純粋仮想関数を追加してもクラスの大きさは <strong>12</strong> ですね。</p>
<p>これらのことから、クラスに仮想関数がひとつ以上あるとき、クラスの大きさは <strong>4</strong> バイト増加すると思われます。</p>
<p><strong>何故</strong>でしょう。その理由は仮想関数が実行されるときのからくりにあります。</p>
<p>仮想関数を含むクラスを宣言すると、そのクラスの中メンバ変数&apos;vptr&apos;がひとつ、こっそり追加されます。これは&quot;仮想関数へのポインタ配列&apos;vtbl&apos;へのポインタ&quot;です。<br />
vtblはクラス内にある仮想関数のポインタを配列内に納めたもので、仮想関数を含むクラス毎にひとつ作られます。仮想関数がコールされるとき、vptrの指す配列vtblの特定の位置から仮想関数へのポインタを取り出し、そこをコールします。</p>
<p>このようなからくりのために、仮想関数を含むクラスは、仮想関数のないクラスより<strong>少し大きい</strong>のです。</p>
<h3>継承</h3>
<p>継承したときのクラスの大きさはどうでしょうか。</p>
<pre>
class c0 {
  int x;
  int y;
};

class c1 : public c0 {
  int a;
  int b;
};
...
cout &lt;&lt; <strong>sizeof</strong>(c0) &lt;&lt; endl;
cout &lt;&lt; <strong>sizeof</strong>(c1) &lt;&lt; endl;
</pre>
<p>結果はそれぞれ <strong>8</strong>,<strong>16</strong> でした、予想通りの結果です。</p>
<p>仮想関数を含むとどうなるでしょう。</p>
<pre>
class c0 {
public:
  int x;
  int y;
  virtual int sum();
};

class c1 : public c0 {
public:
  int a;
  int b;
  virtual int diff();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c0) &lt;&lt; endl;
cout &lt;&lt; <strong>sizeof</strong>(c1) &lt;&lt; endl;
</pre>
<p>sizeof(c0)が <strong>12</strong>だから、c1はそれにint2個分(8byte)と&apos;vptr&apos;(4byte)を加えて 24 &#8230;ではなくて <strong>20</strong> となりました。仮想関数テーブルポインタ&apos;vptr&apos;はクラス内にひとつあればいいからです。</p>
<pre>
cout &lt;&lt; <strong>offsetof</strong>(c1,x) &lt;&lt; endl;
cout &lt;&lt; <strong>offsetof</strong>(c1,y) &lt;&lt; endl;
cout &lt;&lt; <strong>offsetof</strong>(c1,a) &lt;&lt; endl;
cout &lt;&lt; <strong>offsetof</strong>(c1,b) &lt;&lt; endl;
</pre>
<p>の結果はそれぞれ、4, 8, 12, 16 となりました、このことから、class c1のメモリ上でのレイアウトは、</p>
<table summary="レイアウト">
<tbody>
<tr>
<th>offset</th>
<th>member</th>
</tr>
<tr>
<td>+0</td>
<td>c1::vptr</td>
</tr>
<tr>
<td>+4</td>
<td>c0::x</td>
</tr>
<tr>
<td>+8</td>
<td>c0::y</td>
</tr>
<tr>
<td>+12</td>
<td>c1::a</td>
</tr>
<tr>
<td>+16</td>
<td>c1::b</td>
</tr>
</tbody>
</table>
<p>となっていると考えられます。</p>
<h3>多重継承</h3>
<p>多重継承の場合、ちょっとばかしややこしくなります。</p>
<pre>
class c0 {
public:
  int x0;
  int y0;
  virtual void f0() {}
};

class c1 {
public:
  int x1;
  int y1;
  virtual void f1() {}
};

class c2 : <strong>public c0, public c1</strong> {
public:
  int x2;
  int y2;
  virtual void f2() {}
};
...
cout &lt;&lt; &quot;c0  :        &quot; &lt;&lt; sizeof(c0)  &lt;&lt; endl;
cout &lt;&lt; &quot;c1  :        &quot; &lt;&lt; sizeof(c1)  &lt;&lt; endl;
cout &lt;&lt; &quot;c2  : c0, c1 &quot; &lt;&lt; sizeof(c2)  &lt;&lt; endl;
</pre>
<p>&apos;仮想関数ポインタテーブル(vptr)はクラスにひとつ&apos;であるならば、sizeof(c2)は、10,c1,c2それぞれのメンバ変数領域の総和(8&#215;3=24byte)にvptr(4byte)を加えた28byteとなりそうです。</p>
<p>ところが結果は予想に反して <strong>32</strong>となりました。vptrが2つ含まれているようです。</p>
<pre>
cout &lt;&lt; offsetof(c2, x0) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, y0) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, x1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, y1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, x2) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, y2) &lt;&lt; endl;
</pre>
<p>の結果: 4, 8, 16, 20, 24, 28 から察するに、c2のメモリ・レイアウトは:</p>
<table summary="レイアウト">
<tbody>
<tr>
<th>offset</th>
<th>member</th>
</tr>
<tr>
<td>+0</td>
<td>c2::vptr</td>
</tr>
<tr>
<td>+4</td>
<td>c0::x0</td>
</tr>
<tr>
<td>+8</td>
<td>c0::y0</td>
</tr>
<tr>
<td>+12</td>
<td>c1::vptr</td>
</tr>
<tr>
<td>+16</td>
<td>c1::x1</td>
</tr>
<tr>
<td>+20</td>
<td>c1::y1</td>
</tr>
<tr>
<td>+24</td>
<td>c1::x2</td>
</tr>
<tr>
<td>+28</td>
<td>c1::y2</td>
</tr>
</tbody>
</table>
<p>であろうと考えられます。</p>
<h3>仮想継承</h3>
<p>さらに仮想継承を考えると、話はさらにややこしくなります。</p>
<pre>
class c0 {
public:
  int x0;
  int y0;
  virtual void f0() {}
};

class c1 : <strong>virtual public c0</strong> {
public:
  int x1;
  int y1;
  virtual void f1() {}
};

class c2 : <strong>virtual public c0</strong> {
public:
  int x2;
  int y2;
  virtual void f2() {}
};

class c3 : <strong>public c1, public c2</strong> {
public:
  int x3;
  int y3;
  virtual void f3() {}
};
...
cout &lt;&lt; &quot;c0  :        &quot; &lt;&lt; sizeof(c0)  &lt;&lt; endl;
cout &lt;&lt; &quot;c1  : c0     &quot; &lt;&lt; sizeof(c1)  &lt;&lt; endl;
cout &lt;&lt; &quot;c2  : c0     &quot; &lt;&lt; sizeof(c2)  &lt;&lt; endl;
cout &lt;&lt; &quot;c3  : c1, c2 &quot; &lt;&lt; sizeof(c3)  &lt;&lt; endl;
</pre>
<p>sizeof(c3)は <strong>52</strong> となりました。なんとも不思議な結果です。上記の各クラスから仮想関数を抜き取ってみましょう。</p>
<pre>
class c0 {
public:
  int x0;
  int y0;
};

class c1 : virtual public c0 {
public:
  int x1;
  int y1;
};

class c2 : virtual public c0 {
public:
  int x2;
  int y2;
};

class c3 : public c1, public c2 {
public:
  int x3;
  int y3;
};
...
cout &lt;&lt; &quot;c0  :        &quot; &lt;&lt; sizeof(c0)  &lt;&lt; endl;
cout &lt;&lt; &quot;c1  : c0     &quot; &lt;&lt; sizeof(c1)  &lt;&lt; endl;
cout &lt;&lt; &quot;c2  : c0     &quot; &lt;&lt; sizeof(c2)  &lt;&lt; endl;
cout &lt;&lt; &quot;c3  : c1, c2 &quot; &lt;&lt; sizeof(c3)  &lt;&lt; endl;
</pre>
<p>このとき、sizeof(c3)は <strong>40</strong>となりました。仮想継承したベースクラスc0の各メンバは、c3内にはひとつしかないはずなので、予想されるサイズは (8&#215;4=)32<br />
であるはずなのに、8byte余計な領域を必要としています。仮想関数を元に戻し、</p>
<pre>
cout &lt;&lt; offsetof(c3, x1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, y1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, x2) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, y2) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, x3) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, y3) &lt;&lt; endl;
</pre>
<p>を実行すると、それぞれ 8, 12, 24, 28, 32, 36<br />
となりました。この結果から得られるメモリ・レイアウトは:</p>
<table summary="レイアウト">
<tbody>
<tr>
<th>offset</th>
<th>member</th>
</tr>
<tr>
<td>+0</td>
<td>???</td>
</tr>
<tr>
<td>+4</td>
<td>???</td>
</tr>
<tr>
<td>+8</td>
<td>c1::x1</td>
</tr>
<tr>
<td>+12</td>
<td>c1::y1</td>
</tr>
<tr>
<td>+16</td>
<td>???</td>
</tr>
<tr>
<td>+20</td>
<td>???</td>
</tr>
<tr>
<td>+24</td>
<td>c2::x2</td>
</tr>
<tr>
<td>+28</td>
<td>c2::y2</td>
</tr>
<tr>
<td>+32</td>
<td>c3::x3</td>
</tr>
<tr>
<td>+36</td>
<td>c3::y3</td>
</tr>
<tr>
<td>+40</td>
<td>???</td>
</tr>
<tr>
<td>+44</td>
<td>???</td>
</tr>
<tr>
<td>+48</td>
<td>???</td>
</tr>
</tbody>
</table>
<p>&apos;???&apos;の領域のうち、少なくとも2つはc0::x0とc0::y0でしょう。</p>
<p><strong>「C++はCよりデカい」</strong>か?</p>
<p>結論から言うと、仮想関数、多重継承、仮想継承などなどを駆使したC++コードは確かにデカくなることがわかりました。最後の例など、Cでなら32byteで済むところが、20byteも余計にメモリを消費します。インスタンスひとつにつき20byteのメモリを余計に消費するのですからアプリケーション全体では無視できないでしょうね。</p>
<p>しかし、だからといってC++コードから仮想関数を排除するのは決して得策とは思えません。仮想関数と同じふるまいを仮想関数なしで実現するには、オブジェクトの本来の型を識別するためのIDをメンバとして用意しなければなりませんし、関数呼び出しのたびに、IDによる処理の振り分けを行なうことになります。結局仮想関数を用いた場合と同等のサイズになることが予想されるうえ、コードのメンテナンス性が著しく低下するでしょう。</p>
<p class="note">※注意:<br />
これはMicrosoft Visual C++ v6.0での実験結果です。<br />
これ以外のOSや処理系では異なる結果が得られることでしょう。</p>
<h3>おまけ : やりがちな誤り</h3>
<p>仮想関数のお話ついでに、うっかりやってしまいそうな誤りをひとつ紹介しておきます。あなたはいくつかのクラスを設計/実装することになり、それぞれのコンストラクタ/デストラクタが確実に行われているか、画面に表示して確かめたくなりました。そこであなたはこんなコードを書きました。</p>
<pre>
class base {
public:
  base();
  virtual ‾base();
  <strong>virtual void on_ctor</strong>();
  <strong>virtual void on_dtor</strong>();
};

base::base() {
  on_ctor();
}

base::‾base() {
  on_dtor();
}

void base::on_ctor() {
  cout &lt;&lt; &quot;base::ctor¥n&quot;;
}

void base::on_dtor() {
  cout &lt;&lt; &quot;base::dtor¥n&quot;;
}
</pre>
<p>class baseはそのコンストラクタ/デストラクタの中からそれぞれ仮想関数on_ctor()/on_dtor()を呼んでいます。ですからbaseの派生クラス側でon_ctor()/on_dtor()を再定義すればいいはずです。</p>
<pre>
class c0 : public base {
public:
  <strong>virtual void on_ctor</strong>();
  <strong>virtual void on_dtor</strong>();
};

void c0::<strong>on_ctor</strong>() {
  cout &lt;&lt; &quot;c0::ctor¥n&quot;;
}

void c0::<strong>on_dtor</strong>() {
  cout &lt;&lt; &quot;c0::dtor¥n&quot;;
}

class c1 : public base {
public:
  <strong>virtual void on_ctor</strong>();
  <strong>virtual void on_dtor</strong>();
};

void c1::<strong>on_ctor</strong>() {
  cout &lt;&lt; &quot;c1::ctor¥n&quot;;
}

void c1::<strong>on_dtor</strong>() {
  cout &lt;&lt; &quot;c1::dtor¥n&quot;;
}

int main() {
  c0 x;
  c1 y;
  return 0;
}
</pre>
<p>さて、結果はどうなったでしょう&#8230;</p>
<pre>
base::ctor
base::ctor
base::dtor
base::dtor
</pre>
<p>惨澹たる結果となりました。</p>
<p>そう、C++ではコンストラクタ/デストラクタの中から仮想関数を呼んでも、導出クラスで再定義された関数には飛んでこないんです。</p>
<p>この例のように、コンストラクタ/デストラクタから直接仮想関数を呼んでいるのであればそれに気づくのにそう時間はかからないのですが、コンストラクタ/デストラクタが呼んでいる関数の中、あるいはさらにそこから呼ばれている関数&#8230;<br />
コンストラクタ/デストラクタが終了するまでに直接的あるいは間接的に呼ばれている一連の関数の中に仮想関数が含まれていたとき、そのふるまいは意図しているものとは異なることになるのです。</p>
<p>コンストラクタ/デストラクタ内から仮想関数を呼び出すことはできませんから、仕方なしにコンストラクト/デストラクトと初期化/後始末を分離しなければなりません。</p>
<pre>
// ----- これならちゃんと動く,,, -----
class base {
public:
  base() {}
  virtual ‾base() {}
  void    <strong>start</strong>();
  void    <strong>stop</strong>();
  virtual void on_ctor();
  virtual void on_dtor();
};

void base::<strong>start</strong>() {
  <strong>on_ctor</strong>();
}

void base::<strong>stop</strong>() {
  <strong>on_dtor</strong>();
}

void base::on_ctor() {
  cout &lt;&lt; &quot;base::ctor¥n&quot;;
}

void base::on_dtor() {
  cout &lt;&lt; &quot;base::dtor¥n&quot;;
}

/*
 * c0, c1 はそのまま...
 */

int main() {
  c0 x; x.<strong>start</strong>();
  c1 y; y.<strong>start</strong>();
  y.<strong>stop</strong>();
  x.<strong>stop</strong>();
  return 0;
}
</pre>
<p>これがJavaだと思った通りに動いてくれます。ま、Javaにはデストラクタがありませんけど&#8230;</p>
<pre>
// ----- Trial.java -----
class base {
  base() {
    on_ctor();
  }
  void on_ctor() {
    System.out.println(&quot;base::ctor&quot;);
  }
  protected void finalize() {
    System.out.println(&quot;base::dtor&quot;);
  }
}

class c0 extends base {
  void on_ctor() {
    System.out.println(&quot;c0::ctor&quot;);
  }
  protected void finalize() {
    System.out.println(&quot;c0::dtor&quot;);
  }
}

class c1 extends base {
  void on_ctor() {
    System.out.println(&quot;c1::ctor&quot;);
  }
  protected void finalize() {
    System.out.println(&quot;c1::dtor&quot;);
  }
}

public class Trial {
  public static void main(String[] arg) throws Exception {
    c0 x = new c0();
    c1 y = new c1();
  }
}
</pre>
<p>実行結果:</p>
<pre>
c0::ctor
c1::ctor
</pre>
<p>&#8230;ほらね。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/sizeof/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++の新しいキャスト</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/newcast/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/newcast/#comments</comments>
		<pubDate>Mon, 02 Apr 2007 05:59:48 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=202</guid>
		<description><![CDATA[従来のキャストの問題点 異なる型への変換において、C/C++ではキャストが用いられます。 // intからlongへのキャスト int ival; int lval = (long)ival; ご存知のとおり、キャストは非常に危険です。 本来ならば型の不一致によるコンパイルエラーをねじ伏せるのですから。 キャストの使われ方(意味)は、大きく3種(型変換/型変更/const外し)に分類されます。 型変換 // int から double へ int ival; double dval = (double)ival; 型変更 // long から int* へ long lval; int* iptr = (int*)lval; const外し // const int* から int* へ const int* ciptr; int* iptr = (int*)ciptr; 上記のどの目的でキャストしても、構文としてはどれも同じ (型)式 です。つまりコードからは&#8221;何が目的でキャストしたのかはっきりしない&#8220;のですね。 4種類の新しいキャスト [...]]]></description>
			<content:encoded><![CDATA[<h3>従来のキャストの問題点</h3>
<p>異なる型への変換において、C/C++ではキャストが用いられます。</p>
<pre>// intからlongへのキャスト
int ival;
int lval = (long)ival;</pre>
<p>ご存知のとおり、キャストは<strong>非常に危険</strong>です。 本来ならば型の不一致によるコンパイルエラーをねじ伏せるのですから。</p>
<p><span id="more-202"></span></p>
<p>キャストの使われ方(意味)は、大きく3種(型変換/型変更/const外し)に分類されます。</p>
<ol>
<li>型変換
<pre>// int から double へ
int ival;
double dval = (double)ival;</pre>
</li>
<li>型変更
<pre>// long から int* へ
long lval;
int* iptr = (int*)lval;</pre>
</li>
<li>const外し
<pre>// const int* から int* へ
const int* ciptr;
int* iptr = (int*)ciptr;</pre>
</li>
</ol>
<p>上記のどの目的でキャストしても、構文としてはどれも同じ</p>
<pre>(型)式</pre>
<p>です。つまりコードからは&#8221;何が目的でキャストしたのか<strong>はっきりしない</strong>&#8220;のですね。</p>
<h3>4種類の新しいキャスト</h3>
<p>キャストは使わずに済ませられるならそれに越したとはないのですが、どうしても使わざるを得ないシチュエーションは少なからず存在します。<br />
ならばせめて少しでも安全にキャストするために新たに定められたキャスト構文が、以下に挙げる<strong>static_cast</strong> / <strong>reinterpret_cast</strong> / <strong>const_cast</strong> / <strong>dynamic_cast</strong> です。</p>
<h4>static_cast</h4>
<pre><strong>static_cast</strong>&lt;type&gt;(expr)</pre>
<p><strong>static_cast</strong>は<em>expr</em>の型から<em>type</em>への暗黙の型変換、あるいは<em>type</em>から<em>expr</em>への暗黙の型変換が存在する場合にだけキャストします。キャスト不可能であればコンパイルエラーとなります。</p>
<pre>// int から long へ
int ival;
long lval = <strong>static_cast</strong>&lt;long&gt;(ival);</pre>
<h4>reinterpret_cast</h4>
<pre><strong>reinterpret_cast</strong>&lt;type&gt;(expr)</pre>
<p><strong>reinterpret_cast</strong>は<em>type</em>(<em>expr</em>)が許されるなら、<em>expr</em>を<em>type</em>に単にキャストします。</p>
<pre>// long から int* へ
long lval;
int* iptr = <strong>reinterpret_cast</strong>&lt;int*&gt;(lval);</pre>
<p><strong>reinterpret_cast</strong>は単なる型変更であり、たとえ派生関係があったとしてもポインタのアドレス自体はキャスト前と変わりません。<br />
その意味で<strong>reinterpret_cast</strong>は非常に危険なキャストといえるでしょう。</p>
<h4>const_cast</h4>
<pre><strong>const_cast</strong>&lt;<em>type</em>&gt;(<em>expr</em>)</pre>
<p><strong>const_cast</strong>はconstおよびvolatile修飾子を無効にするだけのキャストを行ないます。そのほかのときはコンパイルエラーとします。</p>
<pre>// const int* から int* へ
const int* ciptr;
int* iptr = <strong>const_cast</strong>&lt;int*&gt;(ciptr);</pre>
<h4>dynamic_cast</h4>
<pre><strong>dynamic_cast</strong>&lt;type&gt;(expr)</pre>
<p><strong>dynamic_cast</strong>は基底クラスへのポインタ(or 参照)から派生クラスへのポインタ(or 参照)への型保証キャストを行ないます。</p>
<p>上記3種のキャストはコンパイル時にキャストしますが、<strong>dynamic_cast</strong>は実行時に型の検査が行なわれ、変換不可能であれば0を返します。</p>
<pre>class Base { ... };
class Derived : public Base { ... };

Base base;
Derived derived;

Derived* pd1 = <strong>dynamic_cast</strong>&lt;Derived*&gt;(&amp;base); // 失敗 pd1 == 0
Derived* pd2 = <strong>dynamic_cast</strong>&lt;Derived*&gt;(&amp;derived);
</pre>
<p>参照のキャストに失敗すると、std::bad_cast例外がthrowされます。</p>
<pre>
try {
  Derived&amp; rd1 = <strong>dynamic_cast</strong>&lt;Derived&amp;&gt;(base);
} catch ( const std::bad_cast&amp; e ) {
  std::cout &lt;&lt; e.what() &lt;&lt; std::endl;
};</pre>
<p><strong>dynamic_cast</strong>により、従来のキャストでは不可能であった クロス・キャスト、そして抽象基底クラスからのダウン・キャストが可能になりました。</p>
<h5>クロス・キャスト</h5>
<pre>/*
 * Shape と Drawable は派生関係にない
 */
class Shape {
 ...
};

class Drawable {
public:
  virtual void draw() =0;
};

/*
 * Circle は Drawable にキャスト可能
 */
class Circle : public Shape, public Drawable {
public:
  virtual void draw();
};

Shape* shape = new Circle;
/* 従来のキャスト (Drawable*)shape では、
 * 場合によっては暴走する */
Drawable* drawable = <strong>dynamic_cast</strong>&lt;Drawable*&gt;(shape);
if ( drawable )
  drawable-&gt;draw();</pre>
<h5>抽象基底クラスからのダウン・キャスト</h5>
<pre>class Machine {
  ...
};

class TV : virtual public Machine {
public:
  void tune(int channel);
};

class VTR : virtual public Machine {
public:
  void record();
};

class TeleVideo : public TV, public VTR {
  ...
};

Machine* machine = new TeleVideo;
/* 従来のキャスト (TeleVideo*)machine はコンパイルエラー */
TeleVideo* televideo = <strong>dynamic_cast</strong>&lt;TeleVideo*&gt;(machine);
if ( televideo ) {
  televideo-&gt;tune(8);
  televideo-&gt;record();
}</pre>
<h4>※注意</h4>
<p><strong>dynamic_cast</strong>が適用できるのはポリモルフィック・クラス、 すなわち少なくとも一つのメンバ関数が仮想関数でなくてはなりません。</p>
<pre>struct non_polymorpic { ... }; // 仮想関数が存在しない
struct something : non_polumorphic { ... };
non_polymorphic* p;
something* q = <strong>dynamic_cast</strong>&lt;someting&gt;(p); // error!</pre>
<p>新しいキャストは従来のキャストよりはるかに安全です。<br />
また､grepなどで&#8221;_cast&#8221;をキーにすればキャストした個所を簡単に検索できるという副次的な効果もあります。</p>
<p><!--more--></p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/newcast/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>9章: クラス RWBTreeOnDisk の使い方</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-9/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-9/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:58:28 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=1358</guid>
		<description><![CDATA[9.1 生成 9.2 例 RWBTreeOnDisk クラスは、ディスク上のファイルで B ツリーを管理するために設計されています。このクラスは、キーと値の関連付けを順序よく並べたコレクションを表わします。その順序はキーを比較することによって内部的に決定されます。キーを指定すると、値を取り出すことができます。キーの重複は許可されません。 キーは char の配列で、キーの長さはコンストラクタによって設定されます。B ツリー内の順序は、キーを外部関数 (変更可能) と比較することによって決定されます。 値の型は次のとおりです。 typedef long RWstoredValue; 値は通常、オブジェクトが格納されているファイル内の場所へのオフセットを表わします。キーを指定すると、オブジェクトが格納されている位置を見つけ、それを取り出すことができます。ただし、RWBTreeOnDisk クラスに関するかぎり、この値は特別な意味を持ちません。これを解釈するのはプログラマ次第です。 RWBTreeOnDisk クラスは、RWFileManager クラスを使って B ツリーノードの領域の割り当ておよび解放を管理します。B ツリーとデータが同じファイル内にある場合は、同じ RWFileManager を使って、オブジェクト自体の領域を管理できます。または、別の RWFileManager を使って別のファイルを管理し、B ツリーとデータを別々のファイルに格納することも可能です。 RWBTreeOnDisk クラスのメンバ関数は、メモリにおける RWBTreeDictionary クラスのメンバ関数と似ています。違う点は、キーが RWCollectable ではなく、char の配列であることです。メンバ関数には、キーと値のペアを追加する、ペアを削除する、キーに関連付けられている値を置き換える、キーに関連付けられている情報を照会する、キーと値の全ペアで順序正しく操作する、ツリーに項目数を返す、キーがツリーに含まれているかどうかを決定する、などの関数があります。]]></description>
			<content:encoded><![CDATA[<p><span id="more-1358"></span></p>
<ul>
<li><a href="/cpptechdoc/reference/tools-9-1/">9.1 生成</a></li>
<li><a href="/cpptechdoc/reference/tools-9-2/">9.2 例</a></li>
</ul>
<p><code>RWBTreeOnDisk</code> クラスは、ディスク上のファイルで B ツリーを管理するために設計されています。このクラスは、キーと値の関連付けを順序よく並べたコレクションを表わします。その順序はキーを比較することによって内部的に決定されます。キーを指定すると、値を取り出すことができます。キーの重複は許可されません。</p>
<p>キーは char の配列で、キーの長さはコンストラクタによって設定されます。B ツリー内の順序は、キーを外部関数 (変更可能) と比較することによって決定されます。</p>
<p>値の型は次のとおりです。</p>
<pre>
typedef long RWstoredValue;
</pre>
<p>
値は通常、オブジェクトが格納されているファイル内の場所へのオフセットを表わします。キーを指定すると、オブジェクトが格納されている位置を見つけ、それを取り出すことができます。ただし、<code>RWBTreeOnDisk</code> クラスに関するかぎり、この値は特別な意味を持ちません。これを解釈するのはプログラマ次第です。</p>
<p><code>RWBTreeOnDisk</code> クラスは、<code>RWFileManager</code> クラスを使って B ツリーノードの領域の割り当ておよび解放を管理します。B ツリーとデータが同じファイル内にある場合は、同じ <code>RWFileManager</code> を使って、オブジェクト自体の領域を管理できます。または、別の <code>RWFileManager</code> を使って別のファイルを管理し、B ツリーとデータを別々のファイルに格納することも可能です。</p>
<p><code>RWBTreeOnDisk</code> クラスのメンバ関数は、メモリにおける <code>RWBTreeDictionary</code> クラスのメンバ関数と似ています。違う点は、キーが <code>RWCollectable</code> ではなく、char の配列であることです。メンバ関数には、キーと値のペアを追加する、ペアを削除する、キーに関連付けられている値を置き換える、キーに関連付けられている情報を照会する、キーと値の全ペアで順序正しく操作する、ツリーに項目数を返す、キーがツリーに含まれているかどうかを決定する、などの関数があります。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>18.5 多重継承</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-18-5/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-18-5/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2664</guid>
		<description><![CDATA[第 15章「RWCollectable クラスの設計」で、RWCollectable を継承する Bus クラスを構築しました。すでに Bus クラスが存在すると仮定すると、以下のように多重継承を使って、Bus と RWCollectable の機能を持つ新しいクラスを作成し、手間を省くことができたはずです。 class CollectableBus : public RWCollectable, public Bus { . . . }; これは、Rogue Wave の多くのコレクションクラスで取られている手段です。例えば、RWCollectableString は RWCollectable と RWCString の両方のクラスを継承します。一般的な概念は、まずオブジェクトを作成し、それを RWCollectable クラスに付け加えて、全体をコレクション可能にすることです。この方法を使うと、RWCollectable クラスを継承したくないような他の場合にもオブジェクトを使用できます。 この手段をとる別の理由は、曖昧な基底クラスを避けることにあります。次に例を示します。 class A { }; class B : public A { }; class C : public A { }; class D [...]]]></description>
			<content:encoded><![CDATA[<p><span id="more-2664"></span></p>
<p>
  第 <a href="http://www.s34.co.jp/cpptechdoc/reference/tools-15/">15</a>章「RWCollectable クラスの設計」で、<strong>RWCollectable</strong> を継承する <strong>Bus</strong> クラスを構築しました。すでに Bus クラスが存在すると仮定すると、以下のように多重継承を使って、<strong>Bus</strong> と <strong>RWCollectable</strong> の機能を持つ新しいクラスを作成し、手間を省くことができたはずです。
</p>
<pre>
class CollectableBus : public RWCollectable, public Bus {
  .
  .
  .
};
</pre>
<p>
  これは、Rogue Wave の多くのコレクションクラスで取られている手段です。例えば、<strong>RWCollectableString</strong> は <strong>RWCollectable</strong> と <strong class="c3">RWCString</strong> の両方のクラスを継承します。一般的な概念は、まずオブジェクトを作成し、それを <strong>RWCollectable</strong> クラスに付け加えて、全体をコレクション可能にすることです。この方法を使うと、<strong>RWCollectable</strong> クラスを継承したくないような他の場合にもオブジェクトを使用できます。
</p>
<p>
  この手段をとる別の理由は、曖昧な基底クラスを避けることにあります。次に例を示します。
</p>
<pre>
class A { };

class B : public A { };

class C : public A { };

class D : public B, public C { };

void fun(A&amp;);

main () {
  D  d;
  fun(d);                                           // どちらの A ?
}
</pre>
<p>
  <code>fun()</code> の呼び出しから曖昧さを取り除くには、2 つのやり方があります。以下のように変更するか、
</p>
<pre>
fun((B)d);                         // B の、A の発生
</pre>
<p>
  または、A を仮想基底クラスにすることです。
</p>
<p>
  最初の方法は、適切なキャストを実行するためにユーザが継承階層の詳細を把握している必要があるため、エラーが発生しやすくなります。
</p>
<p>
  A を仮想関数にするという 2 番目のやり方でこの問題は解決しますが、別の問題が発生します。派生元のクラスへキャストし戻すのがほとんど不可能になります。これは、継承階層に複数のパスができてしまうからです。物理的に説明すると、コンパイラが仮想基底クラスを基底クラスへのポインタとして実装し、ポインタを逆方向にはたどれないからです。
</p>
<p>
  オブジェクトの継承階層で、一致を探して可能なパスをすべて検索しつくすことができます (これは、NIH クラスのやり方です)。ただし、キャストごとに検索しなければならないため、結果のアドレスを記憶することによって速度を上げたとしても、この検索は遅くなります。また、サイズが大きく常に複雑なため、この方法は受け入れられないと判断しました。
</p>
<p>
  これらの理由で最初の方法に戻りました。同じ基底クラスからすべてのクラスを派生しないようにして、継承ツリーを単純にして受け入れることができるようにしました。多くの機能を持つ大きな基底クラスではなく、機能を別々の小さな基底クラスに分けることにしました。
</p>
<p>
  目的は、まずオブジェクトを構築して、それから収集性などの必要な機能を基底クラスに付け加えることにあります。つまり、同じ型の複数基底クラスを避け、その結果、曖昧な呼び出しを避けることになります。
</p>
<p>　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-18-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>18.4 RWCollectable の格納と取り出しについて</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-18-4/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-18-4/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2657</guid>
		<description><![CDATA[以下のグローバル関係を用いて、クラスの参照関係あるいはポインタ関係を格納して取り出す方法は、14.5.1節で説明しました。 Rwvostream&#38; operator&#60;&#60;(RWvostream&#38;, const RWCollectable&#38;); RWFile&#38; operator&#60;&#60;(RWFile&#38;, const RWCollectable&#38;); Rwvostream&#38; operator&#60;&#60;(RWvostream&#38;, const RWCollectable*); RWFile&#38; operator&#60;&#60;(RWFile&#38;, const RWCollectable*); Rwvistream&#38; operator&#62;&#62;(RWvistream&#38;, RWCollectable&#38;); RWFile&#38; operator&#62;&#62;(RWFile&#38;, RWCollectable&#38;); Rwvistream&#38; operator&#62;&#62;(RWvistream&#38;, RWCollectable*&#38;); RWFile&#38; operator&#62;&#62;(RWFile&#38;, RWCollectable*&#38;); これらの関数がどのように機能するかを理解することは、RWCollectableで作業する上で大切です。まず、簡単に説明します。 初めてコレクションオブジェクトのどれかに対して左シフト演算子 &#60;&#60; の 1 つを呼び出すとき、アイデンティティ辞書が内部的に作成されます。オブジェクトのアドレスが、その出力ファイルでの順序(例えば、1 番目、2 番目、6 番目など) と共に辞書に納められます。 いったんこれが完了すると、オブジェクトの仮想関数である saveGuts() が呼び出されます。これが仮想関数であるため、呼び出しは派生クラスによって使用される saveGuts() の定義に行きます これまで説明したように、saveGuts() の役目はオブジェクトの内部コンポーネントを格納することです。オブジェクトに RWCollectable を継承する他のオブジェクトが含まれている場合、そのオブジェクトの saveGuts() は含まれている各オブジェクトごとに operator&#60;&#60;() を再帰的に呼び出します。 その後の operator&#60;&#60;() の呼び出しでは、新しくアイデンティティ辞書は作成されず、すでに存在している辞書にオブジェクトのアドレスが格納されます。アドレスがすでに書き込まれているオブジェクトのアドレスと同一である場合は、saveGuts() [...]]]></description>
			<content:encoded><![CDATA[<p><span id="more-2657"></span></p>
<p>
  以下のグローバル関係を用いて、クラスの参照関係あるいはポインタ関係を格納して取り出す方法は、<a href="http://www.s34.co.jp/cpptechdoc/reference/tools-14-5/#toc1451">14.5.1</a>節で説明しました。
</p>
<pre>
Rwvostream&amp;  operator&lt;&lt;(RWvostream&amp;, const RWCollectable&amp;);
RWFile&amp;      operator&lt;&lt;(RWFile&amp;,     const RWCollectable&amp;);
Rwvostream&amp;  operator&lt;&lt;(RWvostream&amp;, const RWCollectable*);
RWFile&amp;      operator&lt;&lt;(RWFile&amp;,     const RWCollectable*);
Rwvistream&amp;  operator&gt;&gt;(RWvistream&amp;, RWCollectable&amp;);
RWFile&amp;      operator&gt;&gt;(RWFile&amp;,     RWCollectable&amp;);
Rwvistream&amp;  operator&gt;&gt;(RWvistream&amp;, RWCollectable*&amp;);
RWFile&amp;      operator&gt;&gt;(RWFile&amp;,     RWCollectable*&amp;);
</pre>
<p>
  これらの関数がどのように機能するかを理解することは、<strong><em>RWCollectable</em>で作業する上で大切です。まず、簡単に説明します。</strong>
</p>
<p>
  初めてコレクションオブジェクトのどれかに対して左シフト演算子 <code>&lt;&lt;</code> の 1 つを呼び出すとき、アイデンティティ辞書が内部的に作成されます。オブジェクトのアドレスが、その出力ファイルでの順序(例えば、1 番目、2 番目、6 番目など) と共に辞書に納められます。
</p>
<p>
  いったんこれが完了すると、オブジェクトの仮想関数である <code>saveGuts()</code> が呼び出されます。これが仮想関数であるため、呼び出しは派生クラスによって使用される <code>saveGuts()</code> の定義に行きます これまで説明したように、<code>saveGuts()</code> の役目はオブジェクトの内部コンポーネントを格納することです。オブジェクトに <em>RWCollectable</em> を継承する他のオブジェクトが含まれている場合、そのオブジェクトの <code>saveGuts()</code> は含まれている各オブジェクトごとに <code>operator&lt;&lt;()</code> を再帰的に呼び出します。
</p>
<p>
  その後の <code>operator&lt;&lt;()</code> の呼び出しでは、新しくアイデンティティ辞書は作成されず、すでに存在している辞書にオブジェクトのアドレスが格納されます。アドレスがすでに書き込まれているオブジェクトのアドレスと同一である場合は、<code>saveGuts()</code> は呼び出されません。その代わり、そのオブジェクトが以前のオブジェクトと同一であるという参照が書き込まれます。
</p>
<p>
  コレクション全体が走査され、<code>saveGuts()</code> への最初の呼び出しが戻ると、アイデンティティ辞書が削除され、<code>operator&lt;&lt;()</code> への呼び出しが戻ります。
</p>
<p>
  <code>operator&gt;&gt;()</code> 関数は、restoreGuts を呼び出してオブジェクトをストリームまたはファイルからメモリに復元し、この処理全体を本質的に逆に行います。既に作成されているオブジェクトへの参照に出会うと、この関数は、<em>RWFactory</em> に新しいオブジェクトの作成を要求するのではなく、単に旧オブジェクトのアドレスを返します。
</p>
<p>
  以下は、これらの関数を使うより複雑な例です。
</p>
<pre>
#include &lt;rw/collect.h&gt;
#include &lt;rw/rwfile.h&gt;
#include &lt;assert.h&gt;

class Tangle : public RWCollectable
{

public:

  RWDECLARE_COLLECTABLE(Tangle)

  Tangle* nextTangle;
  int     someData;

  Tangle(Tangle* t = 0, int dat = 0){nextTangle=t; someData=dat;}

  virtual void saveGuts(RWFile&amp;) const;
  virtual void restoreGuts(RWFile&amp;);

};

void Tangle::saveGuts(RWFile&amp; file) const{
   RWCollectable::saveGuts(file);          // 抽象基底クラスを保存する

   file.Write(someData);                        // 内部を保存する

   file &lt;&lt; nextTangle;                      // 次のリンクを保存する
}

void Tangle::restoreGuts(RWFile&amp; file){
   RWCollectable::restoreGuts(file);    // 抽象基底クラスを復元する

  file.Read(someData);                       // 内部を復元する

  file &gt;&gt; nextTangle;                    // 次のリンクを復元する
}

// ヘッド "p" を持つヌル区切りのリストの内部統合性を調べる
void checkList(Tangle* p){
   int i=0;
   while (p)
   {
     assert(p-&gt;someData==i);
     p = p-&gt;nextTangle;
     i++;
   }
}

RWDEFINE_COLLECTABLE(Tangle, 100)

main(){
   Tangle *head = 0, *head2 = 0;

   for (int i=9; i &gt;= 0; i--)
     head = new Tangle(head,i);

   checkList(head);                    // オリジナルのリストを調べる

   {
     RWFile file("junk.dat");
     file &lt;&lt; head;
   }

   RWFile file2("junk.dat");
   file2 &gt;&gt; head2;

   checkList(head2);                   // 復元したリストを調べる
   return 0;
}
</pre>
<p>
  この例では、Tangle クラスが循環リンクリストを実装します。何が起こるでしょうか? 初めて <code>operator&lt;&lt;()</code> 関数が Tangle のインスタンスに呼び出されると、<code>operator&lt;&lt;()</code> 関数は前述したようにアイデンティティ辞書を設定し、Tangle の <code>saveGuts()</code> を呼び出します。ここで定義されているように、これは Tangle のメンバデータを格納し、次のリンクで <code>operator&lt;&lt;()</code> を呼び出します。この再帰性は、環状に継続します。
</p>
<p>
  チェーンが nil オブジェクトで終わる場合 (すなわち、nextTangle がゼロ) の場合、<code>operator&lt;&lt;()</code> はこれを内部的に記録し、再帰を停止します。
</p>
<p>
  その一方で、リストが環状である場合は、<code>operator&lt;&lt;()</code> が Tangle の最初にインスタンス (このチェーンすべてを開始したインスタンス)で再び呼び出されます。これが発生すると、<code>operator&lt;&lt;()</code> はこれはすでに処理したことがあるインスタンスであることを認識し、再び <code>saveGuts()</code> を呼び出すのではなく、前に書き込んだリンクへの参照を作成します。これにより、一連の再帰的な呼び出しが停止し、スタックが解放されます。
</p>
<p>
  チェーンは、同様な方法で復元されます。次の呼び出しにより、
</p>
<pre>
RWFile&amp; operator&gt;&gt;(RWFile&amp;, RWCollectable*&amp;);
</pre>
<p>
  新しいオブジェクトがヒープ領域に作成され、そのオブジェクトへのポインタ、前に読み込んだオブジェクトのアドレス、あるいはヌルポインタが返されます。最後の 2 つの場合は、再帰が停止し、スタックが解放されます。
</p>
<p>　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-18-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>6.6 まとめ</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-6-6/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-6-6/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2204</guid>
		<description><![CDATA[この章では、ストリームのバイトの最終的な出力先がメモリかディスクかを考えることなく、オブジェクトをどのようにストリームに格納し、読み出すかについて述べました。また、ストリームの最終形式が ASCII かバイナリかを考慮する必要もありません。 また、RWpostream および Rwpistream のように、独自の特殊仮想ストリームクラスを書くこともできます。仮想ストリームの大きな利点は、独自の特殊仮想ストリームを書く場合に、クライアントクラスのコードをまったく変更する必要がないことです。つまり、単にストリームクラスを引数として使用するだけです。 RWvostream&#38; operator&#60;&#60;(RWvostream&#38;, const ClassName&#38;); RWvistream&#38; operator&#62;&#62;(RWvistream&#38;, ClassName&#38;); オブジェクトを仮想ストリームに格納し読み出すほかに、すべてのクラス自体をバイナリで RWFile に格納し読み出すことができます。このファイルは、ANSI-C スタイルのファイル I/O をカプセル化します。ストリーム I/O に比べその機能に、より多くの制限がありますが、仮想関数のディスパッチを必要としないため、この形式でディスクへ格納し読み出す方がやや速くなります。]]></description>
			<content:encoded><![CDATA[<p><span id="more-2204"></span></p>
<p>この章では、ストリームのバイトの最終的な出力先がメモリかディスクかを考えることなく、オブジェクトをどのようにストリームに格納し、読み出すかについて述べました。また、ストリームの最終形式が ASCII かバイナリかを考慮する必要もありません。</p>
<p>また、<strong>RWpostream</strong> および <strong>Rwpistream</strong> のように、独自の特殊仮想ストリームクラスを書くこともできます。仮想ストリームの大きな利点は、独自の特殊仮想ストリームを書く場合に、クライアントクラスのコードをまったく変更する必要がないことです。つまり、単にストリームクラスを引数として使用するだけです。</p>
<pre>
RWvostream&amp; operator&lt;&lt;(RWvostream&amp;, const <em>ClassName</em>&amp;);
RWvistream&amp; operator&gt;&gt;(RWvistream&amp;,       <em>ClassName</em>&amp;);
</pre>
<p>オブジェクトを仮想ストリームに格納し読み出すほかに、すべてのクラス自体をバイナリで <strong>RWFile</strong> に格納し読み出すことができます。このファイルは、ANSI-C スタイルのファイル I/O をカプセル化します。ストリーム I/O に比べその機能に、より多くの制限がありますが、仮想関数のディスパッチを必要としないため、この形式でディスクへ格納し読み出す方がやや速くなります。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-6-6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>6.4 DDE の例</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-6-4/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-6-4/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2198</guid>
		<description><![CDATA[次の例はさらに複雑なもので、Windows DDE 機能を介して、RWDDEstreambuf クラスを使い、RWBinaryTree を交換する方法を示します。Windows クリップボードの場合にも、同様なテクニックを使用することができます。 #include &#60;rw/bintree.h&#62; #include &#60;rw/collstr.h&#62; #include &#60;rw/bstream.h&#62; #include &#60;rw/winstrea.h&#62; #include &#60;windows.h&#62; #include &#60;dde.h&#62; BOOL PostCollection(HWND hwndServer, WORD cFormat){ RWBinaryTree sc; // 1 sc.insert(new RWCollectableString("Mary")); sc.insert(new RWCollectableString("Bill")); sc.insert(new RWCollectableString("Pierre")); // RWDDEstreambuf を割り当て、それを使って初期化する // RWbostream: RWDDEstreambuf* sbuf = new RWDDEstreambuf(cFormat, // 2 FALSE, // 3 TRUE, // 4 TRUE); // 5 RWbostream [...]]]></description>
			<content:encoded><![CDATA[<p><span id="more-2198"></span></p>
<p>次の例はさらに複雑なもので、Windows DDE 機能を介して、<strong>RWDDEstreambuf</strong> クラスを使い、<strong>RWBinaryTree</strong> を交換する方法を示します。Windows クリップボードの場合にも、同様なテクニックを使用することができます。</p>
<pre>
#include &lt;rw/bintree.h&gt;
#include &lt;rw/collstr.h&gt;
#include &lt;rw/bstream.h&gt;
#include &lt;rw/winstrea.h&gt;
#include &lt;windows.h&gt;
#include &lt;dde.h&gt;

BOOL
PostCollection(HWND hwndServer, WORD cFormat){
   RWBinaryTree sc;                                          // 1
   sc.insert(new RWCollectableString("Mary"));
   sc.insert(new RWCollectableString("Bill"));
   sc.insert(new RWCollectableString("Pierre"));

   // RWDDEstreambuf を割り当て、それを使って初期化する
   // RWbostream:
   RWDDEstreambuf* sbuf = new RWDDEstreambuf(cFormat,        // 2
                                             FALSE,          // 3
                                             TRUE,           // 4
                                             TRUE);          // 5
   RWbostream bostr( sbuf );                                 // 6

   // コレクションを Rwbostream に格納する:
   bostr &lt;&lt; sc;                                              // 7

   // 出力ストリームをロックし、そのハンドルを取得する:
   HANDLE hDDEData = sbuf-&gt;str();                            // 8

   // DDE メッセージを同定するアトムを取得する:
   ATOM atom = GlobalAddAtom("SortedNames");                 // 9

   // DDE の応答をポストする:
   return PostMessage(0xFFFF, WM_DDE_DATA, hwndServer,       //10
                      MAKELONG(hDDEData, atom));
}
</pre>
<p>のコードでは、大きなメモリモデルが前提とされています。次に、コードの説明を行ごとに示します。</p>
<dl>
<dt>//1</dt>
<dd><strong>RWBinaryTree</strong> が構築され、それに項目が挿入されます。</dd>
<dt>//2-//5</dt>
<dd><strong>RWDDEstreambuf</strong> が割り当てられます。コンストラクタはいくつかの引数をとります。最初の引数は Windows クリップボードの形式です。この例では、形式の種類が引数として渡されていますが、一般には Windows に形式を登録し (RegisterClipboardFormat() を使う)、それを使用します。<br />
  その他の引数は、DDE データ交換およびメモリ管理に関連しています。引数の一覧に関しては『<cite>Tools.h++ Class Reference</cite>』を参照してください。それらの意味については、『<cite>Programming Windows 3.0</cite>』、または 『<cite>Microsoft Windows Guide to Programming</cite>』を参照してください。</dd>
<dt>//6</dt>
<dd><strong>RWbostream</strong> が与えられた <strong>RWDDEstreambuf</strong> から生成されます。ここで <strong>RWpostream</strong> を使うこともできますが、DDE 交換は同じマシンアーキテクチャ内で行われるので、余分な手間のかかる移植可能な ASCII 形式を使っても意味がありません。また、<strong>streambuf</strong> の型によって設定されるバイトの状態が、どのように <strong>Rwvostream</strong>   の型によって設定される形式から切り離されるかに注意してください。</dd>
<dt>//7</dt>
<dd>コレクションは <strong>Rwbostream</strong> に保存されます。<strong>RWbostream</strong>と関連付けられている <strong>streambuf</strong> は実際には <strong>RWDDEstreambuf</strong> であるため、コレクションは GMEM_DDESHARE 属性を持つ Windows のグローバルメモリ割り当て領域に保存されます。この割り当て領域は、オーバーフローすると自動的にサイズ変更されます。その他の <strong>strstreambuf</strong> と同様、メンバ関数 setbuf() を使って割り当て領域の大きさを変更できます。</dd>
<dt>//8</dt>
<dd><strong>RWDDEstreambuf</strong> がロックされます。いったん str() を使ってロックしたら、他の <strong>strstreambuf</strong> と同様、この <strong>streambuf</strong> を再び使用することはできません。ただし、RWDDEstreambuf::str() は char* ではなく、ハンドルを返すことに注意してください。このハンドルは、返す前にロック解除されます。</dd>
<dt>//9</dt>
<dd>この DDE データを同定するためにアトムを生成します。</dd>
<dt>//10</dt>
<dd>RWDDEstreambuf::str() によって返されたハンドルが、アトムを同定するとともにポストされます。</dd>
</dl>
<p>クリップボードを介したデータのやり取りには、より簡単な同様の方法を使用できます。</p>
<p>特殊化された <strong>streambuf</strong> の <strong>RWCLIPstreambuf</strong> と <strong>RWDDEstreambuf</strong> は、Rogue Wave 仮想ストリーム機能と共に使わなければならないものではありません。それらは通常の <strong>istream</strong> および <strong>ostream</strong> と共に、きわめて簡単に使用することができます。ただし、実行時に形式を設定することができません。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-6-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>6.3 Windows クリップボードと DDE Streambuf</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-6-3/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-6-3/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2193</guid>
		<description><![CDATA[前の節では、仮想ストリーム機能が、ストリームに挿入された項目の形式をどのように抽象化するかについて説明しました。ストリームに挿入された項目の状態も抽象化されました。これは使用した streambuf の型によって設定されます。 streambuf クラスは、iostream 機能の基礎となるシーケンス層です。これは、文字の順序の生成と利用を行います。コンパイラに付属のものには、いくつかの種類の streambuf があります。例えば、filebuf クラスは最終的に文字をファイルに入力および出力します。strstreambuf クラスは、メモリベースの文字ストリームで入力および出力をします。つまり、ANSI-C の sprintf() 関数と同等の iostream と考えることができます。今回、Tools.h++ には次の Windows ベースの拡張機能が付け加えられました。 Windows クリップボードでの入力および出力を行うための RWCLIPstreambuf クラス Windows の DDE (動的データ交換) 機能を経由して入力および出力を行うためのRWDDEstreambuf クラス これらのクラスは、バッファがオーバーフローまたはアンダーフローすると、Windows からのメモリの割り当てと解放を行います。RWDDEstreambuf クラスの場合は、関連する DDEDATA ヘッダも自動的に書き込まれます。Rogue Wave の仮想ストリームクラスと同様に、istream と ostream を含め、ios クラスを継承したすべてのクラスはこれらの streambuf と共に使用できます。 例えば、複雑な構造を一般のディスクベースのファイルに格納するためのコードは、DDE 機能でその構造を別のアプリケーションに転送するためにも使用できるということです。]]></description>
			<content:encoded><![CDATA[<p>前の節では、仮想ストリーム機能が、ストリームに挿入された項目の形式をどのように抽象化するかについて説明しました。ストリームに挿入された項目の状態も抽象化されました。これは使用した <strong>streambuf</strong> の型によって設定されます。</p>
<p><strong>streambuf</strong> クラスは、iostream 機能の基礎となるシーケンス層です。これは、文字の順序の生成と利用を行います。コンパイラに付属のものには、いくつかの種類の <strong>streambuf</strong> があります。例えば、<strong>filebuf</strong> クラスは最終的に文字をファイルに入力および出力します。<strong>strstreambuf</strong> クラスは、メモリベースの文字ストリームで入力および出力をします。つまり、ANSI-C の sprintf() 関数と同等の <strong>iostream</strong> と考えることができます。今回、<em>Tools.h++</em> には次の Windows ベースの拡張機能が付け加えられました。</p>
<ul>
<li>Windows クリップボードでの入力および出力を行うための <strong>RWCLIPstreambuf</strong> クラス</li>
<li>Windows の DDE (動的データ交換) 機能を経由して入力および出力を行うための<strong>RWDDEstreambuf</strong> クラス</li>
</ul>
<p>これらのクラスは、バッファがオーバーフローまたはアンダーフローすると、Windows からのメモリの割り当てと解放を行います。<strong>RWDDEstreambuf</strong> クラスの場合は、関連する DDEDATA ヘッダも自動的に書き込まれます。Rogue Wave の仮想ストリームクラスと同様に、<strong>istream</strong> と <strong>ostream</strong> を含め、<strong>ios</strong> クラスを継承したすべてのクラスはこれらの <strong>streambuf</strong> と共に使用できます。</p>
<p>例えば、複雑な構造を一般のディスクベースのファイルに格納するためのコードは、DDE 機能でその構造を別のアプリケーションに転送するためにも使用できるということです。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-6-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>6.2 簡単な例</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-6-2/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-6-2/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2188</guid>
		<description><![CDATA[次に、抽象基底クラス RWvostream と RWvistream を通して、RWbostream と RWbistream を用いる簡単な例を示します。 #include &#60;rw/bstream.h&#62; #include &#60;rw/cstring.h&#62; #include &#60;fstream.h&#62; #ifdef __BORLANDC__ # define MODE ios::binary // 1 #else # define MODE 0 #endif void save(const RWCString&#38; a, RWvostream&#38; v){ v &#60;&#60; a; // 仮想出力ストリームに保存する } RWCString recover(RWvistream&#38; v) { RWCString dupe; v &#62;&#62; dupe; // 仮想入力ストリームから復元する return dupe; } main(){ [...]]]></description>
			<content:encoded><![CDATA[<p>次に、抽象基底クラス <strong>RWvostream</strong> と <strong>RWvistream</strong> を通して、<strong>RWbostream</strong> と <strong>RWbistream</strong> を用いる簡単な例を示します。</p>
<pre>
#include &lt;rw/bstream.h&gt;
#include &lt;rw/cstring.h&gt;
#include &lt;fstream.h&gt;

#ifdef __BORLANDC__
# define MODE ios::binary                                    // 1
#else
# define MODE 0
#endif

void save(const RWCString&amp; a, RWvostream&amp; v){
  v &lt;&lt; a;                    // 仮想出力ストリームに保存する
}

RWCString recover(RWvistream&amp; v) {
   RWCString dupe;
   v &gt;&gt; dupe;            // 仮想入力ストリームから復元する
   return dupe;
}

main(){
   RWCString a("A string with\ttabs and a\nnewline.");

   {
     ofstream f("junk.dat", ios::out|MODE);                  // 2
     RWbostream bostr(f);                                    // 3
     save(a, bostr);
   }                                                         // 4

   ifstream f("junk.dat", ios::in|MODE);                     // 5
   RWbistream bistr(f);                                      // 6
   RWCString b = recover(bistr);                             // 7

   cout &lt;&lt; a &lt;&lt; endl;  // 2 つの文字列を比較する            // 8
   cout &lt;&lt; b &lt;&lt; endl;
   return 0;
}
</pre>
<p>出力:</p>
<pre>
A string with   tabs and a newline. A string with   tabs and a newline.
</pre>
<p>関数 save(const RWCString&amp; a, RWvostream&amp; v) の役目は、文字列 a を仮想出力ストリーム v に保存することです。関数 recover(RWvistream&amp;) はその結果を読み出します。これらの関数は、文字列の格納に使用される最終形式には関知しません。次に各行に対する注釈を示します。</p>
<dl>
<dt>//1-//2</dt>
<dd>この 2 行では、ファイル junk.dat に対してファイル出力ストリーム f が作成されます。多くの PC コンパイラのファイルオープン時のデフォルトモードは text であるため、DOS の自動改行変換を避けるために明示的にフラッグ ios::binary を使用しなければなりません。<a id="reference_1" href="#footnote_1" class="reference">[1]</a></dd>
<dt>//3</dt>
<dd>この行で、f から <strong>RWbostream</strong> が作成されます。</dd>
<dt>//4</dt>
<dd>この文は { &#8230; } で囲まれているので、f のデストラクタがここで呼び出されます。これにより、ファイルを閉じます。</dd>
<dt>//5</dt>
<dd>ファイルを再び入力のために開きます。</dd>
<dt>//6</dt>
<dd>f から <strong>RWbistream</strong> が作成されます。</dd>
<dt>//7</dt>
<dd>文字列がファイルから読み出されます。</dd>
<dt>//8</dt>
<dd>最後に、オリジナルの文字列と読み出された文字列を比較のために出力します。</dd>
</dl>
<p>このプログラムは、<strong>fstream</strong> クラスを使って簡略化することができます。<strong>fstream</strong> クラスは、出力と入力のために <strong>ofstream</strong> と <strong>ifstream</strong> を多重継承します。結果を読み込む前に、ファイルの先頭を見つける必要があります。seekg() の早期実装は、信頼性が証明されていないので、上記例ではこの手段がとられていません。</p>
<h4>注釈</h4>
<ol>
<li id="footnote_1"><a href="#reference_1" class="footnote">^</a> 多くの PC コンパイラでは、 ios::binary フラッグ付きでファイルが開かれていないかぎり、ostream::write() および istream::read() はテキスト変換を実行します。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-6-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

