<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Thinking more...-C/C++</title>
<link>http://blog.roodo.com/thinkingmore/archives/cat_82817.html</link>
<description>Just thinking more...&amp;nbsp;訂閱 RSS

var gaJsHost = ((&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;);
document.write(unescape(&quot;%3Cscript src=&#039;&quot; + gaJsHost + &quot;google-analytics.com/ga.js&#039; type=&#039;text/javascript&#039;%3E%3C/script%3E&quot;));















  google.load(&quot;jquery&quot;, &quot;1.2&quot;);
  google.setOnLoadCallback( function() {
    try {
      var pageTracker = _gat._getTracker(&quot;UA-97150-7&quot;);
      pageTracker._trackPageview();
    }catch(err) {}
    $(&quot;pre &gt; br&quot;).each( function() { $(this).replaceWith( &quot;\n&quot; ); } );
    $(&quot;textarea &gt; br&quot;).each( function() { $(this).replaceWith( &quot;\n&quot; ); } );
    SyntaxHighlighter.config.ClipboardSwf = &#039;http://alexgorbatchev.com/pub/sh/2.0.296/scripts/clipboard.swf&#039;;
    SyntaxHighlighter.all();
    dp.SyntaxHighlighter.ClipboardSwf = &#039;http://alexgorbatchev.com/pub/sh/2.0.296/scripts/clipboard.swf&#039;;
    dp.SyntaxHighlighter.HighlightAll(&#039;code&#039;);
  } );

</description>
<language>zh-tw</language>
<generator>Roodo Blog System</generator>
<copyright>All Rights Reserved</copyright>
<atom:link href="http://blog.roodo.com/thinkingmore/archives/cat_82817.xml" rel="self" type="application/rss+xml" />
<item>
	<title>會自殺的類別</title>
	<description><![CDATA[
			坦白說，我從沒想過物件可以自殺這件事情。當我看到這樣寫的時候，實在是很令我驚訝...實驗的結果，還真的是可以。


#include &lt;iostream&gt;

class SuicideSample {
        public:
                SuicideSample() { std::cout &lt;&lt; "ctor." &lt;&lt; std::endl; }
                ~SuicideSample() { std::cout &lt;&lt; "dtor." &lt;&lt; std::endl; }
                void DoIt( void ) { 
                        delete this;
                }
};

int main( int argc, char* argv[] )
{
        SuicideSample* obj=new SuicideSample();
        obj->DoIt();
        // of course, object can kill itself, but if you try to kill it again
        // program will crash.
        //delete obj;
        return 0;
}


		]]>
	</description>
	<content:encoded><![CDATA[
			坦白說，我從沒想過<b>物件可以自殺</b>這件事情。當我看到這樣寫的時候，實在是很令我驚訝...實驗的結果，還真的是可以。

<pre name="code" class="cpp">
#include &lt;iostream&gt;

class SuicideSample {
        public:
                SuicideSample() { std::cout &lt;&lt; "ctor." &lt;&lt; std::endl; }
                ~SuicideSample() { std::cout &lt;&lt; "dtor." &lt;&lt; std::endl; }
                void DoIt( void ) { 
                        delete this;
                }
};

int main( int argc, char* argv[] )
{
        SuicideSample* obj=new SuicideSample();
        obj->DoIt();
        // of course, object can kill itself, but if you try to kill it again
        // program will crash.
        //delete obj;
        return 0;
}
</pre>

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/9858209.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/9858209.html</guid>
	<category>C/C++</category>
	<pubDate>Fri, 28 Aug 2009 16:19:33 +0800</pubDate>
</item>
<item>
	<title>Skia and framebuffer</title>
	<description><![CDATA[
			根據 Jserv 大的淺談 Google Skia 圖形處理引擎，得知 skia 只能畫在 Memory buffer 上，那麼，可以直接畫在 Framebuffer 上嗎??

Jserv 大文章裡的例子，SkBitmap 得先呼叫 allocPixels() 來配置所需要的 Memory buffer，根據 SkBitmap.h 裡的宣告，allocPixels 事實上是使用 allocator 來配置所需要的 Memory buffer，如果未指定，會以 stdalloc(HeapAllocator) 來進行配置。因此如果要直接使用 framebuffer，可以繼承 SkBitmap::Allocator 類別之後，改寫 allocPixelRef() 來達到目的。

大致的代碼就像這樣子：
#include "SkTypes.h"
#include "SkRefCnt.h"
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkPaint.h"
#include "SkRect.h"
#include "SkMallocPixelRef.h"

class FrameBufferAllocator: public SkBitmap::Allocator
{
    public:
	FrameBufferAllocator();
	virtual ~FrameBufferAllocator();
        virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
    private:
        char* m_addr;
        int fd;
};

FrameBufferAllocator::FrameBufferAllocator()
{
}

FrameBufferAllocator::~FrameBufferAllocator()
{
  // munmap framebuffer pointer.
  // close the framebuffer device we opened
}

bool FrameBufferAllocator::allocPixelRef(SkBitmap* dst, SkColorTable* ctable)
{
    size_t size = dst-&gt;getSize();

    // open framebuffer
    fd = open( "/dev/fb0", O_RDWR );

    // setup framebuffer device via ioctl.

    // mmap framebuffer
    m_addr = (char *)mmap(0, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

    // call original procedures in HeapAllocator::
    dst-&gt;setPixelRef(new SkMallocPixelRef(m_addr, size, ctable))-&gt;unref();
    dst-&gt;lockPixels();
    return true;
}


接著，Jserv 大的範例只要修改一行：
	bitmap.allocPixels( new FrameBufferAllocator, NULL );


就可以順利運作了。

p.s. 在 link libskia.a 時，遇到很鳥的狀況，skia 的 Makefile 在製作時，是直接以帶有路徑的 .o 去作，而我 toolchain 的 gcc 居然無法處理，必須要以沒有路徑的 .o 去重新製作，這樣才能 link 成功。

		]]>
	</description>
	<content:encoded><![CDATA[
			根據 Jserv 大的<a href="http://blog.linux.org.tw/~jserv/archives/002095.html">淺談 Google Skia 圖形處理引擎</a>，得知 <a href="http://code.google.com/p/skia/">skia</a> 只能畫在 Memory buffer 上，那麼，可以直接畫在 Framebuffer 上嗎??<br/>
<br/>
Jserv 大文章裡的例子，SkBitmap 得先呼叫 allocPixels() 來配置所需要的 Memory buffer，根據 SkBitmap.h 裡的宣告，allocPixels 事實上是使用 allocator 來配置所需要的 Memory buffer，如果未指定，會以 stdalloc(HeapAllocator) 來進行配置。因此如果要直接使用 framebuffer，可以繼承 SkBitmap::Allocator 類別之後，改寫 allocPixelRef() 來達到目的。<br/>

大致的代碼就像這樣子：<pre name="code" class="c">
#include "SkTypes.h"
#include "SkRefCnt.h"
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkPaint.h"
#include "SkRect.h"
#include "SkMallocPixelRef.h"

class FrameBufferAllocator: public SkBitmap::Allocator
{
    public:
	FrameBufferAllocator();
	virtual ~FrameBufferAllocator();
        virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
    private:
        char* m_addr;
        int fd;
};

FrameBufferAllocator::FrameBufferAllocator()
{
}

FrameBufferAllocator::~FrameBufferAllocator()
{
  // munmap framebuffer pointer.
  // close the framebuffer device we opened
}

bool FrameBufferAllocator::allocPixelRef(SkBitmap* dst, SkColorTable* ctable)
{
    size_t size = dst-&gt;getSize();

    // open framebuffer
    fd = open( "/dev/fb0", O_RDWR );

    // setup framebuffer device via ioctl.

    // mmap framebuffer
    m_addr = (char *)mmap(0, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

    // call original procedures in HeapAllocator::
    dst-&gt;setPixelRef(new SkMallocPixelRef(m_addr, size, ctable))-&gt;unref();
    dst-&gt;lockPixels();
    return true;
}
</pre>

接著，Jserv 大的範例只要修改一行：<pre name="code" class="c">
	bitmap.allocPixels( new FrameBufferAllocator, NULL );
</pre>

就可以順利運作了。<br/>
<br/>
p.s. 在 link libskia.a 時，遇到很鳥的狀況，skia 的 Makefile 在製作時，是直接以帶有路徑的 .o 去作，而我 toolchain 的 gcc 居然無法處理，必須要以沒有路徑的 .o 去重新製作，這樣才能 link 成功。

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/9335647.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/9335647.html</guid>
	<category>C/C++</category>
	<pubDate>Mon, 29 Jun 2009 18:03:30 +0800</pubDate>
</item>
<item>
	<title>C 的 &amp;&amp; 與 ||</title>
	<description><![CDATA[
			前一陣子看Javascript 語言精髓與編程實踐這本書的時候，發現可以用 &amp;&amp; 來達到 if ，用 || 達到 if not 的效果，所以下面是以 C/C++ 實驗的結果：


#include &lt;stdio.h&gt;

int say_hello( void )
{
    printf("Hello world!!\n");
    return 0;
}

int main( int argc, char* argv[] )
{
    int flag=0;

    printf("flag=0\n");
    flag && say_hello(); // say_hello() won't be invoked
    flag || say_hello(); // say_hello() will be invoked

    printf("flag=1\n");
    flag=1;
    flag && say_hello(); // say_hello() will be invoked
    flag || say_hello(); // say_hello() won't be invoked

    return 0;
}


不過坦白說，這樣寫的結果是導致可讀性變差，只有自己維護這份 code 時，那是可以用，很多人維護一份 code 時，最好還是避免，或者，加上註解比較好。

我想 c# / java 應該也可以這樣作。

p.s. 如果不是呼叫函數的話，記得要使用 ( )，例如：
i && (j=100);
這就等同於：
if(i) j=100;

		]]>
	</description>
	<content:encoded><![CDATA[
			前一陣子看Javascript 語言精髓與編程實踐這本書的時候，發現可以用 &amp;&amp; 來達到 if ，用 || 達到 if not 的效果，所以下面是以 C/C++ 實驗的結果：

<pre name="code" class="c">
#include &lt;stdio.h&gt;

int say_hello( void )
{
    printf("Hello world!!\n");
    return 0;
}

int main( int argc, char* argv[] )
{
    int flag=0;

    printf("flag=0\n");
    flag && say_hello(); // say_hello() won't be invoked
    flag || say_hello(); // say_hello() will be invoked

    printf("flag=1\n");
    flag=1;
    flag && say_hello(); // say_hello() will be invoked
    flag || say_hello(); // say_hello() won't be invoked

    return 0;
}
</pre>

不過坦白說，這樣寫的結果是導致可讀性變差，只有自己維護這份 code 時，那是可以用，很多人維護一份 code 時，最好還是避免，或者，加上註解比較好。

我想 c# / java 應該也可以這樣作。

p.s. 如果不是呼叫函數的話，記得要使用 ( )，例如：
<pre name="code" class="c">i && (j=100);</pre>
這就等同於：
<pre name="code" class="c">if(i) j=100;</pre>

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/9316397.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/9316397.html</guid>
	<category>C/C++</category>
	<pubDate>Fri, 26 Jun 2009 22:35:42 +0800</pubDate>
</item>
<item>
	<title>Linux I2C 與 AD71471</title>
	<description><![CDATA[
			AD71471 的 Device address 是 0x58，Linux driver 在處理這個時，其實會自行左移一位，因此，在 I2C_SLAVE_FORCE 的 ioctl 裡，應該是要傳 0x2c。這邊因為我暈頭，把 0x58&gt;&gt;1 算成 0x4c，導致我搞了好一陣子，直到 M 同事指正以後，才弄對。

再來，AD71471 在做讀寫時，Register address 與 data 都是 2 bytes，而 i2ctools 裡，處理 Register address 都只傳 1 byte(I2C_SMBUS)，因此不適用在 AD71471 上。我把 Linux kernel i2c-core.c 裡的 code 翻出來改寫，改用 I2C_RDWR 來處理：
bool Write( uint16_t address, uint16_t value ) {
	int res=0;
	struct i2c_rdwr_ioctl_data msg_rdwr;
	char msgbuf0[I2C_SMBUS_BLOCK_MAX+4];
	char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
	struct i2c_msg msg[1] = { { _address, 0, 4, msgbuf0 } };
	uint8_t* pAddr = (uint8_t*)&address;
	uint8_t* pValue = (uint8_t*)&value;

	msg_rdwr.msgs = &msg[0];
	msg_rdwr.nmsgs = 1; // write // read = 2

	// 因為 little endian，所以要作調整
	msgbuf0[0] = *(pAddr+1);
	msgbuf0[1] = *(pAddr+0);
	msgbuf0[2] = *(pValue+1); // (1)
	msgbuf0[3] = *(pValue+0);

	res = ioctl( _file, I2C_RDWR, &msg_rdwr );
	usleep(10000);

	return true;
}
bool Read( uint16_t address, uint16_t& data ) {
	int res=0;
	struct i2c_rdwr_ioctl_data msg_rdwr;
	char msgbuf0[I2C_SMBUS_BLOCK_MAX+2];
	char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]={0};
	struct i2c_msg msg[2] = { { _address, 0, 2, msgbuf0 },
	                          { _address, I2C_M_RD, 2, msgbuf1 }
	                        };
	uint8_t* pAddr = (uint8_t*)&address;

	msg_rdwr.msgs = &msg[0];
	msg_rdwr.nmsgs = 1; // read = 2
	// 因為 little endian，所以要作調整
	msgbuf0[0] = *(pAddr+1);
	msgbuf0[1] = *(pAddr+0);
	res = ioctl( _file, I2C_RDWR, &msg_rdwr );
	usleep(10000);

	msg_rdwr.msgs = &msg[1];
	msg_rdwr.nmsgs = 1; // read = 2
	res = ioctl( _file, I2C_RDWR, &msg_rdwr ); #ifdef DEBUG
	data = msgbuf1[0] | (msgbuf1[1] &lt;&lt; 8);

	return true;
}
int main( int argc, char* argv[] )
{
	int data=0;

	Read( 0x17, data );
	printf("data=%d\n", data );

	// 這邊其實不好，實際上寫 0x0052會比較清楚，這邊必須寫 0x5200，因為我在 Write() 裡有作對調，參看(1)
	Write( 0x00, 0x5200 );  
}


		]]>
	</description>
	<content:encoded><![CDATA[
			AD71471 的 Device address 是 0x58，Linux driver 在處理這個時，其實會自行左移一位，因此，在 I2C_SLAVE_FORCE 的 ioctl 裡，應該是要傳 0x2c。這邊因為我暈頭，把 0x58&gt;&gt;1 算成 0x4c，導致我搞了好一陣子，直到 M 同事指正以後，才弄對。<br/>
<br/>
再來，AD71471 在做讀寫時，Register address 與 data 都是 2 bytes，而 i2ctools 裡，處理 Register address 都只傳 1 byte(I2C_SMBUS)，因此不適用在 AD71471 上。我把 Linux kernel i2c-core.c 裡的 code 翻出來改寫，改用 I2C_RDWR 來處理：<pre name="code" class="c">
bool Write( uint16_t address, uint16_t value ) {
	int res=0;
	struct i2c_rdwr_ioctl_data msg_rdwr;
	char msgbuf0[I2C_SMBUS_BLOCK_MAX+4];
	char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
	struct i2c_msg msg[1] = { { _address, 0, 4, msgbuf0 } };
	uint8_t* pAddr = (uint8_t*)&address;
	uint8_t* pValue = (uint8_t*)&value;

	msg_rdwr.msgs = &msg[0];
	msg_rdwr.nmsgs = 1; // write // read = 2

	// 因為 little endian，所以要作調整
	msgbuf0[0] = *(pAddr+1);
	msgbuf0[1] = *(pAddr+0);
	msgbuf0[2] = *(pValue+1); // (1)
	msgbuf0[3] = *(pValue+0);

	res = ioctl( _file, I2C_RDWR, &msg_rdwr );
	usleep(10000);

	return true;
}
bool Read( uint16_t address, uint16_t& data ) {
	int res=0;
	struct i2c_rdwr_ioctl_data msg_rdwr;
	char msgbuf0[I2C_SMBUS_BLOCK_MAX+2];
	char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]={0};
	struct i2c_msg msg[2] = { { _address, 0, 2, msgbuf0 },
	                          { _address, I2C_M_RD, 2, msgbuf1 }
	                        };
	uint8_t* pAddr = (uint8_t*)&address;

	msg_rdwr.msgs = &msg[0];
	msg_rdwr.nmsgs = 1; // read = 2
	// 因為 little endian，所以要作調整
	msgbuf0[0] = *(pAddr+1);
	msgbuf0[1] = *(pAddr+0);
	res = ioctl( _file, I2C_RDWR, &msg_rdwr );
	usleep(10000);

	msg_rdwr.msgs = &msg[1];
	msg_rdwr.nmsgs = 1; // read = 2
	res = ioctl( _file, I2C_RDWR, &msg_rdwr ); #ifdef DEBUG
	data = msgbuf1[0] | (msgbuf1[1] &lt;&lt; 8);

	return true;
}
int main( int argc, char* argv[] )
{
	int data=0;

	Read( 0x17, data );
	printf("data=%d\n", data );

	// 這邊其實不好，實際上寫 0x0052會比較清楚，這邊必須寫 0x5200，因為我在 Write() 裡有作對調，參看(1)
	Write( 0x00, 0x5200 );  
}
</pre>

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/8891479.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/8891479.html</guid>
	<category>C/C++</category>
	<pubDate>Fri, 08 May 2009 11:57:59 +0800</pubDate>
</item>
<item>
	<title>GtkDrawingArea 與 gtk_widget_set_events()</title>
	<description><![CDATA[
			GtkDrawingArea 預設是不收 button_press_event 跟 key_press_event 的，所以要使用 gtk_widget_set_events() 告訴 GtkDrawingArea 要接收才行。

這個，我是去 Google Code Search 找來的，雖然 devhelp 可以查指令，但沒有範例，還是很難猜到怎麼用。


#include &lt;gdk/gdkkeysyms.h&gt; // 定義按鍵值的 header

static gboolean press_event( GtkWidget* widget, GdkEventButton* event, gpointer data )
{
	if( debug )
		g_print("press_event: x=%f y=%f button=%d\n", event-&gt;x, event-&gt;y, event-&gt;button );

	if( event-&gt;button==1 )  { // left
	}
}

static gboolean key_event( GtkWidget* widget, GdkEventKey* event )
{
	if( debug )
		g_print( "event-&gt;keyval=%d event-&gt;state=%d\n", event-&gt;keyval, event-&gt;state );
	switch (event-&gt;keyval) {
		// 省略...
	}
	return TRUE;
}

int main( int argc, char* argv[] )
{
// 省略一萬行...
	g_signal_connect( drawing_area, "button_press_event", G_CALLBACK( press_event ), NULL );
	g_signal_connect( drawing_area, "key_press_event", G_CALLBACK( key_event ), NULL );

	// 要接收 button_press_event 跟 key_press_event 喔~
	gtk_widget_set_events( drawing_area, gtk_widget_get_events(drawing_area) | GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK );
// 省略兩萬行...
}


		]]>
	</description>
	<content:encoded><![CDATA[
			GtkDrawingArea 預設是不收 button_press_event 跟 key_press_event 的，所以要使用 gtk_widget_set_events() 告訴 GtkDrawingArea 要接收才行。<br/>
<br/>
這個，我是去 Google Code Search 找來的，雖然 devhelp 可以查指令，但沒有範例，還是很難猜到怎麼用。<br/>

<pre name="code" class="c">
#include &lt;gdk/gdkkeysyms.h&gt; // 定義按鍵值的 header

static gboolean press_event( GtkWidget* widget, GdkEventButton* event, gpointer data )
{
	if( debug )
		g_print("press_event: x=%f y=%f button=%d\n", event-&gt;x, event-&gt;y, event-&gt;button );

	if( event-&gt;button==1 )  { // left
	}
}

static gboolean key_event( GtkWidget* widget, GdkEventKey* event )
{
	if( debug )
		g_print( "event-&gt;keyval=%d event-&gt;state=%d\n", event-&gt;keyval, event-&gt;state );
	switch (event-&gt;keyval) {
		// 省略...
	}
	return TRUE;
}

int main( int argc, char* argv[] )
{
// 省略一萬行...
	g_signal_connect( drawing_area, "button_press_event", G_CALLBACK( press_event ), NULL );
	g_signal_connect( drawing_area, "key_press_event", G_CALLBACK( key_event ), NULL );

	// 要接收 button_press_event 跟 key_press_event 喔~
	gtk_widget_set_events( drawing_area, gtk_widget_get_events(drawing_area) | GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK );
// 省略兩萬行...
}
</pre>

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/8888975.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/8888975.html</guid>
	<category>C/C++</category>
	<pubDate>Thu, 07 May 2009 20:13:01 +0800</pubDate>
</item>
<item>
	<title>gtk 載入並顯示圖片</title>
	<description><![CDATA[
			實際上是用 GDK+GtkDrawingArea 來畫，所以在下面的程式片斷，你會看到我宣告了 GtkDrawingArea 並且實作了 GtkDrawingArea 的 expose 事件。
GDK 支援的圖片格式很多，常見的 jpg、png、bmp 都沒問題。


static gboolean expose_event( GtkWidget* widget, GdkEventExpose* event, gpointer data )
{
	GError* error=NULL;
	int width=widget-&gt;allocation.width, height=widget-&gt;allocation.height;

	GdkPixbuf* buf=gdk_pixbuf_new_from_file_at_scale( "your_photo.jpg", &error );
	if( buf==NULL )
		g_print("load fail.\n" );
	else
	{
		bufWidth = gdk_pixbuf_get_width( buf );
		bufHeight = gdk_pixbuf_get_height( buf );
		gdk_draw_pixbuf( widget-&gt;window, NULL, buf, 0, 0, 0, 0, 
				(width&gt;bufWidth?bufWidth:width), (height&gt;bufHeight?bufHeight:height), 
				GDK_RGB_DITHER_NORMAL, 0, 0 );
		g_object_unref( buf );
	}
}

int main( int argc, char* argv[])
{
	GtkWidget* drawing_area=NULL;

	// 省略一萬行
	g_signal_connect( G_OBJECT(drawing_area), "expose_event", G_CALLBACK( expose_event ), NULL );

	// 再省略兩萬行...	
}

		]]>
	</description>
	<content:encoded><![CDATA[
			實際上是用 GDK+GtkDrawingArea 來畫，所以在下面的程式片斷，你會看到我宣告了 GtkDrawingArea 並且實作了 GtkDrawingArea 的 expose 事件。
GDK 支援的圖片格式很多，常見的 jpg、png、bmp 都沒問題。

<pre name="code" class="c">
static gboolean expose_event( GtkWidget* widget, GdkEventExpose* event, gpointer data )
{
	GError* error=NULL;
	int width=widget-&gt;allocation.width, height=widget-&gt;allocation.height;

	GdkPixbuf* buf=gdk_pixbuf_new_from_file_at_scale( "your_photo.jpg", &error );
	if( buf==NULL )
		g_print("load fail.\n" );
	else
	{
		bufWidth = gdk_pixbuf_get_width( buf );
		bufHeight = gdk_pixbuf_get_height( buf );
		gdk_draw_pixbuf( widget-&gt;window, NULL, buf, 0, 0, 0, 0, 
				(width&gt;bufWidth?bufWidth:width), (height&gt;bufHeight?bufHeight:height), 
				GDK_RGB_DITHER_NORMAL, 0, 0 );
		g_object_unref( buf );
	}
}

int main( int argc, char* argv[])
{
	GtkWidget* drawing_area=NULL;

	// 省略一萬行
	g_signal_connect( G_OBJECT(drawing_area), "expose_event", G_CALLBACK( expose_event ), NULL );

	// 再省略兩萬行...	
}
</pre>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/8881399.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/8881399.html</guid>
	<category>C/C++</category>
	<pubDate>Wed, 06 May 2009 19:14:01 +0800</pubDate>
</item>
<item>
	<title>placement new()</title>
	<description><![CDATA[
			昨天有同事問到可不可以讓物件 new 在 share memory 裡面，我跟他說 c++ 的 new 可以像下面例子這樣用，但是他後來沒試。好吧，反正我以前也沒試過，就寫了個小程式試一下：
/**
 *       Filename:  test_new.cpp
 *    Description:  Test new(storage) Person();
 */
#include &lt;cstdlib&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;

class Person {
	public:
		Person() {}
		~Person() {}

		std::string getName() { return _name; };
		void setName( std::string name ) { _name=name; }

		std::string toString() { return _name; }
	private:
		std::string _name;
};

int main( int argc, char* argv[] ) {
	char* storage = (char*)malloc( 1024 ); // allocate 1K

	Person* person = new(storage) Person();
	person-&gt;setName( "anonymous" );
	std::cout &lt;&lt; person-&gt;toString() &lt;&lt; std::endl;

	// if just storage, person will be replaced by intArray. (2)
	// int* intArray = new(storage) int[10];
	int* intArray = new(storage+sizeof(person)) int[10];
	int i=0;

	// assign value.
	for( i=0; i&lt;10; i++ )
		intArray[i] = i;

	// show the values
	for( i=0; i&lt;10; i++ )
		printf( "%d ", intArray[i]);
	printf("\n");

	// dump storage
	char* iter=storage;
	printf("=== begin dump ===\n");
	i=0;
	while( i!=512 ) {
		printf( "%02x ", (unsigned char)*iter );
		iter++;
		i++;
		if( !(i%16) )
			printf( "\n" );
	}
	printf("=== end dump ===\n");

	printf( "person address = %08x intArray address = %08x\n",
			(unsigned int)person, (unsigned int)intArray );

	// cannot delete, it cause segmentation fault. (1)
	//delete person;
	//delete[] intArray;

	// but we can use free.
	free( storage );
}

結論：new(storage) Person() 實際上是 new 在 storage 這塊空間裡面，所以之後如果呼叫 delete，會出錯。如果不累加 storage 的話，會把之前配置的空間覆蓋掉。malloc() 可以用 shmget()、shmat() 代替，沒有問題。以上面的例子，std::string 會配置一塊空間來放字串，這塊空間並不在 storage 裡面，使用時要注意。如果要這樣用，應該要再取代掉 STL allocator 的機制。

		]]>
	</description>
	<content:encoded><![CDATA[
			昨天有同事問到可不可以讓物件 new 在 share memory 裡面，我跟他說 c++ 的 new 可以像下面例子這樣用，但是他後來沒試。好吧，反正我以前也沒試過，就寫了個小程式試一下：
<pre name="code" class="cpp">/**
 *       Filename:  test_new.cpp
 *    Description:  Test new(storage) Person();
 */
#include &lt;cstdlib&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;

class Person {
	public:
		Person() {}
		~Person() {}

		std::string getName() { return _name; };
		void setName( std::string name ) { _name=name; }

		std::string toString() { return _name; }
	private:
		std::string _name;
};

int main( int argc, char* argv[] ) {
	char* storage = (char*)malloc( 1024 ); // allocate 1K

	Person* person = new(storage) Person();
	person-&gt;setName( "anonymous" );
	std::cout &lt;&lt; person-&gt;toString() &lt;&lt; std::endl;

	// if just storage, person will be replaced by intArray. (2)
	// int* intArray = new(storage) int[10];
	int* intArray = new(storage+sizeof(person)) int[10];
	int i=0;

	// assign value.
	for( i=0; i&lt;10; i++ )
		intArray[i] = i;

	// show the values
	for( i=0; i&lt;10; i++ )
		printf( "%d ", intArray[i]);
	printf("\n");

	// dump storage
	char* iter=storage;
	printf("=== begin dump ===\n");
	i=0;
	while( i!=512 ) {
		printf( "%02x ", (unsigned char)*iter );
		iter++;
		i++;
		if( !(i%16) )
			printf( "\n" );
	}
	printf("=== end dump ===\n");

	printf( "person address = %08x intArray address = %08x\n",
			(unsigned int)person, (unsigned int)intArray );

	// cannot delete, it cause segmentation fault. (1)
	//delete person;
	//delete[] intArray;

	// but we can use free.
	free( storage );
}</pre>

結論：<ol><li>new(storage) Person() 實際上是 new 在 storage 這塊空間裡面，所以之後如果呼叫 delete，會出錯。</li><li>如果不累加 storage 的話，會把之前配置的空間覆蓋掉。</li><li>malloc() 可以用 shmget()、shmat() 代替，沒有問題。</li><li>以上面的例子，std::string 會配置一塊空間來放字串，這塊空間並不在 storage 裡面，使用時要注意。如果要這樣用，應該要再取代掉 STL allocator 的機制。</li></ol>

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/8464923.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/8464923.html</guid>
	<category>C/C++</category>
	<pubDate>Tue, 10 Mar 2009 16:05:32 +0800</pubDate>
</item>
<item>
	<title>CMake + CTest</title>
	<description><![CDATA[
			CMake 內建 CTest，基本使用可以參考：CMake Testing With CTest

大致把要點整理如下：在專案根目錄的 CMakeLists.txt 加上 ENABLE_TESTING()在你 test 程式所在目錄的 CMakeLists.txt 加上 add_test( 測試名稱 執行檔名字 [參數1] [參數2] ... )測試程式在錯誤發生時呼叫 exit() 並傳入非 0 值，正常結束的話，則呼叫 exit(0)。大功告成以後，先刪除 CMakeCache.txt，然後用 cmake 重新產生 Makefile，接下來就可以用 make && make test 來進行測試了。make test 是進行所有測試，只想進行某幾項測試的話，可以查看 ctest 的 -R, -E, -I 這幾個選項的說明。-R 是用 regular expression 找特定名稱的測試項目，-E 則是相反，排除掉特定名稱的測試項目，-I 是指定項目號碼，表示進行指定項目的測試。

文件裡面還有提到可以把測試結果自動上傳到網站上等等，不過看來是用不到，就沒嘗試了。

		]]>
	</description>
	<content:encoded><![CDATA[
			CMake 內建 CTest，基本使用可以參考：<a href="http://www.cmake.org/Wiki/CMake_Testing_With_CTest">CMake Testing With CTest</a><br />
<br />
大致把要點整理如下：<ol><li>在專案根目錄的 CMakeLists.txt 加上 ENABLE_TESTING()</li><li>在你 test 程式所在目錄的 CMakeLists.txt 加上 add_test( 測試名稱 執行檔名字 [參數1] [參數2] ... )</li><li>測試程式在錯誤發生時呼叫 exit() 並傳入非 0 值，正常結束的話，則呼叫 exit(0)。</li><li>大功告成以後，先刪除 CMakeCache.txt，然後用 cmake 重新產生 Makefile，接下來就可以用 make && make test 來進行測試了。</li><li>make test 是進行所有測試，只想進行某幾項測試的話，可以查看 ctest 的 -R, -E, -I 這幾個選項的說明。-R 是用 regular expression 找特定名稱的測試項目，-E 則是相反，排除掉特定名稱的測試項目，-I 是指定項目號碼，表示進行指定項目的測試。</li></ol><br />
<br />
文件裡面還有提到可以把測試結果自動上傳到網站上等等，不過看來是用不到，就沒嘗試了。<br />

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/7433707.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/7433707.html</guid>
	<category>C/C++</category>
	<pubDate>Fri, 24 Oct 2008 10:13:12 +0800</pubDate>
</item>
<item>
	<title>以 Visual Studio 2005 編譯 boost::regex</title>
	<description><![CDATA[
			下載 boost 跟 bjam 以後，第一件事情是編譯。
打開 visual studio 2005 命令提示字元切換到你的 boost目錄 下，這裡假設為 c:\boost_1_36_0執行 bjam --build-dir="c:\boost_1_36_0\build" --toolset=msvc-8.0 --build-type=complete --with-regex stage，就可以只編譯 boost::regex.編譯會需要一陣子，編譯好的檔案就會放在 c:\boost_1_36_0\build\boost\bin.v2\libs\regex\ 下

在使用時，在專案屬性裡指定 [組態屬性][C/C++][一般] 的 "其他Include目錄" 為 c:\boost_1_36_0，再指定 [組態屬性][連結器][一般] 的 "其他程式庫目錄" 為 "c:\boost_1_36_0\build\boost\bin.v2\libs\regex\build\msvc-8.0\debug\link-static\threading-multi"，進行編譯即可。

		]]>
	</description>
	<content:encoded><![CDATA[
			下載 boost 跟 bjam 以後，第一件事情是編譯。<br />
<ol><li>打開 visual studio 2005 命令提示字元</li><li>切換到你的 boost目錄 下，這裡假設為 c:\boost_1_36_0</li><li>執行 bjam --build-dir="c:\boost_1_36_0\build" --toolset=msvc-8.0 --build-type=complete --with-regex stage，就可以只編譯 boost::regex.</li><li>編譯會需要一陣子，編譯好的檔案就會放在 c:\boost_1_36_0\build\boost\bin.v2\libs\regex\ 下</li></ol><br />
<br />
在使用時，在專案屬性裡指定 [組態屬性][C/C++][一般] 的 "其他Include目錄" 為 c:\boost_1_36_0，再指定 [組態屬性][連結器][一般] 的 "其他程式庫目錄" 為 "c:\boost_1_36_0\build\boost\bin.v2\libs\regex\build\msvc-8.0\debug\link-static\threading-multi"，進行編譯即可。<br />

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/7111831.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/7111831.html</guid>
	<category>C/C++</category>
	<pubDate>Thu, 04 Sep 2008 15:21:47 +0800</pubDate>
</item>
<item>
	<title>new之後，constructor之前</title>
	<description><![CDATA[
			昨天在代碼裡面看到一個從沒看過的用法，如下列紅色標示部份：

#include &lt;iostream&gt; 

class MyBase {
&nbsp;&nbsp;protected:
&nbsp;&nbsp;&nbsp;&nbsp;int _id;

&nbsp;&nbsp;public:
&nbsp;&nbsp;&nbsp;&nbsp;MyBase():_id(0) {}
&nbsp;&nbsp;&nbsp;&nbsp;MyBase( int id ):_id(id) {}
&nbsp;&nbsp;&nbsp;&nbsp;int getId() {return _id; }
};

class MyClass: public MyBase {
&nbsp;&nbsp;public:
&nbsp;&nbsp;&nbsp;&nbsp;MyClass() {}
&nbsp;&nbsp;&nbsp;&nbsp;MyClass( int id ):MyBase( id ) {}
};

int myclass_mem[ sizeof(MyClass)/sizeof(int) ];

using namespace std;

MyBase* test()
{
&nbsp;&nbsp;return new (myclass_mem)MyClass( 100 );// 這裡 (1)
&nbsp;&nbsp;//return new MyClass( 100 ); // (2) 
}

int
main( int argc, char* argv[] )
{
&nbsp;&nbsp;int len = sizeof( myclass_mem );
&nbsp;&nbsp;cout &lt;&lt; &quot;The size of myclass_mem is &quot; &lt;&lt; len &lt;&lt; endl;
&nbsp;&nbsp;cout &lt;&lt; &quot;content of myclass_mem&quot; &lt;&lt; endl;
&nbsp;&nbsp;for( int i=0; i&lt;len; i++ )
&nbsp;&nbsp;&nbsp;&nbsp;cout &lt;&lt; myclass_mem[i] &lt;&lt; " ";
&nbsp;&nbsp;cout &lt;&lt; endl;

&nbsp;&nbsp;MyBase* base = ::test();
&nbsp;&nbsp;cout &lt;&lt; base-&gt;getId() &lt;&lt; endl;

&nbsp;&nbsp;cout &lt;&lt; &quot;content of myclass_mem&quot; &lt;&lt; endl;
&nbsp;&nbsp;for( int i=0; i&lt;len; i++ )
&nbsp;&nbsp;&nbsp;&nbsp;cout &lt;&lt; myclass_mem[i] &lt;&lt; " ";
&nbsp;&nbsp;cout &lt;&lt; endl;
}


以執行的結果來說，我實在是分辨不出來 (1) 與 (2) 有甚麼分別。
後來我用 gcc -S 去分別產生組合語言碼，總算是大致猜到了，原來以 (1) 的方法來寫，會把 new 以後的結果也複製到 myclass_mem 這個陣列裡面。
所以加上輸出 myclass_mem 內容的程式碼，再分別產生執行檔來看執行結果就很清楚了。
真是特別。

		]]>
	</description>
	<content:encoded><![CDATA[
			昨天在代碼裡面看到一個從沒看過的用法，如下列紅色標示部份：<br/>
<div>
#include &lt;iostream&gt; <br/>
<br/>
class MyBase {<br/>
&nbsp;&nbsp;protected:<br/>
&nbsp;&nbsp;&nbsp;&nbsp;int _id;<br/>
<br/>
&nbsp;&nbsp;public:<br/>
&nbsp;&nbsp;&nbsp;&nbsp;MyBase():_id(0) {}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;MyBase( int id ):_id(id) {}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;int getId() {return _id; }<br/>
};<br/>
<br/>
class MyClass: public MyBase {<br/>
&nbsp;&nbsp;public:<br/>
&nbsp;&nbsp;&nbsp;&nbsp;MyClass() {}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;MyClass( int id ):MyBase( id ) {}<br/>
};<br/>
<br/>
int myclass_mem[ sizeof(MyClass)/sizeof(int) ];<br/>
<br/>
using namespace std;<br/>
<br/>
MyBase* test()<br/>
{<br/>
&nbsp;&nbsp;<span style="color: rgb(255, 0, 0);">return new (myclass_mem)MyClass( 100 );</span>// 這裡 (1)<br/>
&nbsp;&nbsp;//return new MyClass( 100 ); // (2) <br/>
}<br/>
<br/>
int<br/>
main( int argc, char* argv[] )<br/>
{<br/>
&nbsp;&nbsp;int len = sizeof( myclass_mem );<br/>
&nbsp;&nbsp;cout &lt;&lt; &quot;The size of myclass_mem is &quot; &lt;&lt; len &lt;&lt; endl;<br/>
&nbsp;&nbsp;cout &lt;&lt; &quot;content of myclass_mem&quot; &lt;&lt; endl;<br/>
&nbsp;&nbsp;for( int i=0; i&lt;len; i++ )<br/>
&nbsp;&nbsp;&nbsp;&nbsp;cout &lt;&lt; myclass_mem[i] &lt;&lt; " ";<br/>
&nbsp;&nbsp;cout &lt;&lt; endl;<br/>
<br/>
&nbsp;&nbsp;MyBase* base = ::test();<br/>
&nbsp;&nbsp;cout &lt;&lt; base-&gt;getId() &lt;&lt; endl;<br/>
<br/>
&nbsp;&nbsp;cout &lt;&lt; &quot;content of myclass_mem&quot; &lt;&lt; endl;<br/>
&nbsp;&nbsp;for( int i=0; i&lt;len; i++ )<br/>
&nbsp;&nbsp;&nbsp;&nbsp;cout &lt;&lt; myclass_mem[i] &lt;&lt; " ";<br/>
&nbsp;&nbsp;cout &lt;&lt; endl;<br/>
}
</div>
<br/>
以執行的結果來說，我實在是分辨不出來 (1) 與 (2) 有甚麼分別。<br/>
後來我用 gcc -S 去分別產生組合語言碼，總算是大致猜到了，原來以 (1) 的方法來寫，會把 new 以後的結果也複製到 myclass_mem 這個陣列裡面。
所以加上輸出 myclass_mem 內容的程式碼，再分別產生執行檔來看執行結果就很清楚了。
真是特別。

		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/1563208.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/1563208.html</guid>
	<category>C/C++</category>
	<pubDate>Tue, 09 May 2006 12:29:04 +0800</pubDate>
</item>
<item>
	<title>Duff’s Device</title>
	<description><![CDATA[
			在國二菜鳥忙裡偷閒看到這篇Duff’s Device.

這段 code, 真的很神奇
    register n = (count + 7) / 8; /* count > 0 assumed */

    switch (count % 8)
    {
    case 0: do { *to = *from++;
    case 7: *to = *from++;
    case 6: *to = *from++;
    case 5: *to = *from++;
    case 4: *to = *from++;
    case 3: *to = *from++;
    case 2: *to = *from++;
    case 1: *to = *from++;
    } while (–n > 0);
    }


不過我必須承認,在看了它提供的參考網址以後,我不知道這段 code 在幹麼....

參考網址:

  Duff's device
  http://www.lysator.liu.se/c/duffs-device.html

		]]>
	</description>
	<content:encoded><![CDATA[
			在<a href="http://blog.ijliao.info" title="國二菜鳥忙裡偷閒">國二菜鳥忙裡偷閒</a>看到<a href="http://blog.ijliao.info/archives/2005/07/14/1590/" title="Duff’s Device">這篇Duff’s Device</a>.<br />
<br />
這段 code, 真的很神奇<br />
<blockquote>    register n = (count + 7) / 8; /* count > 0 assumed */<br />
<br />
    switch (count % 8)<br />
    {<br />
    case 0: do { *to = *from++;<br />
    case 7: *to = *from++;<br />
    case 6: *to = *from++;<br />
    case 5: *to = *from++;<br />
    case 4: *to = *from++;<br />
    case 3: *to = *from++;<br />
    case 2: *to = *from++;<br />
    case 1: *to = *from++;<br />
    } while (–n > 0);<br />
    }<br />
</blockquote><br />
<br />
不過我必須承認,在看了它提供的參考網址以後,我不知道這段 code 在幹麼....<br />
<br />
參考網址:<br />
<ul><br />
  <li><a href="http://catb.org/~esr/jargon/html/D/Duffs-device.html" title="Duff's device">Duff's device</a></li><br />
  <li><a href="http://www.lysator.liu.se/c/duffs-device.html" title="http://www.lysator.liu.se/c/duffs-device.html">http://www.lysator.liu.se/c/duffs-device.html</a></li><br />
</ul>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554272.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554272.html</guid>
	<category>C/C++</category>
	<pubDate>Fri, 15 Jul 2005 09:43:46 +0800</pubDate>
</item>
<item>
	<title>longjmp/setjmp</title>
	<description><![CDATA[
			這是一組有趣的 api, 一般性的用法,是先使用 setjmp 設定 break point, 之後要跳回來的時候,就使用 longjmp.

所以,用法大概是這樣...
#include &lt;stdio.h&gt;
#include &lt;setjmp.h&gt;

jmp_buf setjmp_buffer;

int
main( int argc, char* argv[] )
{
    if( setjmp( setjmp_buffer ) == 0 )
    {
        // do something
        printf("longjmp!!\n");
        longjmp( setjmp_buffer, 1 );  // 用 0 的話, setjmp() 就會收到 0, 那麼可能會無窮回圈
    }
    else
    {
        // do something
        printf("after longjmp.\n");
    }
}


很簡單吧,應該也可以兜出 try ... catch ... finally 的用法.
下面是我大致的想法
    int exception_id;
    if( ( exception_id = setjmp( setjmp_buffer ) ) == 0 )  // try
    {
        // do something
        longjmp( setjmp_buffer, 1 );  // throw
    }
    else
    {
        if( exception_id == 1 )    // catch( 1 )
        {
        }
        if( exception_id == 2 )    // catch( 2 )
        {
        }
        // ....
    }
    // 之後,就是 finally...


之後再參考書看看吧... ^^
		]]>
	</description>
	<content:encoded><![CDATA[
			這是一組有趣的 api, 一般性的用法,是先使用 setjmp 設定 break point, 之後要跳回來的時候,就使用 longjmp.<br />
<br />
所以,用法大概是這樣...<br />
<blockquote>#include &lt;stdio.h&gt;<br />
#include &lt;setjmp.h&gt;<br />
<br />
jmp_buf setjmp_buffer;<br />
<br />
int<br />
main( int argc, char* argv[] )<br />
{<br />
    if( setjmp( setjmp_buffer ) == 0 )<br />
    {<br />
        // do something<br />
        printf("longjmp!!\n");<br />
        longjmp( setjmp_buffer, 1 );  // 用 0 的話, setjmp() 就會收到 0, 那麼可能會無窮回圈<br />
    }<br />
    else<br />
    {<br />
        // do something<br />
        printf("after longjmp.\n");<br />
    }<br />
}<br />
</blockquote><br />
<br />
很簡單吧,應該也可以兜出 try ... catch ... finally 的用法.<br />
下面是我大致的想法<br />
<blockquote>    int exception_id;<br />
    if( ( exception_id = setjmp( setjmp_buffer ) ) == 0 )  // try<br />
    {<br />
        // do something<br />
        longjmp( setjmp_buffer, 1 );  // throw<br />
    }<br />
    else<br />
    {<br />
        if( exception_id == 1 )    // catch( 1 )<br />
        {<br />
        }<br />
        if( exception_id == 2 )    // catch( 2 )<br />
        {<br />
        }<br />
        // ....<br />
    }<br />
    // 之後,就是 finally...<br />
</blockquote><br />
<br />
之後再參考書看看吧... ^^
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554267.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554267.html</guid>
	<category>C/C++</category>
	<pubDate>Sat, 02 Jul 2005 09:54:34 +0800</pubDate>
</item>
<item>
	<title>用 wprintf 輸出 string</title>
	<description><![CDATA[
			wprintf() 要印一個 wchar 字串時，得特別使用 %ls，如果你用 %s, 那表示要印一個一般的 char 字串。

所以這樣會只印出 m
wchar_t* myString=L"music is wonderful!!";
wprintf( L"%s\n", myString );

而這樣才會正確的印出 "music is wonderful!"
wchar_t* myString=L"music is wonderful!!";
wprintf( L"%ls\n", myString );

		]]>
	</description>
	<content:encoded><![CDATA[
			wprintf() 要印一個 wchar 字串時，得特別使用 %ls，如果你用 %s, 那表示要印一個一般的 char 字串。<br />
<br />
所以這樣會只印出 m<br />
<blockquote>wchar_t* myString=L"music is wonderful!!";<br />
wprintf( L"%s\n", myString );<br />
</blockquote><br />
而這樣才會正確的印出 "music is wonderful!"<br />
<blockquote>wchar_t* myString=L"music is wonderful!!";<br />
wprintf( L"%ls\n", myString );<br />
</blockquote>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554229.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554229.html</guid>
	<category>C/C++</category>
	<pubDate>Thu, 10 Mar 2005 16:57:31 +0800</pubDate>
</item>
<item>
	<title>printf</title>
	<description><![CDATA[
			神奇的用法....老實說....我以前的確不知道可以這樣用~~

三人行必有我師

#include &lt;stdio.h&gt;

int main()
{
	int i;
	i=23;
	printf("%*s\n", i, "blah");
	printf("%*s\n", i+1, "blah");
}

這樣就可以自由決定格式了....
我以前大概會先用字串去串出前面的格式字串吧~~
		]]>
	</description>
	<content:encoded><![CDATA[
			神奇的用法....老實說....我以前的確不知道可以這樣用~~<br />
<br />
<a href="http://apple.sysbio.info/~mjhsieh/archives/000425.html" title="三人行必有我師">三人行必有我師</a><br />
<br />
#include &lt;stdio.h&gt;<br />
<br />
int main()<br />
{<br />
	int i;<br />
	i=23;<br />
	printf("%*s\n", i, "blah");<br />
	printf("%*s\n", i+1, "blah");<br />
}<br />
<br />
這樣就可以自由決定格式了....<br />
我以前大概會先用字串去串出前面的格式字串吧~~
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554197.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554197.html</guid>
	<category>C/C++</category>
	<pubDate>Tue, 04 Jan 2005 19:17:07 +0800</pubDate>
</item>
<item>
	<title>ltdl library</title>
	<description><![CDATA[
			可以輸入 info libtool 找 Module loaders for libltdl 就可以得到不少資訊.
簡單說,就是可以製作能動態載入 module/plugin 的 library, 以 linux 來說,底層其實就是 dlopen, dlsym 這些函數
這個 library 其實是一個 wrapper, 跨了好幾個平台: BSD, Linux, Win32...

configure.in 中的設定
AC_LIBLTDL_CONVENIENCE
AC_SUBST(LTLINCL)
AC_SUBST(LIBLTDL)
AC_LIBTOOL_DLOPEN
AC_PROG_LIBTOOL
AC_CONFIG_SUBDIRS(libltdl)

Makefile.am 中的設定
SUBDIRS=libltdl
INCLUDES=$(LTDLINCL)
prog_LDFLAGS=-export-dynamic
prog_LDADD=$(LIBLTDL) "_dlopen" self "_dlopen" fxx.la
prog_DEPENDENCIES=$(LIBLTDL) fool.la

prog 指的是執行檔名稱

而 module/plugin 的 Makefile.am 設定
xxx_la_LDFLAGS=-module

函數蠻多的,可以直接從 ltdl.h 去找或參考 info
對於 Multi-Thread 也有支援.
		]]>
	</description>
	<content:encoded><![CDATA[
			可以輸入 info libtool 找 Module loaders for libltdl 就可以得到不少資訊.<br />
簡單說,就是可以製作能動態載入 module/plugin 的 library, 以 linux 來說,底層其實就是 dlopen, dlsym 這些函數<br />
這個 library 其實是一個 wrapper, 跨了好幾個平台: BSD, Linux, Win32...<br />
<br />
configure.in 中的設定<br />
AC_LIBLTDL_CONVENIENCE<br />
AC_SUBST(LTLINCL)<br />
AC_SUBST(LIBLTDL)<br />
AC_LIBTOOL_DLOPEN<br />
AC_PROG_LIBTOOL<br />
AC_CONFIG_SUBDIRS(libltdl)<br />
<br />
Makefile.am 中的設定<br />
SUBDIRS=libltdl<br />
INCLUDES=$(LTDLINCL)<br />
prog_LDFLAGS=-export-dynamic<br />
prog_LDADD=$(LIBLTDL) "_dlopen" self "_dlopen" fxx.la<br />
prog_DEPENDENCIES=$(LIBLTDL) fool.la<br />
<br />
prog 指的是執行檔名稱<br />
<br />
而 module/plugin 的 Makefile.am 設定<br />
xxx_la_LDFLAGS=-module<br />
<br />
函數蠻多的,可以直接從 ltdl.h 去找或參考 info<br />
對於 Multi-Thread 也有支援.
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554029.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554029.html</guid>
	<category>C/C++</category>
	<pubDate>Fri, 14 May 2004 10:35:43 +0800</pubDate>
</item>
<item>
	<title>Linux Timezone programming HOW-TO</title>
	<description><![CDATA[
			以前在作 NAS (Network Attached Server) 的時候,因為要取得並設定時區,所以去找到的資料.
主要是參考這篇 The GNU C Library - Time Zone Functions.還有一些其他 Linux 程式的方式來解決.
不過 GNU C 的這個 timezone functions 似乎不太 work,我想可能是我不會用吧~~後來是改用 symbolic link 的方式來解決~~

以下用問答的方式來記述:
1.如何取得目前 timezone?

在設計上,可以將 /etc/localtime symbolic link 到 /usr/share/zoneinfo 下的 zone file. 藉著取得真實路徑的函數readlink(),就可以得知是哪裡的 timezone.

2.如何設定 timezone?

同 1, 可以用 symbolic link 的方式來設定

3.和 TZ Environment Variable 的關係

如果有設定 TZ 環境變數,那麼 /etc/localtime 將會失效.
TZ 有特定格式,需要查一下,大多的 embedded system 都是直接利用 TZ 環境變數,而不使用 zone file.

4.如何瀏覽 zone??

可以直接開啟 /usr/share/zoneinfo/zone.tab 來取得列表.
此檔共有四欄: code, coordinates, TZ, comments.
照慣例,以 # 開始的該列為註解.
比較需要用到的兩個欄位: TZ 與 comments.

5.預設值??

預設值可以直接指向 /usr/share/zoneinfo/GreenWich 表示為格林威治標準時間

		]]>
	</description>
	<content:encoded><![CDATA[
			以前在作 NAS (Network Attached Server) 的時候,因為要取得並設定時區,所以去找到的資料.<br />
主要是參考<a href="http://www.gnu.org/software/libc/manual/html_node/Time-Zone-Functions.html">這篇 The GNU C Library - Time Zone Functions</a>.還有一些其他 Linux 程式的方式來解決.<br />
不過 GNU C 的這個 timezone functions 似乎不太 work,我想可能是我不會用吧~~後來是改用 symbolic link 的方式來解決~~<br />
<br />
以下用問答的方式來記述:<br />
1.如何取得目前 timezone?<br />
<blockquote><br />
在設計上,可以將 /etc/localtime symbolic link 到 /usr/share/zoneinfo 下的 zone file. 藉著取得真實路徑的函數readlink(),就可以得知是哪裡的 timezone.<br />
</blockquote><br />
2.如何設定 timezone?<br />
<blockquote><br />
同 1, 可以用 symbolic link 的方式來設定<br />
</blockquote><br />
3.和 TZ Environment Variable 的關係<br />
<blockquote><br />
如果有設定 TZ 環境變數,那麼 /etc/localtime 將會失效.<br />
TZ 有特定格式,需要查一下,大多的 embedded system 都是直接利用 TZ 環境變數,而不使用 zone file.<br />
</blockquote><br />
4.如何瀏覽 zone??<br />
<blockquote><br />
可以直接開啟 /usr/share/zoneinfo/zone.tab 來取得列表.<br />
此檔共有四欄: code, coordinates, TZ, comments.<br />
照慣例,以 # 開始的該列為註解.<br />
比較需要用到的兩個欄位: TZ 與 comments.<br />
</blockquote><br />
5.預設值??<br />
<blockquote><br />
預設值可以直接指向 /usr/share/zoneinfo/GreenWich 表示為格林威治標準時間<br />
</blockquote>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554025.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554025.html</guid>
	<category>C/C++</category>
	<pubDate>Thu, 22 Apr 2004 10:28:45 +0800</pubDate>
</item>
<item>
	<title>向侯捷請問STL sort的問題</title>
	<description><![CDATA[
			這是蠻久以前的事情了,那時候為了這個 sort 的問題,發 mail 向侯捷先生請教.雖然隔了很久才回覆我,不過心裡還是很感動~~也才知道自己的程式出了什麼錯.



回覆日期:2003年2月20日 上午 03:38

你的程式寫的邏輯不對。

> bool operator==( const myInt& l, const myInt& r) {
> bool operator>( const myInt& l, const myInt& r) {
> bool operator
> 冒昧請教,以下是程式,而問題在最後面~
>
> #include &lt;iostream&gt;
> #include &lt;algorithm&gt;
> #include &lt;vector&gt;
>
> using namespace std;
>
> class myInt {
> protected:
> &nbsp;&nbsp;int id;
> public:
> &nbsp;&nbsp;myInt():id(0) {};
> &nbsp;&nbsp;explicit myInt( int i ):id(i) {};
> &nbsp;&nbsp;myInt( const myInt& i ):id(i.id) { };
> &nbsp;&nbsp;int getId( void ) const {
> &nbsp;&nbsp;&nbsp;&nbsp;return id;
> &nbsp;&nbsp;}
> &nbsp;&nbsp;myInt& operator=( myInt& i) {
> &nbsp;&nbsp;&nbsp;&nbsp;id=i.id;
> &nbsp;&nbsp;&nbsp;&nbsp;return *this;
> &nbsp;&nbsp;}
> &nbsp;&nbsp;myInt& operator=( int i ) {
> &nbsp;&nbsp;&nbsp;&nbsp;id=i;
> &nbsp;&nbsp;&nbsp;&nbsp;return *this;
> &nbsp;&nbsp;}
> &nbsp;&nbsp;myInt& operator*() { return *this; }
> &nbsp;&nbsp;myInt* operator->() { return this; }
> &nbsp;&nbsp;friend bool operator==( const myInt& l, const myInt& r );
> &nbsp;&nbsp;friend bool operator &nbsp;&nbsp;friend bool operator>( const myInt& l, const myInt& r );
> };
>
> bool operator==( const myInt& l, const myInt& r) {
> &nbsp;&nbsp;if( l.getId() == r.getId() )
> &nbsp;&nbsp;&nbsp;&nbsp;return true;
> }
>
> bool operator>( const myInt& l, const myInt& r) {
> &nbsp;&nbsp;if( l.getId() > r.getId() )
> &nbsp;&nbsp;&nbsp;&nbsp;return true;
> }
> bool operator &nbsp;&nbsp;if( l.getId() < r.getId() )
> &nbsp;&nbsp;&nbsp;&nbsp;return true;
> }
>
> class myIntCompare {
> public:
> &nbsp;&nbsp;int operator()( myInt aa, myInt bb ) {
> &nbsp;&nbsp;&nbsp;&nbsp;if( aa.getId() > bb.getId() )
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;
> &nbsp;&nbsp;&nbsp;&nbsp;if( aa.getId() == bb.getId() )
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
> &nbsp;&nbsp;&nbsp;&nbsp;if( aa.getId() < bb.getId() )
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;
> &nbsp;&nbsp;}
> };
>
> int
> main( int argc, char* argv[] ) {
> &nbsp;&nbsp;const int COUNTS=10000;
> &nbsp;&nbsp;typedef vector< myInt > intVector_t;
> &nbsp;&nbsp;intVector_t intVector;
> &nbsp;&nbsp;int i;
>
> &nbsp;&nbsp;/**
> &nbsp;&nbsp;&nbsp;* 下面的兩行 code 會導致 segmentation fault,這就是我要問的問題!!
> &nbsp;&nbsp;&nbsp;* for( i=COUNTS;i>=0;--i )
> &nbsp;&nbsp;&nbsp;*      intVector.push_back( myInt(i) );
> &nbsp;&nbsp;&nbsp;*/
> &nbsp;&nbsp;intVector.reserve( COUNTS );
> &nbsp;&nbsp;for( i=COUNTS; i>=0; --i )
> &nbsp;&nbsp;&nbsp;&nbsp;intVector[i]=i;
>
> &nbsp;&nbsp;// 以下兩種寫法都可以
> &nbsp;&nbsp;sort( intVector.begin(), intVector.end(), myIntCompare() );
> &nbsp;&nbsp;//sort( intVector.begin(), intVector.end() );
>
> &nbsp;&nbsp;for( i=0; i &nbsp;&nbsp;&nbsp;&nbsp;cout 
> 為什麼把
> &nbsp;&nbsp;intVector.reserve( COUNTS );
> &nbsp;&nbsp;for( i=COUNTS; i>=0; --i )
> &nbsp;&nbsp;&nbsp;&nbsp;intVector[i]=i;
>
> 改成
> &nbsp;&nbsp;for( i=COUNTS;i>=0;--i )
> &nbsp;&nbsp;&nbsp;&nbsp;intVector.push_back( myInt(i) );
>
> 編譯後再執行
> 就會發生 segmentation fault 的問題呢??
> 真的是很奇怪
> 我知道 vector 在配置的時候會先預留一塊記憶體
> 在 push_back() 的時候,如果發現不夠大,會自動重新配置,
> 並把原來的內容搬到新的地方去.
> 我想可能是重新配置的問題~~
> 但卻不知道真正的原因是什麼?
> 希望侯 sir 能給我一些方向,讓我下手去找出答案~
>
> 喔,對了,我的環境是 linux g++ 2.96.
>
> With Best Regards.

		]]>
	</description>
	<content:encoded><![CDATA[
			這是蠻久以前的事情了,那時候為了這個 sort 的問題,發 mail 向侯捷先生請教.雖然隔了很久才回覆我,不過心裡還是很感動~~也才知道自己的程式出了什麼錯.<br />
<br />
<blockquote><br />
<br />
回覆日期:2003年2月20日 上午 03:38<br />
<br />
你的程式寫的邏輯不對。<br />
<br />
> bool operator==( const myInt& l, const myInt& r) {<br />
> bool operator>( const myInt& l, const myInt& r) {<br />
> bool operator<( const myInt& l, const myInt& r) {<br />
<br />
這三個都沒有完整判斷。sort 的時候，要求 class 需定義 operator<，<br />
而 operator< 在什麼時候應該傳回 0, 什麼時候傳回 正值，什麼時候傳回負值，有一定的規則。<br />
<br />
問題不是出在你指的地方，而在這裡。<br />
<br />
-- jjhou<br />
<br />
----- Original Message -----<br />
寄件者: "晏仁" <ellery@pchome.com.tw><br />
收件者: <jjhou@jjhou.com><br />
傳送日期: 2002年10月8日 PM 11:07<br />
主旨: 請教 STL sort() 的問題<br />
<br />
<br />
> Dear 侯sir:<br />
><br />
> 冒昧請教,以下是程式,而問題在最後面~<br />
><br />
> #include &lt;iostream&gt;<br />
> #include &lt;algorithm&gt;<br />
> #include &lt;vector&gt;<br />
><br />
> using namespace std;<br />
><br />
> class myInt {<br />
> protected:<br />
> &nbsp;&nbsp;int id;<br />
> public:<br />
> &nbsp;&nbsp;myInt():id(0) {};<br />
> &nbsp;&nbsp;explicit myInt( int i ):id(i) {};<br />
> &nbsp;&nbsp;myInt( const myInt& i ):id(i.id) { };<br />
> &nbsp;&nbsp;int getId( void ) const {<br />
> &nbsp;&nbsp;&nbsp;&nbsp;return id;<br />
> &nbsp;&nbsp;}<br />
> &nbsp;&nbsp;myInt& operator=( myInt& i) {<br />
> &nbsp;&nbsp;&nbsp;&nbsp;id=i.id;<br />
> &nbsp;&nbsp;&nbsp;&nbsp;return *this;<br />
> &nbsp;&nbsp;}<br />
> &nbsp;&nbsp;myInt& operator=( int i ) {<br />
> &nbsp;&nbsp;&nbsp;&nbsp;id=i;<br />
> &nbsp;&nbsp;&nbsp;&nbsp;return *this;<br />
> &nbsp;&nbsp;}<br />
> &nbsp;&nbsp;myInt& operator*() { return *this; }<br />
> &nbsp;&nbsp;myInt* operator->() { return this; }<br />
> &nbsp;&nbsp;friend bool operator==( const myInt& l, const myInt& r );<br />
> &nbsp;&nbsp;friend bool operator<( const myInt& l, const myInt& r );<br />
> &nbsp;&nbsp;friend bool operator>( const myInt& l, const myInt& r );<br />
> };<br />
><br />
> bool operator==( const myInt& l, const myInt& r) {<br />
> &nbsp;&nbsp;if( l.getId() == r.getId() )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
> }<br />
><br />
> bool operator>( const myInt& l, const myInt& r) {<br />
> &nbsp;&nbsp;if( l.getId() > r.getId() )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
> }<br />
> bool operator<( const myInt& l, const myInt& r) {<br />
> &nbsp;&nbsp;if( l.getId() < r.getId() )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
> }<br />
><br />
> class myIntCompare {<br />
> public:<br />
> &nbsp;&nbsp;int operator()( myInt aa, myInt bb ) {<br />
> &nbsp;&nbsp;&nbsp;&nbsp;if( aa.getId() > bb.getId() )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;<br />
> &nbsp;&nbsp;&nbsp;&nbsp;if( aa.getId() == bb.getId() )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br />
> &nbsp;&nbsp;&nbsp;&nbsp;if( aa.getId() < bb.getId() )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;<br />
> &nbsp;&nbsp;}<br />
> };<br />
><br />
> int<br />
> main( int argc, char* argv[] ) {<br />
> &nbsp;&nbsp;const int COUNTS=10000;<br />
> &nbsp;&nbsp;typedef vector< myInt > intVector_t;<br />
> &nbsp;&nbsp;intVector_t intVector;<br />
> &nbsp;&nbsp;int i;<br />
><br />
> &nbsp;&nbsp;/**<br />
> &nbsp;&nbsp;&nbsp;* 下面的兩行 code 會導致 segmentation fault,這就是我要問的問題!!<br />
> &nbsp;&nbsp;&nbsp;* for( i=COUNTS;i>=0;--i )<br />
> &nbsp;&nbsp;&nbsp;*      intVector.push_back( myInt(i) );<br />
> &nbsp;&nbsp;&nbsp;*/<br />
> &nbsp;&nbsp;intVector.reserve( COUNTS );<br />
> &nbsp;&nbsp;for( i=COUNTS; i>=0; --i )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;intVector[i]=i;<br />
><br />
> &nbsp;&nbsp;// 以下兩種寫法都可以<br />
> &nbsp;&nbsp;sort( intVector.begin(), intVector.end(), myIntCompare() );<br />
> &nbsp;&nbsp;//sort( intVector.begin(), intVector.end() );<br />
><br />
> &nbsp;&nbsp;for( i=0; i<=COUNTS; ++i )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;cout << intVector[i].getId() << endl;<br />
> }<br />
><br />
> 為什麼把<br />
> &nbsp;&nbsp;intVector.reserve( COUNTS );<br />
> &nbsp;&nbsp;for( i=COUNTS; i>=0; --i )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;intVector[i]=i;<br />
><br />
> 改成<br />
> &nbsp;&nbsp;for( i=COUNTS;i>=0;--i )<br />
> &nbsp;&nbsp;&nbsp;&nbsp;intVector.push_back( myInt(i) );<br />
><br />
> 編譯後再執行<br />
> 就會發生 segmentation fault 的問題呢??<br />
> 真的是很奇怪<br />
> 我知道 vector 在配置的時候會先預留一塊記憶體<br />
> 在 push_back() 的時候,如果發現不夠大,會自動重新配置,<br />
> 並把原來的內容搬到新的地方去.<br />
> 我想可能是重新配置的問題~~<br />
> 但卻不知道真正的原因是什麼?<br />
> 希望侯 sir 能給我一些方向,讓我下手去找出答案~<br />
><br />
> 喔,對了,我的環境是 linux g++ 2.96.<br />
><br />
> With Best Regards.<br />
</blockquote>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554007.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554007.html</guid>
	<category>C/C++</category>
	<pubDate>Sun, 28 Mar 2004 11:31:47 +0800</pubDate>
</item>
<item>
	<title>gcc 的超強 Warning 選項</title>
	<description><![CDATA[
			想不到
真是想不到
gcc 的 Warning 選項居然可以做到可以檢查你的 code 是不是用了 char, 還有 enumeration 內定義的所有數字是否都被用了
功能真是超強~

但是今天下午被這個玩意兒卡了快一個多小時
真是可惡~~
		]]>
	</description>
	<content:encoded><![CDATA[
			想不到<br />
真是想不到<br />
gcc 的 Warning 選項居然可以做到可以檢查你的 code 是不是用了 char, 還有 enumeration 內定義的所有數字是否都被用了<br />
功能真是超強~<br />
<br />
但是今天下午被這個玩意兒卡了快一個多小時<br />
真是可惡~~
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553919.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553919.html</guid>
	<category>C/C++</category>
	<pubDate>Tue, 18 Nov 2003 16:44:05 +0800</pubDate>
</item>
<item>
	<title>C++ ABI</title>
	<description><![CDATA[
			ABI 是 Application Binary Interface, 它是一種介於使用者 C++ 程式碼和提供實作系統和 library 的物件程式碼. 
這包含了 C++ 資料物件的記憶體配置,包含先行定義和使用者定義的資料型態,也包含了內部 Compiler 產生的物件,如: virtual tables. 
它也包含了函數呼叫介面,例外處理介面,全域名稱空間和多樣物件程式碼的協定. 

GCC 將在 3.2 版以後為編譯器提供此功能~
		]]>
	</description>
	<content:encoded><![CDATA[
			ABI 是 Application Binary Interface, 它是一種介於使用者 C++ 程式碼和提供實作系統和 library 的物件程式碼. <br />
這包含了 C++ 資料物件的記憶體配置,包含先行定義和使用者定義的資料型態,也包含了內部 Compiler 產生的物件,如: virtual tables. <br />
它也包含了函數呼叫介面,例外處理介面,全域名稱空間和多樣物件程式碼的協定. <br />
<br />
GCC 將在 3.2 版以後為編譯器提供此功能~
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553847.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553847.html</guid>
	<category>C/C++</category>
	<pubDate>Sat, 21 Dec 2002 05:19:09 +0800</pubDate>
</item>
<item>
	<title>C++ 的 wstring</title>
	<description><![CDATA[
			在 C 裡面有 wchar_t
  那麼在 C++ 應當也有對應的 container 才對
  經查訪之後
  應該是 wstring
於是就寫了一個小程式來測試一下
#include &lt;iostream&gt;
  #include &lt;string&gt;
  
int
main(int argc, char* argv[] ) {
  wstring ws;
  ws=wstring(&quot;This is a test&quot;);
  cout &lt;&lt; ws &lt;&lt; endl;
}
但卻遭遇編譯失敗.
在看過 C++ 的 header 之後
  發覺他把 wstring 的部分給 remark 起來了
  所以我利用 grep 去找尋相關的部分
  並做了一些修正如下:
  
  將 string 中的 wstring 前頭的註解拿掉
std/straits.h 134行 #if 0 改為 #if 1
  std/cpp_type_traits.h 130行 #if 0 改為 #if 1
在 #include &lt;string&gt; 之前,要先 
  定義 __USE_ISOC99 或 __USE_UNIX98 (但#define __USE_UNIX98 會使得 pthread 有誤)
  然後再 #include &lt;cwchar&gt; #include &lt;cwctype&gt;
結果呢
  還是不行
  真是傷腦筋~~
也許是因為雖然我用的是 gcc3 ,但header檔卻是 2.96 的緣故吧~

		]]>
	</description>
	<content:encoded><![CDATA[
			<p>在 C 裡面有 wchar_t<br>
  那麼在 C++ 應當也有對應的 container 才對<br>
  經查訪之後<br>
  應該是 wstring</p>
<p>於是就寫了一個小程式來測試一下</p>
<p>#include &lt;iostream&gt;<br>
  #include &lt;string&gt;</p>
  <pre>
int
main(int argc, char* argv[] ) {
  wstring ws;
  ws=wstring(&quot;This is a test&quot;);
  cout &lt;&lt; ws &lt;&lt; endl;
}</pre>
<p>但卻遭遇編譯失敗.</p>
<p>在看過 C++ 的 header 之後<br>
  發覺他把 wstring 的部分給 remark 起來了<br>
  所以我利用 grep 去找尋相關的部分<br>
  並做了一些修正如下:</p>
  <ol>
  <li>將 string 中的 wstring 前頭的註解拿掉</li>
<li>std/straits.h 134行 #if 0 改為 #if 1
  std/cpp_type_traits.h 130行 #if 0 改為 #if 1</li>
<li>在 #include &lt;string&gt; 之前,要先 
  定義 __USE_ISOC99 或 __USE_UNIX98 (但#define __USE_UNIX98 會使得 pthread 有誤)
  然後再 #include &lt;cwchar&gt; #include &lt;cwctype&gt;</li></ol>
<p>結果呢<br>
  還是不行<br>
  真是傷腦筋~~</p>
<p>也許是因為雖然我用的是 gcc3 ,但header檔卻是 2.96 的緣故吧~<br>
</p>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553832.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553832.html</guid>
	<category>C/C++</category>
	<pubDate>Thu, 02 May 2002 22:26:28 +0800</pubDate>
</item>
<item>
	<title>boolean in C</title>
	<description><![CDATA[
			在 C 裡面並沒有 boolean 這種型態的變數
而是使用整數型態來代替
因此常常我們會看到

#define TRUE 1
#define FALSE 0
但是
true 真的是 1 嗎? 那麼 -1 會不會比 1 來個更好,因為所有的 bit 都是 1?
而 false 真的是 0 嗎??

前幾天在看某個程式的 Source code 的時候
我發現它是這樣子定義的

#define TRUE (1==1)
#define FALSE (0==1)
真的很聰明~~

簡單的說
(1==1) 一定是真~~
(0==1) 一定是假~~
compiler 會自己去求出 (1==1) 跟 (0==1) 的值
這樣子就不用我們自己去定義一個特定的數值
將來轉換平台或環境的時候
可以減少轉換的阻礙~~

很簡單的方式,完全讓編譯器去決定 TRUE 跟 FALSE 的值~
		]]>
	</description>
	<content:encoded><![CDATA[
			在 C 裡面並沒有 boolean 這種型態的變數<br />
而是使用整數型態來代替<br />
因此常常我們會看到<br />
<br />
#define TRUE 1<br />
#define FALSE 0<br />
但是<br />
true 真的是 1 嗎? 那麼 -1 會不會比 1 來個更好,因為所有的 bit 都是 1?<br />
而 false 真的是 0 嗎??<br />
<br />
前幾天在看某個程式的 Source code 的時候<br />
我發現它是這樣子定義的<br />
<br />
#define TRUE (1==1)<br />
#define FALSE (0==1)<br />
真的很聰明~~<br />
<br />
簡單的說<br />
(1==1) 一定是真~~<br />
(0==1) 一定是假~~<br />
compiler 會自己去求出 (1==1) 跟 (0==1) 的值<br />
這樣子就不用我們自己去定義一個特定的數值<br />
將來轉換平台或環境的時候<br />
可以減少轉換的阻礙~~<br />
<br />
很簡單的方式,完全讓編譯器去決定 TRUE 跟 FALSE 的值~
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553820.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553820.html</guid>
	<category>C/C++</category>
	<pubDate>Thu, 03 Jan 2002 11:17:53 +0800</pubDate>
</item>
<item>
	<title>STL Container 的 destroy</title>
	<description><![CDATA[
			因為工作的緣故,想要了解一下,如果我把物件丟到 STL Container 裡面的時候,C++會怎麼幫我 destroy, 所以寫了以下這支小程式來驗證一下:


#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
class node {
public:
 node();
 node( string s );
 node( char* s );
 ~node();
private:
 string m_string;
};
node::node() {
 m_string="";
 cout &lt;&lt; "initialize " &lt;&lt; m_string &lt;&lt; endl;
}
node::node(string s):m_string(s) {
  cout &lt;&lt; "initialize " &lt;&lt; m_string &lt;&lt; endl;
}
node::node(char* s):m_string(s) {
 cout &lt;&lt; "initialize " &lt;&lt; m_string &lt;&lt; endl;
}
node::~node() {
 cout &lt;&lt; "destroy " &lt;&lt; m_string &lt;&lt; endl;
}
int main(void) {
 vector&lt;node&gt; vector_test1;
 vector&lt;node*&gt; vector_test2;
 cout &lt;&lt; "Non-Pointer Version" &lt;&lt; endl;
 vector_test1.push_back( node("test1") );
 vector_test1.push_back( node("test2") );
 vector_test1.push_back( node("test3") );
 vector_test1.push_back( node("test4") );
 vector_test1.push_back( node("test5") );
 vector_test1.push_back( node("test6") );
 vector_test1.push_back( node("test7") );
 vector_test1.push_back( node("test8") );
 vector_test1.push_back( node("test9") );
 vector_test1.push_back( node("test10") );
 cout &lt;&lt; endl &lt;&lt; "Pointer Version" &lt;&lt; endl;
 vector_test2.push_back( new node("test1") );
 vector_test2.push_back( new node("test2") );
 vector_test2.push_back( new node("test3") );
 vector_test2.push_back( new node("test4") );
 vector_test2.push_back( new node("test5") );
 vector_test2.push_back( new node("test6") );
 vector_test2.push_back( new node("test7") );
 vector_test2.push_back( new node("test8") );
 vector_test2.push_back( new node("test9") );
 vector_test2.push_back( new node("test10") );
}


執行以後的結果很有趣.
在我的機器上是這樣子的:


Non-Pointer Version
initialize test1
destroy test1
initialize test2
destroy test1
destroy test2
initialize test3
destroy test1
destroy test2
destroy test3
initialize test4
destroy test4
initialize test5
destroy test1
destroy test2
destroy test3
destroy test4
destroy test5
initialize test6
destroy test6
initialize test7
destroy test7
initialize test8
destroy test8
initialize test9
destroy test1
destroy test2
destroy test3
destroy test4
destroy test5
destroy test6
destroy test7
destroy test8
destroy test9
initialize test10
destroy test10
Pointer Version
initialize test1
initialize test2
initialize test3
initialize test4
initialize test5
initialize test6
initialize test7
initialize test8
initialize test9
initialize test10
destroy test1
destroy test2
destroy test3
destroy test4
destroy test5
destroy test6
destroy test7
destroy test8
destroy test9
destroy test10


這說明了幾件事情:
1.如果我不是利用 new 去建立物件,而是利用宣告建立物件的話
c++只是把建立好的物件複製一份到 container 中,接著就釋放他了
如果是利用 new 去建立物件,那麼在程式的最後,container 會自動呼叫每個元素的 解構子 destroyer 去解構,而無須我們再手動去解構他們.
2.vector 會自我成長,但是在成長的時候,會同樣利用複製的方式,把原本在 container 的資料複製到新的 container 裡面去,但這樣子不是很有效率
		]]>
	</description>
	<content:encoded><![CDATA[
			因為工作的緣故,想要了解一下,如果我把物件丟到 STL Container 裡面的時候,C++會怎麼幫我 destroy, 所以寫了以下這支小程式來驗證一下:<br />
<br />
<pre id=code><br />
#include &lt;iostream&gt;<br />
#include &lt;string&gt;<br />
#include &lt;vector&gt;<br />
class node {<br />
public:<br />
 node();<br />
 node( string s );<br />
 node( char* s );<br />
 ~node();<br />
private:<br />
 string m_string;<br />
};<br />
node::node() {<br />
 m_string="";<br />
 cout &lt;&lt; "initialize " &lt;&lt; m_string &lt;&lt; endl;<br />
}<br />
node::node(string s):m_string(s) {<br />
  cout &lt;&lt; "initialize " &lt;&lt; m_string &lt;&lt; endl;<br />
}<br />
node::node(char* s):m_string(s) {<br />
 cout &lt;&lt; "initialize " &lt;&lt; m_string &lt;&lt; endl;<br />
}<br />
node::~node() {<br />
 cout &lt;&lt; "destroy " &lt;&lt; m_string &lt;&lt; endl;<br />
}<br />
int main(void) {<br />
 vector&lt;node&gt; vector_test1;<br />
 vector&lt;node*&gt; vector_test2;<br />
 cout &lt;&lt; "Non-Pointer Version" &lt;&lt; endl;<br />
 vector_test1.push_back( node("test1") );<br />
 vector_test1.push_back( node("test2") );<br />
 vector_test1.push_back( node("test3") );<br />
 vector_test1.push_back( node("test4") );<br />
 vector_test1.push_back( node("test5") );<br />
 vector_test1.push_back( node("test6") );<br />
 vector_test1.push_back( node("test7") );<br />
 vector_test1.push_back( node("test8") );<br />
 vector_test1.push_back( node("test9") );<br />
 vector_test1.push_back( node("test10") );<br />
 cout &lt;&lt; endl &lt;&lt; "Pointer Version" &lt;&lt; endl;<br />
 vector_test2.push_back( new node("test1") );<br />
 vector_test2.push_back( new node("test2") );<br />
 vector_test2.push_back( new node("test3") );<br />
 vector_test2.push_back( new node("test4") );<br />
 vector_test2.push_back( new node("test5") );<br />
 vector_test2.push_back( new node("test6") );<br />
 vector_test2.push_back( new node("test7") );<br />
 vector_test2.push_back( new node("test8") );<br />
 vector_test2.push_back( new node("test9") );<br />
 vector_test2.push_back( new node("test10") );<br />
}<br />
</pre id=code><br />
<br />
執行以後的結果很有趣.<br />
在我的機器上是這樣子的:<br />
<br />
<pre id=code><br />
Non-Pointer Version<br />
initialize test1<br />
destroy test1<br />
initialize test2<br />
destroy test1<br />
destroy test2<br />
initialize test3<br />
destroy test1<br />
destroy test2<br />
destroy test3<br />
initialize test4<br />
destroy test4<br />
initialize test5<br />
destroy test1<br />
destroy test2<br />
destroy test3<br />
destroy test4<br />
destroy test5<br />
initialize test6<br />
destroy test6<br />
initialize test7<br />
destroy test7<br />
initialize test8<br />
destroy test8<br />
initialize test9<br />
destroy test1<br />
destroy test2<br />
destroy test3<br />
destroy test4<br />
destroy test5<br />
destroy test6<br />
destroy test7<br />
destroy test8<br />
destroy test9<br />
initialize test10<br />
destroy test10<br />
Pointer Version<br />
initialize test1<br />
initialize test2<br />
initialize test3<br />
initialize test4<br />
initialize test5<br />
initialize test6<br />
initialize test7<br />
initialize test8<br />
initialize test9<br />
initialize test10<br />
destroy test1<br />
destroy test2<br />
destroy test3<br />
destroy test4<br />
destroy test5<br />
destroy test6<br />
destroy test7<br />
destroy test8<br />
destroy test9<br />
destroy test10<br />
</pre id=code><br />
<br />
這說明了幾件事情:<br />
1.如果我不是利用 new 去建立物件,而是利用宣告建立物件的話<br />
c++只是把建立好的物件複製一份到 container 中,接著就釋放他了<br />
如果是利用 new 去建立物件,那麼在程式的最後,container 會自動呼叫每個元素的 解構子 destroyer 去解構,而無須我們再手動去解構他們.<br />
2.vector 會自我成長,但是在成長的時候,會同樣利用複製的方式,把原本在 container 的資料複製到新的 container 裡面去,但這樣子不是很有效率
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553817.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553817.html</guid>
	<category>C/C++</category>
	<pubDate>Wed, 12 Dec 2001 20:31:31 +0800</pubDate>
</item>
<item>
	<title>筆記:深度探索C++物件模型 第一章</title>
	<description><![CDATA[
			摘要

  C++ Object Model
  繼承
  虛擬繼承
  class 與 struct
  多型

C++ Object Model
一般來說有三種實作方式:
1.Simple: 一個Object內有一個 slot table, table 中各 slot 指向一個 member.

2.Table: 把 member 和 function 抽出來為 兩個 table, 物件內含一指標指向這兩個 table, 而 function 
  table 又指向各 function.

3.目前一般常見 C++ Compiler 所採用的物件模型, 則是將 member 放在物件中,物件內含一指標指向 function table, 
  而 static member 則另外存放於 heap 中.

繼承
早期,直接把 base class member 含括到 derived class, 較快; 但若 base class 變更,會造成 derived 
  class 需重新 compile.
2.0 規格以後,為有關聯的 virtual base class 加上指標:

  導入 virtual base class table
  擴充現有的 virtual table

簡單的說就是在衍生類別中加上指標指回 base class 的 member 和 function.
&nbsp;
虛擬繼承
亦可指定 virtual, 如 iostream

class istream: virtual public ios {...};
class ostream:virtual public ios {...};

此情況下, base class 不管在繼承串列中被衍生幾次,均只存在一個實體,上例中, ios 將只存在一個.
&nbsp;

class 和 struct
class 和 struct 幾乎相同,但仍然請依照需要乖乖使用.
&nbsp;
多型
只有透過 pointer 或 reference 的間接處理,才支援多型.
		]]>
	</description>
	<content:encoded><![CDATA[
			<h3>摘要</h3>
<ul>
  <li><a href="#ObjectModel">C++ Object Model</a></li>
  <li><a href="#inherit">繼承</a></li>
  <li><a href="#virtualinherit">虛擬繼承</a></li>
  <li><a href="#struct">class 與 struct</a></li>
  <li><a href="#polymorph">多型</a></li>
</ul>
<h3>C++ Object Model<a name="ObjectModel"></a></h3>
<p>一般來說有三種實作方式:</p>
<p>1.Simple: 一個Object內有一個 slot table, table 中各 slot 指向一個 member.</p>
<p><img src="images/insideobj_chap1_1.jpg" width="292" height="339"></p>
<p>2.Table: 把 member 和 function 抽出來為 兩個 table, 物件內含一指標指向這兩個 table, 而 function 
  table 又指向各 function.</p>
<p><img src="images/insideobj_chap1_2.jpg" width="503" height="287"></p>
<p>3.目前一般常見 C++ Compiler 所採用的物件模型, 則是將 member 放在物件中,物件內含一指標指向 function table, 
  而 static member 則另外存放於 heap 中.</p>
<p><img src="images/insideobj_chap1_3.jpg" width="503" height="317"></p>
<h3>繼承<a name="inherit"></a></h3>
<p>早期,直接把 base class member 含括到 derived class, 較快; 但若 base class 變更,會造成 derived 
  class 需重新 compile.</p>
<p>2.0 規格以後,為有關聯的 virtual base class 加上指標:</p>
<ol>
  <li>導入 virtual base class table</li>
  <li>擴充現有的 virtual table</li>
</ol>
<p>簡單的說就是在衍生類別中加上指標指回 base class 的 member 和 function.</p>
<p>&nbsp;</p>
<h3>虛擬繼承<a name="virtualinherit"></a></h3>
<p>亦可指定 virtual, 如 iostream</p>
<pre>
class istream: virtual public ios {...};
class ostream:virtual public ios {...};
</pre>
<p>此情況下, base class 不管在繼承串列中被衍生幾次,均只存在一個實體,上例中, ios 將只存在一個.</p>
<p>&nbsp;</p>
<p></p>
<h3>class 和 struct<a name="struct"></a></h3>
<p>class 和 struct 幾乎相同,但仍然請依照需要乖乖使用.</p>
<p>&nbsp;</p>
<h3>多型<a name="polymorph"></a></h3>
<p>只有透過 pointer 或 reference 的間接處理,才支援多型.</p>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553804.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553804.html</guid>
	<category>C/C++</category>
	<pubDate>Sat, 15 Sep 2001 16:40:54 +0800</pubDate>
</item>
<item>
	<title>筆記:深度探索C++物件模型 第四章</title>
	<description><![CDATA[
			Function 
  語意學 ( The Semantics of Function )
摘要:
nonstatic member function
virtual member function
static member function
virtual member function(單一繼承)
多重繼承
函式的效能
Point-to-member functions
inline function
&nbsp;
nonstatic member function
nonstatic member function 實際上會被內化為 nonmember 的形式,步驟如下:

  改寫函式的 signature 以安插一個額外的參數,用以提供一個存取管道,使 class object 得以將此函式喚起.額外參數就是 this.
  將對 &quot;nonstatic data member 的存取動作&quot; 改經由 this 指標存取.
  改寫成一個外部函式,將函式名稱經過 &quot;mangling&quot; 處理,以成為一個獨一無二的語彙.

名稱的特殊處理 (Name mangling)
一般而言, member 的名稱後面會被加上 class 名稱. 若你宣告 extern &quot;C&quot;, 就會壓抑 nonmember 
  functions 的 &quot;mangling&quot; 效果. 此 mangling 的動作, 各家 compiler 實作方式不同.
&nbsp;
virtual member functions
若 ptr-&gt;normalize(); 
則會被轉化為
(*ptr-&gt;vptr[1])(ptr);
其中:

  vptr 表示由 compiler 產生的指標,指向 virtual table, 其名稱也會被 &quot;mangled&quot;, 因為在一個複雜的衍生體系中,可能存在多個 
    vptrs. 
  1 是 virtual tabe slot 的索引值.
  第二個 ptr 表示 this 指標.

若宣告為 inline, 則會被 compiler 當作一般 nonstatic member function 一樣地決議,提供極大的效率利益.
&nbsp;
static member function
一般建議把 static data member 宣告為 nonpublic, 並提供一個或多個 member functions 來存取之.
主要特性: 沒有 this 指標.
次要特性: 他不能直接存取其 class 中的 nonstatic members; 他不能被宣告為 const, volatile, 或 virtual; 
  他不需要經由 class object 才被喚起.
如果取一個 static member function 的位址, 將獲得其在記憶體中的位置,其位址的型別並不是一個 &quot;指向 class member 
  function 的指標&quot;, 而是一個 &quot;nonmember 函式指標&quot;.即 
&amp;Point3D::object_count();
會得到
 unsigned int (*)();
而非
 unsigned int (Point3D:*)();
差不多等同於 nonmember function.
p.s. object_count 原型宣告為
unsigned int
Point3D::
object_count() {
	return _object_count;
}
&nbsp;
virtual member function(單一繼承)
單一繼承一般是在每個多型的 class object 身上增加 2 個 member:

  一個字串或數字,表示 class 型別.
  一個指標指向某表格,表格中持有程式的 virtual functions 的執行時期位址.為了找到函式位址,每個 virtual function 
    被指派一個表格索引值.

這些工作都由 compiler 完成. 執行時期要做的只是在特定的 virtual table slot 中啟動 virtual function.
圖解. 若 Point3D 繼承 Point2D 繼承 Point, 那麼個別的 virtual table 就可能是

於是當
Point *ptr;
ptr=new Point3D();
ptr-&gt;z();
compiler 可以把該呼叫轉化為
(*ptr-&gt;vptr[4])(ptr);
&nbsp;
多重繼承
在多重繼承中支援 virtual function,其複雜度圍繞在第二個及後繼的 base class 上,以及&quot;必須在執行時期調整 this 
  指標&quot;上.

即後繼的 class 會有多個 virtual table.
將後繼的物件位址指定給一個 base1 指標或 base2 指標時, virtual table 就要視指標的型態作切換,以免呼叫到錯誤的函數.
效率若依照原始 c++ 模型,會變的不好,但這方面各家 compiler 會利用 thunk 或 address points 策略來改善.
虛擬繼承下的 virtual functions

實作上,同樣要調整 this 指標,很複雜,效率也不一定較好,建議不要在一個 virtual base class 中宣告 nonstatic data 
  members.
&nbsp;
函式的效能
inline &gt; (nonmember friend=static member=nonstatic member) &gt; virtual 
  member &gt; virtual member(多重繼承) &gt; virtual member(虛擬繼承)
virtual member 在層數越多的狀況下,其執行時間也成正比增加.
&nbsp;
Point-to-Member functions
double (Point::*pmf)();
double (Point::*coord)()=&amp;Point::x; //初始
coord=&amp;Point::y; //或是這樣初始
於是可以
(origin.*coord)();
或
(ptr-&gt;*coord)();
這樣用.
實際上則會轉化為
(coord)(&amp;origin);
和
(coord)(ptr);
支援&quot;指向 virtual member functions&quot;的指標
考慮如下片段(假設 z 為 virtual function)
float (Point::*pmf)()=&amp;Point::z;
Point *ptr=new Point3D;
ptr-&gt;z(); //ok
(ptr-&gt;*pmf)(); //仍然 ok
compiler 實作上,必須定義 pmf, 使他能持有兩種數值,並且其數值能區分其意義.
多重繼承的狀況:
stroustrup 利用 union 來處理
struct __mptr {
	int delta;
	int index; //處理 virtual table 索引,不指時為 -1
	union {
		ptrfunc faddr;
		int v_offset; //處理 nonvirtual member function
	};
};
於是
(ptr-&gt;*pmf)();
會變成
(pmf.index&lt;0)?
	(*pmf.faddr)(ptr):
	(*ptr-&gt;vptr[pmf.index](ptr));
Microsoft 以 vcall thunk 來作檢查,避免浪費檢查的時間,但副作用是,當傳遞一個不變值的指標給 member function 時,需要產生暫時性的物件.
效率:同樣地,不牽涉到&quot;虛擬&quot;+&quot;多重&quot;情況的,效率較佳.
&nbsp;
inline function
一般處理時,有兩個階段:

  分析函式,若因某些問題(複雜度過高,建構問題...等)被判斷不可 inline, 則會轉為 static 函式,並在被編譯模組中產生對應的函式定義.
  真正的 inline function 擴展動作,是在呼叫的那一點上,這會帶來參數的求值動作及暫時性物件的管理.

通常需進入 assembler 中,才能得知是否真實現了 inline
形式參數擴展的情況大致如下:
inline int min(int i, int j) {
	return i&lt;j?i:j;
}
int
main() {
	int minval;
	int val1=1024,val2=2048;
	minval=min(val1,val2);		//minval=val1&lt;val2?val1:val2;
	minval=min(1024,2048);		//minval=1024;
	minval=min(foo(),bar()+1);	//int t1,t2;
								//minval=(t1=foo()),(t2=bar()+1),t1&lt;t2?t1:t2;

&nbsp;
區域變數的情況
若
inline int
min(int i, int j) {
	int minval=i&lt;j?i:j;
	return minval;
}
int local_var;
int minval;
...
minval=min(val1,val2);
則可能會代換為
int local_var;
int minval;
int __min_lv_minval;
minval=(__min_lv_minval=val1&lt;val2?val1:val2),__min_ln_minval;
inline 函式中的區域變數再加上有副作用的參數,可能會導致大量暫時性物件的產生.並且使得程式大小暴增.避免過於複雜的 inline 函式,以免 compiler 
  無法擴展開來.
		]]>
	</description>
	<content:encoded><![CDATA[
			<h2 align="center"><font face="Verdana, Arial, Helvetica, sans-serif">Function 
  語意學 ( The Semantics of Function )</font></h2>
<p>摘要:</p>
<p><a href="#nonstatic_member">nonstatic member function</a></p>
<p><a href="#virtual_member">virtual member function</a></p>
<p><a href="#static_member">static member function</a></p>
<p><a href="#single">virtual member function(單一繼承)</a></p>
<p><a href="#multiple_inherit">多重繼承</a></p>
<p><a href="#performance">函式的效能</a></p>
<p><a href="#point_to_member_functions">Point-to-member functions</a></p>
<p><a href="#inline_function">inline function</a></p>
<p>&nbsp;</p>
<h3>nonstatic member function<a name="nonstatic_member"></a></h3>
<p>nonstatic member function 實際上會被內化為 nonmember 的形式,步驟如下:</p>
<ol>
  <li>改寫函式的 signature 以安插一個額外的參數,用以提供一個存取管道,使 class object 得以將此函式喚起.額外參數就是 this.</li>
  <li>將對 &quot;nonstatic data member 的存取動作&quot; 改經由 this 指標存取.</li>
  <li>改寫成一個外部函式,將函式名稱經過 &quot;mangling&quot; 處理,以成為一個獨一無二的語彙.</li>
</ol>
<p>名稱的特殊處理 (Name mangling)</p>
<p>一般而言, member 的名稱後面會被加上 class 名稱. 若你宣告 extern &quot;C&quot;, 就會壓抑 nonmember 
  functions 的 &quot;mangling&quot; 效果. 此 mangling 的動作, 各家 compiler 實作方式不同.</p>
<p>&nbsp;</p>
<h3>virtual member functions<a name="virtual_member"></a></h3>
<pre>若 ptr-&gt;normalize(); </pre>
<p>則會被轉化為</p>
<pre>(*ptr-&gt;vptr[1])(ptr);</pre>
<p>其中:</p>
<ul>
  <li>vptr 表示由 compiler 產生的指標,指向 virtual table, 其名稱也會被 &quot;mangled&quot;, 因為在一個複雜的衍生體系中,可能存在多個 
    vptrs. </li>
  <li>1 是 virtual tabe slot 的索引值.</li>
  <li>第二個 ptr 表示 this 指標.</li>
</ul>
<p>若宣告為 inline, 則會被 compiler 當作一般 nonstatic member function 一樣地決議,提供極大的效率利益.</p>
<p>&nbsp;</p>
<h3>static member function<a name="static_member"></a></h3>
<p>一般建議把 static data member 宣告為 nonpublic, 並提供一個或多個 member functions 來存取之.</p>
<p>主要特性: 沒有 this 指標.</p>
<p>次要特性: 他不能直接存取其 class 中的 nonstatic members; 他不能被宣告為 const, volatile, 或 virtual; 
  他不需要經由 class object 才被喚起.</p>
<p>如果取一個 static member function 的位址, 將獲得其在記憶體中的位置,其位址的型別並不是一個 &quot;指向 class member 
  function 的指標&quot;, 而是一個 &quot;nonmember 函式指標&quot;.即 </p>
<pre>&amp;Point3D::object_count();</pre>
<p>會得到</p>
<pre> unsigned int (*)();</pre>
<p>而非</p>
<pre> unsigned int (Point3D:*)();</pre>
<p>差不多等同於 nonmember function.</p>
<p>p.s. object_count 原型宣告為</p>
<pre>unsigned int
Point3D::
object_count() {
	return _object_count;
}</pre>
<p>&nbsp;</p>
<h3>virtual member function(單一繼承)<a name="single"></a></h3>
<p>單一繼承一般是在每個多型的 class object 身上增加 2 個 member:</p>
<ol>
  <li>一個字串或數字,表示 class 型別.</li>
  <li>一個指標指向某表格,表格中持有程式的 virtual functions 的執行時期位址.為了找到函式位址,每個 virtual function 
    被指派一個表格索引值.</li>
</ol>
<p>這些工作都由 compiler 完成. 執行時期要做的只是在特定的 virtual table slot 中啟動 virtual function.</p>
<p>圖解. 若 Point3D 繼承 Point2D 繼承 Point, 那麼個別的 virtual table 就可能是</p>
<p><img src="images/insideobj_chap4_1.jpg" width="311" height="243"></p>
<p>於是當</p>
<pre>Point *ptr;
ptr=new Point3D();
ptr-&gt;z();</pre>
<p>compiler 可以把該呼叫轉化為</p>
<pre>(*ptr-&gt;vptr[4])(ptr);</pre>
<p>&nbsp;</p>
<h3>多重繼承<a name="multiple_inherit"></a></h3>
<p>在多重繼承中支援 virtual function,其複雜度圍繞在第二個及後繼的 base class 上,以及&quot;必須在執行時期調整 this 
  指標&quot;上.</p>
<p><img src="images/insideobj_chap4_2.jpg" width="485" height="565"></p>
<p>即後繼的 class 會有多個 virtual table.</p>
<p>將後繼的物件位址指定給一個 base1 指標或 base2 指標時, virtual table 就要視指標的型態作切換,以免呼叫到錯誤的函數.</p>
<p>效率若依照原始 c++ 模型,會變的不好,但這方面各家 compiler 會利用 thunk 或 address points 策略來改善.</p>
<p>虛擬繼承下的 virtual functions</p>
<p><img src="images/insideobj_chap4_3.jpg" width="461" height="460"></p>
<p>實作上,同樣要調整 this 指標,很複雜,效率也不一定較好,建議不要在一個 virtual base class 中宣告 nonstatic data 
  members.</p>
<p>&nbsp;</p>
<h3>函式的效能<a name="performance"></a></h3>
<p>inline &gt; (nonmember friend=static member=nonstatic member) &gt; virtual 
  member &gt; virtual member(多重繼承) &gt; virtual member(虛擬繼承)</p>
<p>virtual member 在層數越多的狀況下,其執行時間也成正比增加.</p>
<p>&nbsp;</p>
<h3>Point-to-Member functions<a name="point_to_member_functions"></a></h3>
<pre>double (Point::*pmf)();
double (Point::*coord)()=&amp;Point::x; //初始
coord=&amp;Point::y; //或是這樣初始</pre>
<p>於是可以</p>
<pre>(origin.*coord)();</pre>
<p>或</p>
<pre>(ptr-&gt;*coord)();</pre>
<p>這樣用.</p>
<p>實際上則會轉化為</p>
<pre>(coord)(&amp;origin);</pre>
<p>和</p>
<pre>(coord)(ptr);</pre>
<p>支援&quot;指向 virtual member functions&quot;的指標</p>
<p>考慮如下片段(假設 z 為 virtual function)</p>
<pre>float (Point::*pmf)()=&amp;Point::z;
Point *ptr=new Point3D;
ptr-&gt;z(); //ok
(ptr-&gt;*pmf)(); //仍然 ok</pre>
<p>compiler 實作上,必須定義 pmf, 使他能持有兩種數值,並且其數值能區分其意義.</p>
<p>多重繼承的狀況:</p>
<p>stroustrup 利用 union 來處理</p>
<pre>struct __mptr {
	int delta;
	int index; //處理 virtual table 索引,不指時為 -1
	union {
		ptrfunc faddr;
		int v_offset; //處理 nonvirtual member function
	};
};</pre>
<p>於是</p>
<p>(ptr-&gt;*pmf)();</p>
<p>會變成</p>
<pre>(pmf.index&lt;0)?
	(*pmf.faddr)(ptr):
	(*ptr-&gt;vptr[pmf.index](ptr));</pre>
<p>Microsoft 以 vcall thunk 來作檢查,避免浪費檢查的時間,但副作用是,當傳遞一個不變值的指標給 member function 時,需要產生暫時性的物件.</p>
<p>效率:同樣地,不牽涉到&quot;虛擬&quot;+&quot;多重&quot;情況的,效率較佳.</p>
<p>&nbsp;</p>
<h3>inline function<a name="inline_function"></a></h3>
<p>一般處理時,有兩個階段:</p>
<ol>
  <li>分析函式,若因某些問題(複雜度過高,建構問題...等)被判斷不可 inline, 則會轉為 static 函式,並在被編譯模組中產生對應的函式定義.</li>
  <li>真正的 inline function 擴展動作,是在呼叫的那一點上,這會帶來參數的求值動作及暫時性物件的管理.</li>
</ol>
<p>通常需進入 assembler 中,才能得知是否真實現了 inline</p>
<p>形式參數擴展的情況大致如下:</p>
<pre>inline int min(int i, int j) {
	return i&lt;j?i:j;
}
int
main() {
	int minval;
	int val1=1024,val2=2048;
	minval=min(val1,val2);		//minval=val1&lt;val2?val1:val2;
	minval=min(1024,2048);		//minval=1024;
	minval=min(foo(),bar()+1);	//int t1,t2;
								//minval=(t1=foo()),(t2=bar()+1),t1&lt;t2?t1:t2;
</pre>
<p>&nbsp;</p>
<p>區域變數的情況</p>
<p>若</p>
<pre>inline int
min(int i, int j) {
	int minval=i&lt;j?i:j;
	return minval;
}
int local_var;
int minval;
...
minval=min(val1,val2);</pre>
<p>則可能會代換為</p>
<pre>int local_var;
int minval;
int __min_lv_minval;
minval=(__min_lv_minval=val1&lt;val2?val1:val2),__min_ln_minval;</pre>
<p>inline 函式中的區域變數再加上有副作用的參數,可能會導致大量暫時性物件的產生.並且使得程式大小暴增.避免過於複雜的 inline 函式,以免 compiler 
  無法擴展開來.</p>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553803.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553803.html</guid>
	<category>C/C++</category>
	<pubDate>Wed, 12 Sep 2001 22:01:28 +0800</pubDate>
</item>
<item>
	<title>筆記:深度探索C++物件模型 第三章</title>
	<description><![CDATA[
			The Semantics of Data
摘要

static data member
物件的大小
data member 的佈局
data member 的存取
繼承與 data member
指向 data members 的指標

&nbsp;
static data member
static data member 永遠在 global data segment, 不影響 class object 的大小.
&nbsp;
物件的大小
class object 的大小有可能因為 compiler 實作物件模型的方式不同而有不同,原因:

  compiler自動加上額外 data member, 以支援某些語言特性.
  alignment 邊界調整需要(比如: char 為配合機器特性而以 long 存放)

雖然目前 C++ compiler 已經非常進步,但早期會有兩種程式寫作防禦方格,以防止資料繫結錯誤.

  把 data member 寫在 class 的開頭.
   inline function 移到 class 宣告之後,而不在宣告區裡面實作, 如:

extern int x;
class Point3D {
private:
	float x,y,z;
public:
	float X() const {return x;} 	//到底傳回哪個 x 呢?
	float getX() const; 			//早期若不這樣寫,會造成資料繫結到上面那個 x
};
inline float
Point3D::
getX() const {
	return x;
}
&nbsp;
data member 的佈局
data member 實際的存放(佈局)
注意:各 data member 再實際存放時不一定連續.
如:
class Point3D {
	float x;
	float y;
	float z;
};
x,y,z 不一定是連續的,有可能為了要補 alignment 或因compiler 的調整為 y,x,z, 主要原因是 C++ Standard 對此採放任態度.一般而言,仍是連續的.
&nbsp;
data member 的存取
當 member 被宣告為 static 時,實際上和一般變數存取一樣,因為 static member 存放在 class 之外,不需要再透過 class 
  去存取. 若不是宣告為 static, 事實上,都會透過一個隱含的 class object (this) 完成,也就是類似這樣
Point3D
Point3D:translate( Point3D* this, const Point3D &amp;pt) {
	this-&gt;x+=pt.x;
	this-&gt;y+=pt.y;
	this-&gt;z+=pt.z;
}
若在程式中對 data member 做存取,如:
origin.y=0.0;
那麼實際上將等於
&amp;origin+(&amp;Point3D::y-1)
指向 data member 的指標,其 offset 值總是被加上 1, 這樣子 compiler就能需分出&quot;一個指向 data member 
  的指標,用以指出 class 的第一個 member&quot;和&quot;一個指向 data member 的指標,未指向任何 member&quot;的情況.(那就是上面為什麼要 
  -1的原因).
&nbsp;
繼承與 data member
在沒有 virtual function 的狀況下,和 struct 相同.原本是獨立不相干的 class 湊成 type / subtype, 並有繼承關係(如 
  Point2D -&gt; Point3D), 經驗不足的人可能會重複設計一些相同動作的函式,以 constructor 和 operator += 為例, 
  可以做成 inline. 另外把一個 class 分解為二層或更多層,有可能會為了&quot;表現 class 體系之抽象化&quot;而膨脹所需空間.如: 
  parent 為兩個 int, child 為一個 char, grandchild 為一個 char, compiler 會為了 alignment 
  而填補空間.
加上 virtual function 後,將需要導入 vptr, 並在每個函式做 vptr 的處理, 解構時也要把 vptr 抹消,一般而言, vptr 
  放在 class 的最後面,但 visual c++ 放在最前面,主要是為了繼承的效率問題.
class 若內含一個或多個 virtual base class subobjects, 將會分割為二部分:一個不變區域和一個共享區域,不變區域的資料,不管後繼如何衍化,總擁有固定的 
  offset(自 object 的起頭算起), 所以可以直接存取,至於共享區域,表現的是 virtual base class subobject, 其位置會因為每次衍生動作而有變化,只能被間接存取, 
  compiler 會在子類別中安插一些指標,每個指標指向一個 virtual base class.這樣的做法有兩個缺點:

  每個物件必須針對其每個 virtual base class 背負一個額外指標(空間增加).
  由於虛擬繼承串鏈的加長,導致間接存取層次增加.(時間增加)

大部分編譯器到今天仍使用&quot;經由拷貝動作取得所有的 nested virtual base class 指標,放到 derived class 
  object 之中&quot;來解決第二個問題.
第一個問題一般有兩個解法. Microsoft compiler 引入 virtual base class table, 每一個 class object 
  如果有一個或多個 virtual base class, 就會由 compiler 安插一個 pointer, 指向 virtual base class 
  table. 第二個方法是在 virtual function table 中放置 virtual base class 的 offset.
&nbsp;
指向 data members 的指標
可用以決定 vptr 放在 class 的 begin 或 end, 另一個用途可用來決定 class 中的 access sections.
usage:
	printf(&quot;&amp;Point3d::x=%p\n&quot;,&amp;Point3d::x);
如果 vptr 在物件尾巴,則 offset 為 0
如果 vptr 在物件起頭,則 offset 為 4
但為何在尾巴時,傳回值總是多 1? 意即 1 主要是用以區分&quot;沒有指向任何 data member&quot; 的指標和 &quot;指向第一個 
  data member&quot; 的指標.
注意,此種額外的間接性會降低&quot;把所有處理都搬移到暫存器中執行&quot;最佳能力.
		]]>
	</description>
	<content:encoded><![CDATA[
			<h2 align="center"><font face="Verdana, Arial, Helvetica, sans-serif">The Semantics of Data</font></h2>
<h3>摘要</h3>
<ul>
<li><a href="#staticdatamember">static data member</a></li>
<li><a href="#objectsize">物件的大小</a></li>
<li><a href="#datamemberlayout">data member 的佈局</a></li>
<li><a href="#datamemberaccess">data member 的存取</a></li>
<li><a href="#inheritanddatamember">繼承與 data member</a></li>
<li><a href="#datamemberptr">指向 data members 的指標</a></li>
</ul>
<p>&nbsp;</p>
<h3>static data member<a name="staticdatamember"></a></h3>
<p>static data member 永遠在 global data segment, 不影響 class object 的大小.</p>
<p>&nbsp;</p>
<h3>物件的大小<a name="objectsize"></a></h3>
<p>class object 的大小有可能因為 compiler 實作物件模型的方式不同而有不同,原因:</p>
<ol>
  <li>compiler自動加上額外 data member, 以支援某些語言特性.</li>
  <li>alignment 邊界調整需要(比如: char 為配合機器特性而以 long 存放)</li>
</ol>
<p>雖然目前 C++ compiler 已經非常進步,但早期會有兩種程式寫作防禦方格,以防止資料繫結錯誤.</p>
<ol>
  <li>把 data member 寫在 class 的開頭.</li>
  <li> inline function 移到 class 宣告之後,而不在宣告區裡面實作, 如:</li>
</ol>
<pre>extern int x;
class Point3D {
private:
	float x,y,z;
public:
	float X() const {return x;} 	//到底傳回哪個 x 呢?
	float getX() const; 			//早期若不這樣寫,會造成資料繫結到上面那個 x
};
inline float
Point3D::
getX() const {
	return x;
}</pre>
<p>&nbsp;</p>
<h3>data member 的佈局<a name="datamemberlayout"></a></h3>
<p>data member 實際的存放(佈局)</p>
<p>注意:各 data member 再實際存放時不一定連續.</p>
<p>如:</p>
<pre>class Point3D {
	float x;
	float y;
	float z;
};</pre>
<p>x,y,z 不一定是連續的,有可能為了要補 alignment 或因compiler 的調整為 y,x,z, 主要原因是 C++ Standard 對此採放任態度.一般而言,仍是連續的.</p>
<p>&nbsp;</p>
<h3>data member 的存取<a name="datamemberaccess"></a></h3>
<p>當 member 被宣告為 static 時,實際上和一般變數存取一樣,因為 static member 存放在 class 之外,不需要再透過 class 
  去存取. 若不是宣告為 static, 事實上,都會透過一個隱含的 class object (this) 完成,也就是類似這樣</p>
<pre>Point3D
Point3D:translate( Point3D* this, const Point3D &amp;pt) {
	this-&gt;x+=pt.x;
	this-&gt;y+=pt.y;
	this-&gt;z+=pt.z;
}</pre>
<p>若在程式中對 data member 做存取,如:</p>
<pre>origin.y=0.0;</pre>
<p>那麼實際上將等於</p>
<pre>&amp;origin+(&amp;Point3D::y-1)</pre>
<p>指向 data member 的指標,其 offset 值總是被加上 1, 這樣子 compiler就能需分出&quot;一個指向 data member 
  的指標,用以指出 class 的第一個 member&quot;和&quot;一個指向 data member 的指標,未指向任何 member&quot;的情況.(那就是上面為什麼要 
  -1的原因).</p>
<p>&nbsp;</p>
<h3>繼承與 data member<a name="inheritanddatamember"></a></h3>
<p>在沒有 virtual function 的狀況下,和 struct 相同.原本是獨立不相干的 class 湊成 type / subtype, 並有繼承關係(如 
  Point2D -&gt; Point3D), 經驗不足的人可能會重複設計一些相同動作的函式,以 constructor 和 operator += 為例, 
  可以做成 inline. 另外把一個 class 分解為二層或更多層,有可能會為了&quot;表現 class 體系之抽象化&quot;而膨脹所需空間.如: 
  parent 為兩個 int, child 為一個 char, grandchild 為一個 char, compiler 會為了 alignment 
  而填補空間.</p>
<p>加上 virtual function 後,將需要導入 vptr, 並在每個函式做 vptr 的處理, 解構時也要把 vptr 抹消,一般而言, vptr 
  放在 class 的最後面,但 visual c++ 放在最前面,主要是為了繼承的效率問題.</p>
<p>class 若內含一個或多個 virtual base class subobjects, 將會分割為二部分:一個不變區域和一個共享區域,不變區域的資料,不管後繼如何衍化,總擁有固定的 
  offset(自 object 的起頭算起), 所以可以直接存取,至於共享區域,表現的是 virtual base class subobject, 其位置會因為每次衍生動作而有變化,只能被間接存取, 
  compiler 會在子類別中安插一些指標,每個指標指向一個 virtual base class.這樣的做法有兩個缺點:</p>
<ol>
  <li>每個物件必須針對其每個 virtual base class 背負一個額外指標(空間增加).</li>
  <li>由於虛擬繼承串鏈的加長,導致間接存取層次增加.(時間增加)</li>
</ol>
<p>大部分編譯器到今天仍使用&quot;經由拷貝動作取得所有的 nested virtual base class 指標,放到 derived class 
  object 之中&quot;來解決第二個問題.</p>
<p>第一個問題一般有兩個解法. Microsoft compiler 引入 virtual base class table, 每一個 class object 
  如果有一個或多個 virtual base class, 就會由 compiler 安插一個 pointer, 指向 virtual base class 
  table. 第二個方法是在 virtual function table 中放置 virtual base class 的 offset.</p>
<p>&nbsp;</p>
<h3>指向 data members 的指標<a name="datamemberptr"></a></h3>
<p>可用以決定 vptr 放在 class 的 begin 或 end, 另一個用途可用來決定 class 中的 access sections.</p>
<p>usage:</p>
<pre>	printf(&quot;&amp;Point3d::x=%p\n&quot;,&amp;Point3d::x);</pre>
<p>如果 vptr 在物件尾巴,則 offset 為 0</p>
<p>如果 vptr 在物件起頭,則 offset 為 4</p>
<p>但為何在尾巴時,傳回值總是多 1? 意即 1 主要是用以區分&quot;沒有指向任何 data member&quot; 的指標和 &quot;指向第一個 
  data member&quot; 的指標.</p>
<p>注意,此種額外的間接性會降低&quot;把所有處理都搬移到暫存器中執行&quot;最佳能力.</p>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553801.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553801.html</guid>
	<category>C/C++</category>
	<pubDate>Tue, 11 Sep 2001 23:25:01 +0800</pubDate>
</item>
<item>
	<title>筆記:深度探索C++物件模型 第二章</title>
	<description><![CDATA[
			摘要

  簡介
  default constructor
  copy constructor
  NRV
  使用 member initialization list.

&nbsp;
簡介
編譯器可能會有隱含的動作,如對 overload operator 的誤判. 因為 compiler 會自動去尋找最符合其意義的來解釋你的意圖.
&nbsp;
constructor
以下是各種 default constructor 的建構情形. 一般若 class 沒有宣告 constructor, 那麼 compiler 會在需要時為他建立一個簡單的 
  constructor, 有四種狀況:
1. class 沒有 constructor, 但成員之中有 member object, 而該 member object 有 default constructor 
  時, compiler 必定會為前者合成一個 default constructor.
假設
class Dopey {...};
class Sneezy {...};
class Bashful {...};
class Snow_White {
	public:
		Dopey dopey;
		Sneezy sneezy;
		Bashful bashful;
	private:
		int mumble;
};
Snow_White 沒有 default constructor, 那麼 compiler 會為他合成一個 default constructor, 
  依序喚起 Dopey, Sneezy, Bashful 的 default constructor. 若 Snow_White 定義了 constructor 
  如下:
Snow_White::Snow_White:sneezy(1024) {
mumble=2048;
}
則 compiler 會擴充為
Snow_White::Snow_White();sneezy(1024) {
dopey.Dopey::Dopey();
sneezy.Sneezy::Sneezy(1024);
bashful.Bashful::Bashful();
mumble=2048;
}
2. 若 class 繼承一個帶有 default constructor 的 class, 則 compiler 會為這個 class 合成一個 constructor, 
  這個 constructor 只呼叫 parent class 的 constructor.
又若有多個 constructor, 但卻沒有 default constructor 時, compiler 會擴張各 constructor, 以便去呼叫必要的 
  default constructor. 但卻不會去合成一個新的 default constructor.
3. 若 class 有 virtual function 的時候, compiler 會在 default constructor 中把 vtable 
  初始化,以便讓子類別能正確呼叫到 virtual function 所對應的 function.
4. virtual base class, 各 compiler 的實作方法不同,但共通點都是要使 virtual base class 在每個 derived 
  class object 中的位置,能夠在 runtime 時準備妥當.例如:
class X { public: int i; };
class A: public virtual X { public: int j; };
class B: public virtual X { public: double d; };
class C:public virtual X {public: int k;};
//無法在編譯時決議(resolve)出 pa-&gt;X::i
void foo( const A* pa) { pa-&gt;i=1024; }
int
main() {
	foo(new A);
	foo(new C);
//...
}

所以, compiler 必須改變&quot;執行存取動作&quot;的程式碼,使得 X::i 能延遲到 runtime 時才決定下來.
以上四種情況都會使 compiler 必須為未宣告 constructor 的 class 合成 default constructor, 這些被 compiler 
  合成出來的東西, 在 C++ Standard 中稱為 implict nontrival default constructor.
除了這四種情況之外, compiler 不會合成任何 constructor.
另外在合成的 default constructor 中, 只有 base class object 和 member class object 會被初始化, 
  其他 nonstatic data member,如: int, int*, int array 等都不會.
新手誤解:
1.任何 class 若沒有 default constructor, 就會自動合成一個.
2. compiler合成的 default constructor 會明白設定&quot;class內每個 data member 的預設值&quot;.
&nbsp;
copy constructor
有三種情況會以一 object 內容作為另一 class object 的初值.

  string bb; string aa=bb;
   object 被當作參數交給函式時.
   函數傳回值是 object 時.

當沒有提供 copy constructor 時,會直接把 member 一個個地複製到要複製的 object 上, 如:
class string {
	char *str;
	int len;
};
則
string noun(&quot;book&quot;);
string verb=noun;
實際上是
verb.str=noun.str;
verb.len=noun.len;
此處的 copy constructor 不等於 copy assignment (operator=)!!
default constructor 和 copy constructor 在必要的時候才用 compiler 產生出來.
請注意 member 若有 pointer 時,那麼預設會把指標指過去,就會有潛在的指標問題!
此種情況應宣告 explicit copy constructor 來解決此問題.如:
class string {
public:
	string(const char *);
	string(const string&amp;);
};
一般 copy 會有四種情況, class 不展現出 &quot;bitwise copy semantics&quot;(即上述狀況):

  1.class 內含一個 member object 而後者有 copy constructor 時.
  2.class衍生自一個 base class, 而 base class 有 copy constructor 時.
  3.class宣告一個或多個 virtual functions.
  4.當 class 衍生自一個繼承串鏈,其中有一個或多個 virtual base classes.

第三種情況需考慮到之前第一章所提到的 virtual table.
因此若父類別有 animate() 和 draw()這兩個 virtual function,而子類別增加 dance(), 則不能
childclass cc1;
parentclass parent1=cc1;
會造成 virtual table 被切掉,因為 parent1 的 virtual table 根本就沒有 dance.若 parent1宣告為參考或指標時,被 
  compiler 合成出來的 copy constructor 會把隱含的 vptr 指向 childclass 的 virtual table, 而非 
  bitwise copy.這個也叫做 upcasting. 
第四種情況則請回想一下 virtual base classes, 因為只會有一個實體存在,因此,compiler 會特別審慎考量,不會 bitwise 
  copy.
NRV最佳化,如下情況會被 compiler 
x bar() {
	X xx;
	//....
	return xx;
}
轉為如下的情況
void bar(X&amp; __result) {
	__result.X::X();
	//....
	return;
}
&nbsp;
NRV
NRV -&gt; Named Return Value 提供重要效率改善.
&nbsp;
使用 member initialization list.
時機:

  當初始化一個 reference member
  當初始化一個 const member 時
  當喚起一個 base class 的 constructor, 而他擁有一個參數時.
  當喚起一個 member class 的 constructor, 而他擁有一個參數時.

Why:
一般這樣寫, compiler 會先產生一暫存物件, 再 assign 給 member object, 所以效率不彰.
class Word {
	String _name;
	int _cnt;
public:
	Word(){
		_name=0;
		_cnt=0;
	}
};
所以要用 Word::Word:_name(0) {_cnt=0;}; 效率較佳.
缺點及注意事項:
注意 initialization list 的初始順序. 若 member object 依賴性太高,最好不要放到 initialization list 
  中,而應移至 constructor 中.
		]]>
	</description>
	<content:encoded><![CDATA[
			<h3>摘要</h3>
<ul>
  <li><a href="#introduce">簡介</a></li>
  <li><a href="#constructor">default constructor</a></li>
  <li><a href="#copyconstructor">copy constructor</a></li>
  <li><a href="#nrv">NRV</a></li>
  <li><a href="#initializationlist">使用 member initialization list.</a></li>
</ul>
<p>&nbsp;</p>
<h3>簡介<a name="introduce"></a></h3>
<p>編譯器可能會有隱含的動作,如對 overload operator 的誤判. 因為 compiler 會自動去尋找最符合其意義的來解釋你的意圖.</p>
<p>&nbsp;</p>
<h3>constructor<a name="constructor"></a></h3>
<p>以下是各種 default constructor 的建構情形. 一般若 class 沒有宣告 constructor, 那麼 compiler 會在需要時為他建立一個簡單的 
  constructor, 有四種狀況:</p>
<p>1. class 沒有 constructor, 但成員之中有 member object, 而該 member object 有 default constructor 
  時, compiler 必定會為前者合成一個 default constructor.</p>
<p>假設</p>
<pre>class Dopey {...};
class Sneezy {...};
class Bashful {...};
class Snow_White {
	public:
		Dopey dopey;
		Sneezy sneezy;
		Bashful bashful;
	private:
		int mumble;
};</pre>
<p>Snow_White 沒有 default constructor, 那麼 compiler 會為他合成一個 default constructor, 
  依序喚起 Dopey, Sneezy, Bashful 的 default constructor. 若 Snow_White 定義了 constructor 
  如下:</p>
<pre>Snow_White::Snow_White:sneezy(1024) {
mumble=2048;
}</pre>
<p>則 compiler 會擴充為</p>
<pre>Snow_White::Snow_White();sneezy(1024) {
dopey.Dopey::Dopey();
sneezy.Sneezy::Sneezy(1024);
bashful.Bashful::Bashful();
mumble=2048;
}</pre>
<p>2. 若 class 繼承一個帶有 default constructor 的 class, 則 compiler 會為這個 class 合成一個 constructor, 
  這個 constructor 只呼叫 parent class 的 constructor.</p>
<p>又若有多個 constructor, 但卻沒有 default constructor 時, compiler 會擴張各 constructor, 以便去呼叫必要的 
  default constructor. 但卻不會去合成一個新的 default constructor.</p>
<p>3. 若 class 有 virtual function 的時候, compiler 會在 default constructor 中把 vtable 
  初始化,以便讓子類別能正確呼叫到 virtual function 所對應的 function.</p>
<p>4. virtual base class, 各 compiler 的實作方法不同,但共通點都是要使 virtual base class 在每個 derived 
  class object 中的位置,能夠在 runtime 時準備妥當.例如:</p>
<pre>class X { public: int i; };
class A: public virtual X { public: int j; };
class B: public virtual X { public: double d; };
class C:public virtual X {public: int k;};
//無法在編譯時決議(resolve)出 pa-&gt;X::i
void foo( const A* pa) { pa-&gt;i=1024; }
int
main() {
	foo(new A);
	foo(new C);
//...
}
</pre>
<p>所以, compiler 必須改變&quot;執行存取動作&quot;的程式碼,使得 X::i 能延遲到 runtime 時才決定下來.</p>
<p>以上四種情況都會使 compiler 必須為未宣告 constructor 的 class 合成 default constructor, 這些被 compiler 
  合成出來的東西, 在 C++ Standard 中稱為 implict nontrival default constructor.</p>
<p>除了這四種情況之外, compiler 不會合成任何 constructor.</p>
<p>另外在合成的 default constructor 中, 只有 base class object 和 member class object 會被初始化, 
  其他 nonstatic data member,如: int, int*, int array 等都不會.</p>
<p>新手誤解:</p>
<p>1.任何 class 若沒有 default constructor, 就會自動合成一個.</p>
<p>2. compiler合成的 default constructor 會明白設定&quot;class內每個 data member 的預設值&quot;.</p>
<p>&nbsp;</p>
<h3>copy constructor<a name="copyconstructor"></a></h3>
<p>有三種情況會以一 object 內容作為另一 class object 的初值.</p>
<ol>
  <li>string bb; string aa=bb;</li>
  <li> object 被當作參數交給函式時.</li>
  <li> 函數傳回值是 object 時.</li>
</ol>
<p>當沒有提供 copy constructor 時,會直接把 member 一個個地複製到要複製的 object 上, 如:</p>
<pre>class string {
	char *str;
	int len;
};</pre>
<p>則</p>
<pre>string noun(&quot;book&quot;);
string verb=noun;</pre>
<p>實際上是</p>
<pre>verb.str=noun.str;
verb.len=noun.len;</pre>
<p>此處的 copy constructor 不等於 copy assignment (operator=)!!</p>
<p>default constructor 和 copy constructor 在必要的時候才用 compiler 產生出來.</p>
<p>請注意 member 若有 pointer 時,那麼預設會把指標指過去,就會有潛在的指標問題!</p>
<p>此種情況應宣告 explicit copy constructor 來解決此問題.如:</p>
<pre>class string {
public:
	string(const char *);
	string(const string&amp;);
};</pre>
<p>一般 copy 會有四種情況, class 不展現出 &quot;bitwise copy semantics&quot;(即上述狀況):</p>
<ol>
  <li>1.class 內含一個 member object 而後者有 copy constructor 時.</li>
  <li>2.class衍生自一個 base class, 而 base class 有 copy constructor 時.</li>
  <li>3.class宣告一個或多個 virtual functions.</li>
  <li>4.當 class 衍生自一個繼承串鏈,其中有一個或多個 virtual base classes.</li>
</ol>
<p>第三種情況需考慮到之前第一章所提到的 virtual table.</p>
<p>因此若父類別有 animate() 和 draw()這兩個 virtual function,而子類別增加 dance(), 則不能</p>
<pre>childclass cc1;
parentclass parent1=cc1;</pre>
<p>會造成 virtual table 被切掉,因為 parent1 的 virtual table 根本就沒有 dance.若 parent1宣告為參考或指標時,被 
  compiler 合成出來的 copy constructor 會把隱含的 vptr 指向 childclass 的 virtual table, 而非 
  bitwise copy.這個也叫做 upcasting. </p>
<p>第四種情況則請回想一下 virtual base classes, 因為只會有一個實體存在,因此,compiler 會特別審慎考量,不會 bitwise 
  copy.</p>
<p>NRV最佳化,如下情況會被 compiler </p>
<pre>x bar() {
	X xx;
	//....
	return xx;
}</pre>
<p>轉為如下的情況</p>
<pre>void bar(X&amp; __result) {
	__result.X::X();
	//....
	return;
}</pre>
<p>&nbsp;</p>
<h3>NRV<a name="nrv"></a></h3>
<p>NRV -&gt; Named Return Value 提供重要效率改善.</p>
<p>&nbsp;</p>
<h3>使用 member initialization list.<a name="initializationlist"></a></h3>
<p>時機:</p>
<ol>
  <li>當初始化一個 reference member</li>
  <li>當初始化一個 const member 時</li>
  <li>當喚起一個 base class 的 constructor, 而他擁有一個參數時.</li>
  <li>當喚起一個 member class 的 constructor, 而他擁有一個參數時.</li>
</ol>
<p>Why:</p>
<p>一般這樣寫, compiler 會先產生一暫存物件, 再 assign 給 member object, 所以效率不彰.</p>
<pre>class Word {
	String _name;
	int _cnt;
public:
	Word(){
		_name=0;
		_cnt=0;
	}
};</pre>
<p>所以要用 Word::Word:_name(0) {_cnt=0;}; 效率較佳.</p>
<p>缺點及注意事項:</p>
<p>注意 initialization list 的初始順序. 若 member object 依賴性太高,最好不要放到 initialization list 
  中,而應移至 constructor 中.</p>
		
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553800.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553800.html</guid>
	<category>C/C++</category>
	<pubDate>Thu, 06 Sep 2001 21:54:01 +0800</pubDate>
</item>
</channel>
</rss>