<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Vietnamese Developers' Blog &#187; C/C++</title>
	<atom:link href="http://openandfree.wordpress.com/category/programming/cc/feed/" rel="self" type="application/rss+xml" />
	<link>http://openandfree.wordpress.com</link>
	<description>There are 10 types of people in the world: Those who know the binary system and those who don't</description>
	<lastBuildDate>Sat, 22 Nov 2008 00:47:09 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='openandfree.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/caa4770bbad32f97d95cfd98b414dfc1?s=96&#038;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>Vietnamese Developers' Blog &#187; C/C++</title>
		<link>http://openandfree.wordpress.com</link>
	</image>
			<item>
		<title>Linked List (1) &#8211; Basic</title>
		<link>http://openandfree.wordpress.com/2008/07/26/linked-list-1-basic/</link>
		<comments>http://openandfree.wordpress.com/2008/07/26/linked-list-1-basic/#comments</comments>
		<pubDate>Fri, 25 Jul 2008 18:39:19 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=51</guid>
		<description><![CDATA[Lấy từ tài liệu tuyệt vời này về linked list của trường Stanford mà tôi muốn khái lược lại bằng những đoạn mã trong đó. Danh sách liên kết (linked list) là  một kiến thức rất cơ bản của cấu trúc dữ liệu và giải thuật. Hiểu được những ưu và khuyết điểm của [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=69&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="text-align:justify;">Lấy từ tài liệu tuyệt vời <a href="http://cslibrary.stanford.edu/103/LinkedListBasics.pdf">này</a> về linked list của trường Stanford mà tôi muốn khái lược lại bằng những đoạn mã trong đó. Danh sách liên kết (linked list) là  một kiến thức rất cơ bản của cấu trúc dữ liệu và giải thuật. Hiểu được những ưu và khuyết điểm của nó sẽ giúp chúng ta hiểu các vấn đề về thời gian, không gian bộ nhớ và cấu trúc dữ liệu nói chung. Hơn thế nữa học về linked list là một cách rất tốt để hiểu về pointer. Những bài toán về linked list là sự kết hợp rất &#8220;đẹp&#8221; giữa giải thuật (algorithms) và các phép toán với con trỏ (pointer manipulation). Linked list là cách các lập trình viên luyện tập để thực sự hiểu về pointer và bạn sẽ thấy ngôn ngữ C &#8220;đẹp&#8221; vô cùng <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Hãy tham khảo thêm tài liệu của Stanford để hiểu kỹ thêm (Highly recommend!)</p>
<p><!--TOC--><br />
<span id="more-69"></span></p>
<h1>Linked list basic</h1>
<h2>Khai báo linked list:</h2>
<pre>struct node {
     int data;
     struct node* next;
};
struct node* head;</pre>
<h2>Hàm length() lấy số phần tử của danh sách</h2>
<pre>
/*
 Given a linked list head pointer, compute
 and return the number of nodes in the list.
*/
int Length(struct node* head) {
    struct node* current = head;
    int count = 0;
    while (current != NULL) {
        count++;
        current = current-&gt;next;
    }
    return count;
}
</pre>
<h2>Duyệt danh sách sử dụng một con trỏ local</h2>
<pre>
struct node* current = head;
while (current != NULL) {
    // do something with *current node
    current = current-&gt;next;
}
</pre>
<h1>Linked List Building</h1>
<h2>3 bước để thêm một node vào đầu linked list</h2>
<li>Cấp phát vùng nhớ cho node mới</li>
<pre>
struct node* newNode;
newNode = malloc(sizeof(struct node));
newNode-&gt;data = data_client_wants_stored;
</pre>
<li>Kết nối trường next của node mới vào đầu danh sách</li>
<pre>
newNode-&gt;next = head;</pre>
<li>Đưa con trỏ head trỏ vào node mới</li>
<pre>
head = newNode;</pre>
<h2>Hàm Push()</h2>
<p style="text-align:justify;">Có vẻ từ 3 bước trên thì việc viết hàm Push() để thêm một node vào đầu danh sách rất dễ dàng. Hãy xem hàm sau và tìm chỗ sai trước khi xem lời giải.</p>
<pre>
void WrongPush(struct node* head, int data) {
     struct node* newNode = malloc(sizeof(struct node));
     newNode-&gt;data = data;
     newNode-&gt;next = head;
     head = newNode;
}
</pre>
<p style="text-align:justify;">Thực ra sai ở dòng</p>
<pre>head = newNode; // NO this line does not work!</pre>
<p style="text-align:justify;">Hãy sửa lại hàm Push() trước khi xem tiếp.</p>
<pre>
/*
Takes a list and a data value.
Creates a new link with the given data and pushes
it onto the front of the list.
The list is not passed in by its head pointer.
Instead the list is passed in as a "reference" pointer
to the head pointer -- this allows us
to modify the caller's memory.
*/
void Push(struct node** headRef, int data) {
     struct node* newNode = malloc(sizeof(struct node));
     newNode-&gt;data = data;
     newNode-&gt;next = *headRef; // The '*' to dereferences back to the real head
     *headRef = newNode;
}
</pre>
<p style="text-align:justify;">Với C++ thì có hỗ trợ truyền tham chiếu (reference &amp;) vào hàm, nên hàm Push có thể viết như sau với C++</p>
<pre>
/*
 Push() in C++ -- we just add a '&amp;' to the right hand
 side of the head parameter type, and the compiler makes
 that parameter work by reference. So this code changes
 the caller's memory, but no extra uses of '*' are necessary --
 we just access "head" directly, and the compiler makes that
 change reference back to the caller.
*/
void Push(struct node*&amp; head, int data) {
     struct node* newNode = malloc(sizeof(struct node));
     newNode-&gt;data = data;
     newNode-&gt;next = head; // No extra use of * necessary on head -- the compiler
     head = newNode; // just takes care of it behind the scenes.
}
</pre>
<h1>Techniques</h1>
<h2>Duyệt xuống (Iterate down)</h2>
<pre>
struct node* current = head;
while (current != NULL) {
     /* Do sth here */
     current = current-&gt;next;
}
</pre>
<p style="text-align:justify;">Có thể thay vòng lặp bằng</p>
<pre>
for (current = head; current != NULL; current = current-&gt;next) {</pre>
<h2>Đổi một pointer bằng một tham chiếu tới pointer (reference pointer)</h2>
<p style="text-align:justify;">Nhiều hàm cần phải thay đổi con trỏ head mặc dù nó được truyền vào hàm số theo kiểu pass-by-value. Để làm điều này trong C thì hãy truyền một pointer đến head pointer thay vì chỉ truyền head pointer. Kiểu pointer chỉ đến một pointer khác thường được gọi là reference pointer.</p>
<p style="text-align:justify;">Hãy xem hàm ChangeToNull để cho con trỏ head trỏ vào NULL</p>
<pre>
// Change the passed in head pointer to be NULL
// Uses a reference pointer to access the caller's memory
void ChangeToNull(struct node** headRef) {
     // Takes a pointer to the value of interest
     *headRef = NULL; // use '*' to access the value of interest
}</pre>
<h2>Build list sử dụng thêm node vào đầu bằng hàm Push</h2>
<p style="text-align:justify;">Phương pháp này làm cho đoạn mã xây dựng list rất ngắn và chạy rất nhanh</p>
<pre>
struct node* AddAtHead() {
	struct node* head = NULL;
	int i;
	for (i=1; i&lt;6; i++) {
		Push(&amp;head, i);
	}
	// head == {5, 4, 3, 2, 1};
	return(head);
}</pre>
<h2>Build list sử dụng con trỏ tail</h2>
<p style="text-align:justify;">Cũng có thể thêm một node mới vào cuối cùng của list. Chúng ta sẽ cần một con trỏ tail trỏ vào node cuối cùng của list và trường next của nó sẽ trỏ vào null. Khi thêm một node mới vào </p>
<pre>
newNode-&gt;next = NULL;
tail-&gt;next = newNode;
tail = tail-&gt;next;</pre>
<p style="text-align:justify;">Tuy nhiên thì có một trường hợp đặc biệt khi phần tử mới thêm vào danh sách là phần tử đầu tiên của list. Hãy xem xét tiếp các kỹ thuật sau.</p>
<h2>Build list sử dụng con trỏ tail có xử lý trường hợp đặc biệt</h2>
<p style="text-align:justify;">Khi build một danh sách bằng cách thêm một node mới vào cuối danh sách thì điểm khó là node đầu tiên phải được thêm vào head pointer và các node khác thì được thêm vào sau node cuối cùng sử dụng tail pointer. Cách đơn giản nhất để xử lý cả điều này là viết hai đoạn code riêng rẽ xử lý chúng. Đầu tiên là đoạn mã thêm vào head pointer cho phần tử đầu tiên và tiếp đó là một vòng lặp sử dụng tail pointer để thêm tất cả các node khác. Trông nó có vẻ &#8220;xấu&#8221; nhưng đơn giản và chạy nhanh.</p>
<pre>
struct node* BuildWithSpecialCase() {
	struct node* head = NULL;
	struct node* tail;
	int i;
	// Deal with the head node here, and set the tail pointer
	Push(&amp;head, 1);
	tail = head;

	// Do all the other nodes using 'tail'
	for (i=2; inext), i); // add node at tail-&gt;next
		tail = tail-&gt;next; // advance tail to point to last node
	}
	return(head); // head == {1, 2, 3, 4, 5};
}</pre>
<h2>Build list sử dụng dummy node</h2>
<p style="text-align:justify;">Một giải pháp khác là sử dụng một dummy node tạm thời ở đầu danh sách. Dummy node sẽ đóng vai trò là phần tử đầu tiên và tất cả các node &#8220;thực sự&#8221; sẽ được thêm vào sau dummy node.</p>
<pre>
struct node* BuildWithDummyNode() {
	struct node dummy; // Dummy node is temporarily the first node
	struct node* tail = &dummy; // Start the tail at the dummy.
	// Build the list on dummy.next (aka tail-&gt;next)
	int i;
	dummy.next = NULL;
	for (i=1; inext), i);
		tail = tail-&gt;next;
	}
	// The real result list is now in dummy.next
	// dummy.next == {1, 2, 3, 4, 5};
	return(dummy.next);
}</pre>
<p style="text-align:justify;">Ở ví dụ trên ta thấy dummy node sẽ không ở trong list khi ra khỏi hàm. Nhưng có một vài cách thể hiện linked list luôn giữ lại dummy node trong suốt quá trình sử dụng nó. Với cách tiếp cận đó thì sẽ không bao giờ có một danh sách rỗng (empty list). Thay vì đó thì danh sách luôn có một dummy node ở đầu. Tuy nhiên thì tôi không khuyến khích phương pháp tiếp cận đó. Phương án sử dụng dummy node tạm thời nên được sử dụng hơn.</p>
<h2>Build list sử dụng tham chiếu cục bộ (local references)</h2>
<p style="text-align:justify;">Cuối cùng đây là một giải pháp rất &#8220;mẹo mực&#8221; mà không phải sự dụng dummy node. Đó là sử dụng một local &#8220;reference pointer&#8221; mà luôn trỏ vào pointer cuối cùng của list chứ không phải node cuối cùng.</p>
<pre>
struct node* BuildWithLocalRef() {
	struct node* head = NULL;
	struct node** lastPtrRef= &head; // Start out pointing to the head pointer
	int i;
	for (i=1; inext); // Advance to point to the
							// new last pointer
	}
	// head == {1, 2, 3, 4, 5};
	return(head);
}</pre>
<p style="text-align:justify;">Kỹ thuật này rất ngắn nhưng bên trong vòng lặp thật &#8220;kinh hoàng&#8221;.</p>
<p style="text-align:justify;">Cả hai giải pháp sử dụng temporary-dummy và reference pointer có hơi chút &#8220;không bình thường&#8221; nhưng nó rất tốt để chắc chắn rằng chúng ta hiểu về pointer bởi vì chúng sử dụng pointer theo cách &#8220;không bình thường&#8221;.</p>
<h1>Các đoạn mã ví dụ</h1>
<h2>AppendNode()</h2>
<pre>
struct node* AppendNode(struct node** headRef, int data) {
	struct node* current = *headRef;
	struct node* newNode;
	newNode = malloc(sizeof(struct node));
	newNode-&gt;data = data;
	newNode-&gt;next = NULL;

	// special case for length 0
	if (current == NULL) {
		*headRef = newNode;
	} else {
		// Locate the last node
		while (current-&gt;next != NULL) {
			current = current-&gt;next;
		}
		current-&gt;next = newNode;
	}
}</pre>
<h2>AppendNode() sử dụng Push()</h2>
<pre>
struct node* AppendNode(struct node** headRef, int data) {
	struct node* current = *headRef;
	// special case for the empty list
	if (current == NULL) {
		Push(headRef, data);
	} else {
		// Locate the last node
		while (current-&gt;next != NULL) {
			current = current-&gt;next;
		}
		// Build the node after the last node
		Push(&amp;(current-&gt;next), data);
	}
}
</pre>
<h2>CopyList()</h2>
<pre>
struct node* CopyList(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* newList = NULL; // head of the new list
	struct node* tail = NULL; // kept pointing to the last node in the new list
	while (current != NULL) {
		if (newList == NULL) { // special case for the first new node
			newList = malloc(sizeof(struct node));
			newList-&gt;data = current-&gt;data;
			newList-&gt;next = NULL;
			tail = newList;
		} else {
			tail-&gt;next = malloc(sizeof(struct node));
			tail = tail-&gt;next;
			tail-&gt;data = current-&gt;data;
			tail-&gt;next = NULL;
		}
		current = current-&gt;next;
	}
	return(newList);
}</pre>
<h2>CopyList() with Push()</h2>
<pre>
// Variant of CopyList() that uses Push()
struct node* CopyList2(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* newList = NULL; // head of the new list
	struct node* tail = NULL; // kept pointing to the last node in the new list
	while (current != NULL) {
		if (newList == NULL) { // special case for the first new node
			Push(&amp;newList, current-&gt;data);
			tail = newList;
		} else {
			Push(&amp;(tail-&gt;next), current-&gt;data); // add each node at the tail
			tail = tail-&gt;next; // advance the tail to the new last node
		}
		current = current-&gt;next;
	}
	return(newList);
}</pre>
<h2>CopyList() With Dummy Node</h2>
<pre>
// Dummy node variant
struct node* CopyList(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* tail; // kept pointing to the last node in the new list
	struct node dummy; // build the new list off this dummy node
	dummy.next = NULL;
	tail = &dummy; // start the tail pointing at the dummy
	while (current != NULL) {
		Push(&amp;(tail-&gt;next), current-&gt;data); // add each node at the tail
		tail = tail-&gt;next; // advance the tail to the new last node
		current = current-&gt;next;
	}
	return(dummy.next);
}</pre>
<h2>CopyList() With Local References</h2>
<pre>
// Local reference variant
struct node* CopyList(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* newList = NULL;
	struct node** lastPtr;
	lastPtr = &newList; // start off pointing to the head itself
	while (current != NULL) {
		Push(lastPtr, current-&gt;data); // add each node at the lastPtr
		lastPtr = &amp;((*lastPtr)-&gt;next); // advance lastPtr
		current = current-&gt;next;
	}
	return(newList);
}</pre>
<h2>CopyList() Recursive</h2>
<pre>
// Recursive variant
struct node* CopyList(struct node* head) {
	if (head == NULL) return NULL;
	else {
		struct node* newList = malloc(sizeof(struct node)); // make the one node
		newList-&gt;data = current-&gt;data;
		newList-&gt;next = CopyList(current-&gt;next); // recur for the rest
		return(newList);
	}
}</pre>
<h1>Other implementations</h1>
<p style="text-align:justify;">Hãy tham khảo thêm trong tài liệu</p>
<li>Dummy header</li>
<li>Circular</li>
<li>Tail pointer</li>
<li>Head struct</li>
<li>Doubly-linked</li>
<li>Chunk list</li>
<li>Dynamic array</li>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/69/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/69/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/69/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=69&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/07/26/linked-list-1-basic/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>
	</item>
		<item>
		<title>StringTokenizer</title>
		<link>http://openandfree.wordpress.com/2008/07/21/stringtokenizer/</link>
		<comments>http://openandfree.wordpress.com/2008/07/21/stringtokenizer/#comments</comments>
		<pubDate>Mon, 21 Jul 2008 02:47:14 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=49</guid>
		<description><![CDATA[StringTokenizer là một class thường được viết trong các dự án. Đó là một class dùng để xử lý chuỗi, chia tách chuỗi thành các chuỗi con (token) được phân cách bởi các ký tự định nghĩa trước (delimiter). Ví dụ:
&#8220;Hoang Tran:1982:Hanoi University of Technology&#8221; được phân cách bởi ký tự &#8220;:&#8221; để thu được [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=65&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="text-align:justify;">StringTokenizer là một class thường được viết trong các dự án. Đó là một class dùng để xử lý chuỗi, chia tách chuỗi thành các chuỗi con (token) được phân cách bởi các ký tự định nghĩa trước (delimiter). Ví dụ:</p>
<p style="text-align:justify;">&#8220;Hoang Tran:1982:Hanoi University of Technology&#8221; được phân cách bởi ký tự &#8220;:&#8221; để thu được thông tin tương ứng về tên, năm sinh và trường học.</p>
<p style="text-align:justify;">Có rất nhiều cách thể hiện lớp StringTokenizer này ở các ngôn ngữ khác nhau (Java thì đã có trong JDK của nó rồi) Bạn có thể google để tìm tham khảo các cách viết đó. Dựa trên ý tưởng của java, chúng ta sẽ xây dựng một lớp StringTokenizer hữu dụng trong C++.</p>
<p style="text-align:justify;">Trong nhiều cách viết lớp này ở trên mạng thì mình cũng nhận ra rằng cách viết ở dự án cũ là tối ưu, hay và dễ hiểu nhất <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Sử dụng deque làm storage data cho lớp StringTokenizer sẽ làm cho quá trình insert cũng như delete các beginning và end của container rất nhanh.</p>
<p style="text-align:justify;">Đây là lớp StringTokenizer đã được thêm bớt chút ít sau khi đi chôm <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<p><span id="more-65"></span></p>
<pre>
class StringTokenizer
{
  public:
    typedef std::deque::iterator iterator;

    StringTokenizer( std::string str, std::string delim = " \t\r\f\n", bool split = false )
    {
      std::string::size_type pos;
      while (( pos = str.find_first_of( delim ) ) != std::string::npos ) {
        if ( split &amp;&amp; pos == 0 ) {
          _tokens.push_back( std::string() );
        } else if ( pos &gt; 0 ) {
          _tokens.push_back( str.substr( 0, pos ) );
        }
        ++pos;

        /* Skip consecutive delimiter characters when tokenising. */

        if (( ! split ) &amp;&amp; ( ! str.empty() )) {
          for( ; delim.find( str[pos] ) != std::string::npos; ++pos ) {
            /* Empty loop. */
          }
        }
        str = str.substr( pos );
      }

      if (( split ) || ( ! str.empty() )) {
        _tokens.push_back( str );
      }
    }

    StringTokenizer( const StringTokenizer &amp;rhs )
    {
      *this = rhs;
    }

    virtual ~StringTokenizer()
    {
    }

    iterator begin()
    {
      return _tokens.begin();
    }

    iterator end()
    {
      return _tokens.end();
    }

    StringTokenizer&amp; operator=( const StringTokenizer &amp;rhs )
    {
      if ( &amp;rhs != this ) {
        _tokens = rhs._tokens;
      }
      return *this;
    }

    const std::string&amp; operator [] ( int index ) {
      return _tokens[ index ];
    }

    int size() const
    {
      return _tokens.size();
    }
    bool empty() const
    {
      return size() == 0;
    }
    bool has_more_tokens()
    {
      return (_tokens.size() &gt; 0);
    }

    std::string next_token()
    {
      std::string tok = _tokens.front();
      _tokens.pop_front();
      return tok;
    }

  private:
    std::deque _tokens;

};
</pre>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/65/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/65/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/65/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=65&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/07/21/stringtokenizer/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>
	</item>
		<item>
		<title>[Basic] Pointer</title>
		<link>http://openandfree.wordpress.com/2008/04/02/basic-pointer/</link>
		<comments>http://openandfree.wordpress.com/2008/04/02/basic-pointer/#comments</comments>
		<pubDate>Wed, 02 Apr 2008 15:47:07 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=46</guid>
		<description><![CDATA[Phần này sẽ khái lược rất nhanh một vài điểm về các quy tắc sử dụng con trỏ. Để tham khảo toàn diện về con trỏ và bộ nhớ hãy xem tài liệu Pointers and Memory http://cslibrary.stanford.edu/102/).
-         Pointer/Pointee: Một con trỏ “pointer” sẽ lưu một reference đến [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=51&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Phần này sẽ khái lược rất nhanh một vài điểm về các quy tắc sử dụng con trỏ. Để tham khảo toàn diện về con trỏ và bộ nhớ hãy xem tài liệu Pointers and Memory <a href="http://cslibrary.stanford.edu/102/">http://cslibrary.stanford.edu/102/</a>).</span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Pointer/Pointee: Một con trỏ “pointer” sẽ lưu một reference đến một biến khác được biết như là pointee của nó. Con trỏ có thể được thiết lập giá trị NULL có nghĩa là nó refer đến một pointee nào. (Trong C và C++, giá trị NULL có thể được sử dụng như là giá trị boolean false).</span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Dereference: Toán tử dereference trên con trỏ cho phép truy nhập vào pointee của nó. Một pointer chỉ có thể bị dereference sau khi nó được thiết lập trỏ đến một pointee cụ thể. Một pointer mà không có pointee thì là bad pointer và không thể bị dereference. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Bad pointer: Một pointer mà không được trỏ vào một pointee thì là “bad” và không thể dereference. Trong C và C++, việc dereference một bad pointer đôi khi gây xung đột ngay lập tức và làm hỏng bộ nhớ của chương trình đang chạy, gây nên &#8220;không biết đường nào mà lần&#8221;. Kiểu lỗi này rất khó để theo dõi. Trong C và C++, tất cả các pointer bắt đầu bằng bad values (những giá trị ngẫu nhiên), do đó rất dễ tình cờ sử dụng bad pointer. Những đoạn mã đúng sẽ thiết lập mỗi pointer có một good value trước khi sử dụng chúng. Chính vì vậy sử dụng bad pointer là một lỗi rất phổ biến trong C/C++. Với Java và các ngôn ngữ khác, các pointers được tự động bắt đầu với giá trị NULL, do đó quá trình dereference sẽ được dễ dàng detect nên các chương trình Java dễ gỡ lỗi này hơn nhiều. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Pointer assignment: Một phép gán giữa hai con trỏ như p = q; sẽ làm cho hai pointer trỏ vào cùng một pointee. Nó sẽ không copy vùng nhớ của pointee. Sau phép gán thì cả hai pointer sẽ chỉ vào cùng một vùng nhớ của pointee. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">malloc(): malloc() là một hàm hệ thống mà cấp pháp một vùng nhớ trong “heap” và trả về con trỏ tới vùng nhớ mới đó. Prototype của malloc() và các hàm khác ở trong stdlib.h. Tham số của malloc() là một số nguyên là kích thước của vùng nhớ cần cấp phát tính theo bytes. Không giống như các biến cục bộ (“stack”), vùng nhớ heap không tự động giải phóng khi hàm tạo thoát ra. malloc() sẽ trả về NULL nếu nó không thế đáp ứng được yêu cầu cấp phát. Bạn nên kiểm tra trường hợp NULL với assert() nếu bạn mong nó an toàn. Hầu hết các hệ điều hành tiên tiến sẽ ném ra một exception hoặc làm việc bắt lỗi tự động trong việc cấp phát bộ nhớ của chúng, do đó không nhất thiết là trong đoạn mã của bạn phải kiểm tra việc cấp phát bộ nhớ thất bại. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">free(): free() thì ngược với malloc(). Gọi hàm free() trên vùng nhớ trên heap để chỉ ra rằng hệ thống đã thực hiện xong và giải phóng vùng nhớ đó. Tham số của free là một con trỏ tới vùng nhớ trên heap – con trỏ mà chúng ta đã có được thông qua lời gọi tới hàm malloc().</span></p>
<p class="MsoNormal" style="text-align:center;"><span style="text-align:center; display: block;"><a href="http://openandfree.wordpress.com/2008/04/02/basic-pointer/"><img src="http://img.youtube.com/vi/f-pJlnpkLp0/2.jpg" alt="" /></a></span></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/51/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/51/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/51/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=51&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/04/02/basic-pointer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>

		<media:content url="http://img.youtube.com/vi/f-pJlnpkLp0/2.jpg" medium="image" />
	</item>
		<item>
		<title>Các kiểu khởi tạo biến</title>
		<link>http://openandfree.wordpress.com/2008/02/23/cac-ki%e1%bb%83u-kh%e1%bb%9fi-t%e1%ba%a1o-bi%e1%ba%bfn/</link>
		<comments>http://openandfree.wordpress.com/2008/02/23/cac-ki%e1%bb%83u-kh%e1%bb%9fi-t%e1%ba%a1o-bi%e1%ba%bfn/#comments</comments>
		<pubDate>Sat, 23 Feb 2008 07:48:43 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[constructor]]></category>
		<category><![CDATA[initialization]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=44</guid>
		<description><![CDATA[Một bài viết nhỏ để warm-up cái blog sau những ngày đông giá lạnh  
Nguồn: http://www.gotw.ca/gotw/001.htm
Câu hỏi: Cho biết sự khác nhau của những dòng khởi tạo biến sau đây

SomeType t;
SomeType t();
SomeType t( u );
SomeType t = u;
Trả lời:

SomeType t;
Biến t được khởi tạo bởi default constructor SomeType::SomeType()

SomeType t();
Một dòng lệnh dễ gây nhầm [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=49&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p align="justify">Một bài viết nhỏ để warm-up cái blog sau những ngày đông giá lạnh <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<p align="justify">Nguồn: <a href="http://www.gotw.ca/gotw/001.htm">http://www.gotw.ca/gotw/001.htm</a></p>
<p align="justify"><strong>Câu hỏi</strong>: Cho biết sự khác nhau của những dòng khởi tạo biến sau đây</p>
<pre>
SomeType t;
SomeType t();
SomeType t( u );
SomeType t = u;</pre>
<p align="justify"><strong>Trả lời</strong>:</p>
<pre>
SomeType t;</pre>
<p align="justify">Biến t được khởi tạo bởi default constructor SomeType::SomeType()</p>
<pre>
SomeType t();</pre>
<p align="justify">Một dòng lệnh dễ gây nhầm lẫn! Thực ra đây là dòng khai báo một HÀM không có tham số và trả về một giá trị kiểu SomeType.</p>
<pre>
SomeType t( u );</pre>
<p align="justify">Đây là dòng lệnh khởi tạo biến trực tiếp (direct initialization). Biến t được khởi tạo nhờ constructor SomeType::SomeType( u );</p>
<pre>
SomeType t = u;</pre>
<p align="justify">Nhiều người nhầm lẫn rằng lệnh này gọi đến toán tử gán (assignment operator). Thực ra đây là một khởi tạo sao chép, trong đó t được khởi tạo nhờ copy constructor của lớp SomeType. Nếu u không thuộc kiểu SomeType thì constructor SomeType::SomeType( u ) sẽ được gọi để tạo ra một đối tượng tạm thời kiểu SomeType từ u, sau đó đối tượng này sẽ được sao chép sang t bởi copy constructor.</p>
<p><span id="more-49"></span></p>
<p align="justify">Chúng ta có thể dễ dàng kiểm chứng sự khác nhau nói trên nhờ đoạn mã sau đây:</p>
<pre>
#include 

class SomeType
{
  pubic:
    //Default constructor
    SomeType() {
      std::cout  &lt;&lt; “Default constructor called” &lt;&lt; std::endl;
    }

    //Another constructor
    SomeType( AnotherType u ) {
      std::cout &lt;&lt; “Another constructor called” &lt;&lt; std::endl;
    }

    //Copy constructor
    SomeType( const SomeType &amp;rhs ) {
      std::cout &lt;&lt; “Copy constructor called” &lt;&lt; std::endl;
    }

    //Assignment operator
    SomeType&amp; operator=( const SomeType &amp;rhs ) {
      std::cout &lt;&lt; “Assignment operator called” &lt;&lt; std::endl;
    }

};

int main()
{
  SomeType t;
 //SomeType t();
 //SomeType t( u );
 //SomeType t = u;
 return EXIT_SUCCESS;
}</pre>
<p align="justify"><strong>Chú ý</strong>: Một số dự án khuyến khích nhân viên sử dụng kiểu khởi tạo SomeType t = u để tránh nhầm lẫn với các khai báo hàm.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/49/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/49/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/49/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=49&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/02/23/cac-ki%e1%bb%83u-kh%e1%bb%9fi-t%e1%ba%a1o-bi%e1%ba%bfn/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
		<item>
		<title>Unix programming with standard I/O (2)</title>
		<link>http://openandfree.wordpress.com/2008/01/22/unix-programming-with-standard-io-2/</link>
		<comments>http://openandfree.wordpress.com/2008/01/22/unix-programming-with-standard-io-2/#comments</comments>
		<pubDate>Tue, 22 Jan 2008 12:46:22 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[cat]]></category>
		<category><![CDATA[standard I/O]]></category>
		<category><![CDATA[system]]></category>
		<category><![CDATA[terminal]]></category>
		<category><![CDATA[unix programming]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=43</guid>
		<description><![CDATA[Phần 2: Chương trình hiển thị nội dung file theo từng trang màn hình
1.	Đặt vấn đề
 Khi xem nội dung một file dài, chúng ta thường muốn nội dung file đó được hiển thị lần lượt theo từng trang màn hình. Hai lệnh phổ biến để xem nội dung file là cat và more không [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=48&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><h3>Phần 2: Chương trình hiển thị nội dung file theo từng trang màn hình</h3>
<p align="justify"><strong>1.	Đặt vấn đề</strong></p>
<p align="justify"> Khi xem nội dung một file dài, chúng ta thường muốn nội dung file đó được hiển thị lần lượt theo từng trang màn hình. Hai lệnh phổ biến để xem nội dung file là cat và more không đáp ứng được nhu cầu này (chúng không có khả năng này, hoặc có nhưng không tiện dụng, “tác giả” cũng không biết rõ về tất cả các khả năng của hai lệnh này). Bởi vậy, chúng ta sẽ phát triển một chương trình tên là p làm nhiệm vụ in ra nội dung một file theo từng trang màn hình (screenful-at-a-time). Chương trình sẽ đợi người dùng ấn một phím để chuyển sang hiển thị trang tiếp theo. Giống như vis, p nhận dữ liệu vào từ cả file lẫn standard input. Ví dụ:</p>
<p align="justify">p nhận dữ liệu vào từ file</p>
<pre>
$ p vis.c</pre>
<p align="justify">p nhận dữ liệu vào từ standard input</p>
<pre>
$ grep  ‘#define’  *.[ch]  |  p</pre>
<p align="justify">Ở phiên bản đầu tiên, p sẽ hiển thị nội dung file theo từng khối, mỗi khối 22 dòng (phần lớn các terminal gồm 24 dòng văn bản). Một cách đơn giản để nhắc người dùng ấn một phím để tiếp tục là không in ra kí tự new line nằm cuối dòng thứ 22. Khi đó, con trỏ sẽ nằm ở cuối dòng thứ 22 thay vì đầu dòng thứ 23. Khi người dùng ấn phím enter, kí tự new line còn thiếu của dòng 22 sẽ được thêm vào, nhờ đó dòng tiếp theo (dòng 23) sẽ được in ra ở đúng vị trí của nó. Nếu người dùng ấn ctrl-d hay q thay vì enter, p sẽ kết thúc. Chúng ta sẽ không quan tâm đến các dòng quá dài. Ngoài ra, khi hiển thị nhiều file cùng lúc thì nội dung các file sẽ được in ra liên tục mà không có sự phân cách gì cả. Tức là, đầu ra của hai lệnh sau đây là như nhau</p>
<pre>$ p file1 file2 …</pre>
<p align="justify">và</p>
<pre>$ cat file1 file2 …  |  p</pre>
<p align="justify">Chú ý rằng lệnh cat in ra nội dung các file một cách liên tục mà không có sự phân cách gì cả. Nếu muốn nội dung các file được phân cách bởi tên file, chúng ta có thể dùng vòng lặp sau đây (ôn lại lập trình shell luôn <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> )</p>
<pre>$ for i in filenames
&gt; do
&gt;      echo $i:
&gt;      cat $i
&gt; done  |  p</pre>
<p align="justify">Có rất nhiều tính năng có thể được đưa vào chương trình p. Quan điểm của chúng ta là: <strong>Trước hết tạo ra một phiên bản đơn giản, sau đó dần dần thêm vào các tính năng phức tạp hơn khi cần thiết. Những tính năng được thêm vào phải là những cái mà người dùng thực sự muốn, chứ không phải những cái mà chúng ta nghĩ rằng họ muốn</strong>.</p>
<p align="justify"><strong>2. Phiên bản đầu tiên của p</strong></p>
<p align="justify">Cấu trúc của p cũng tương tự như vis: Hàm main duyệt qua các file đầu vào và gọi một hàm tên là print để xử lí từng file. Dưới đây là hàm main</p>
<p><span id="more-48"></span></p>
<pre>
/* p: print input in chunks (version 1) */

#include 

#define PAGESIZE 22

char *progname; /* program name for error message */

main( int argc, char *argv[ ] )

{

  int i;

  FILE *fp, *efopen( char*, char* );

  void print( FILE*, int );

  progname = argv[ 0 ];

  if( argc == 1 )

    print( stdin, PAGESIZE );

  else

    for( i = 1; i &lt; argc; i++ ) {

      fp = efopen( argv[ i ], “r” );

      print( fp, PAGESIZE );

      fclose( fp );

    }

  exit( 0 );

}</pre>
<p align="justify">Hàm efopen đóng gói các xử lí quen thuộc: Mở một file, nếu không thành công thì in ra thông báo lỗi và dừng chương trình. Thông báo lỗi bao gồm tên chương trình chứa trong một biến external tên là progname được khởi tạo giá trị trong hàm main.</p>
<pre>
/* open file, die if cannot */

FILE *efopen( char *file, char *mode )

{

  FILE *fp;

  extern char *progname;

  if( ( fp = fopen( file, “r” ) ) != NULL )

    return fp;

  fprintf( stderr, “%s: cannot open file %s mode %s\n”, progname, file, mode );

  exit( 1 );

}</pre>
<p align="justify">Có nhiều phương án khác để thiết kế hàm efopen. Cách thứ nhất là khi gặp lỗi mở file, chúng ta in ra thông báo lỗi và trả về con trỏ NULL. Phương án này cho phép người dùng tự quyết định có tiếp tục xử lí hay dừng chương trình. Cách thứ hai là đưa vào một tham số thứ ba cho efopen, chỉ định hàm sẽ dừng chương trình hay trả về NULL trong trường hợp không thể mở file. Trong ngữ cảnh cụ thể của chương trình p, không có lí do gì để chương trình tiếp tục chạy khi không thể mở một file đầu vào. Bởi vậy, thiết kế hiện thời của hàm print là chấp nhận được. Phần xử lí quan trọng nhất của p được thực hiện bởi hàm print.</p>
<pre>
/* print fp in pagesize chunks */

void print( FILE *fp, int pagesize )

{

  char ttyin();

  static int lines = 0; /* Số dòng đã được in ra trên một trang màn hình */

  char buf[ BUFSIZ ];

  while( fgets( buf, sizeof buf, fp ) != NULL ) {

  if( ++lines &lt; pagesize ) {

      fputs( buf, stdout );

    } else {

      buf[ strlen( buf ) – 1 ] = ‘’;

      fputs( buf, stdout );

      fflush( stdout );

      ttyin();

      lines = 0;

    }

  }

}</pre>
<p align="justify">BUFSIZ là một hằng số được định nghĩa trong stdio.h. Hàm fgets( buf, size, fp ) đọc dòng văn bản tiếp theo từ fp vào buf và thêm kí tự ‘’ vào cuối  dòng đó. Dòng được đọc bao gồm cả kí tự new line ở cuối. Ngoài ra, độ dài của nó không được vượt quá size – 1 kí tự. <strong>Thiết kế của hàm fgets có hai điểm chưa tốt. Thứ nhất, nó nên trả về số kí tự vừa được đọc thay vì giá trị buf. Thứ hai, nó không hề đưa ra cảnh báo khi phải xử lí một dòng văn bản dài quá size – 1 kí tự. Mặc dù không có kí tự nào bị mất, chúng ta sẽ phải kiểm tra nội dung của buf để biết thực sự fgets xử lí các dòng quá dài như thế nào!!!</strong> Hàm strlen trả về độ dài của xâu, không tính kí tự ‘’ ở cuối. <strong>Hãy chú ý cách sử dụng strlen để loại bỏ kí tự cuối cùng khỏi một xâu</strong>, trong trường hợp này là kí tự new line. Việc đọc vào phím được ấn được thực hiện bởi hàm ttyin. Hàm ttyin bắt buộc phải đọc dữ liệu từ bàn phím (/dev/tty) chứ không phải từ standard input để đề phòng trường hợp standard input bị định hướng lại thành một file hay một pipeline. Chúng ta thiết kế hàm ttyin sao cho nó trả về kí tự đầu tiên trong chuỗi các kí tự được ấn bởi người dùng, tuy nhiên giá trị này chưa được sử dụng trong phiên bản đầu tiên của p.</p>
<pre>
/* process response from /dev/tty (version 1) */

char ttyin()

{

  char buf[ BUFSIZ ];

  FILE *efopen( char*, char* );

  static FILE *tty = NULL;

  if( tty == NULL )

    tty = efopen( “/dev/tty”, “r” );

  if( ( fgets( buf, BUFSIZ, tty ) == NULL ) || ( buf[ 0 ] == ‘q’ ) )

    exit( 0 );

  else

    return buf[ 0 ];

}</pre>
<p align="justify"><strong>3. Phát triển chương trình</strong></p>
<p align="justify">Phiên bản thứ nhất của p làm duy nhất một việc: In ra nội dung file theo từng trang 22 dòng và đợi phản ứng từ người dùng. Có rất nhiều tính năng mới có thể được thêm vào p mà không đòi hỏi nhiều công sức. Tuy nhiên, thực tế cho thấy những tính năng đó hiếm khi được sử dụng!!! Sự mở rộng đơn giản đầu tiên của p là cho phép người dùng nhập vào số dòng của file sẽ được in ra trên một trang màn hình. Lệnh sau đây</p>
<pre>$ p –n file</pre>
<p align="justify">sẽ in ra nội dung của file theo từng trang, mỗi trang n dòng. Tương tự như chương trình vis, chúng ta thêm đoạn mã sau đây vào hàm main.</p>
<pre>
/* p: print input in chunks (version 2) */

…

progname = argv[ 0 ];

if( ( argc &gt; 1 ) &amp;&amp; ( argv[ 1 ][ 0 ] == ‘-‘ ) ) {

  pagesize = atoi( &amp;argv[ 1 ][ 1 ] );

  argc--;

  argv++;

}

if( argc == 1 )

…</pre>
<p align="justify">Một tính năng mở rộng nữa của p là tạm thời dừng hiển thị nội dung file để thực thi các lệnh khác. <strong>Giống như trong ed và nhiều chương trình khác của Unix, khi người dùng gõ vào một dòng văn bản bắt đầu bởi dấu chấm than (exclaimation mark) thì phần còn lại của dòng đó sẽ được coi như một câu lệnh và sẽ được chuyển tới shell để thực hiện</strong>. Tính năng này được cài đặt khá đơn giản nhờ hàm system. Chúng ta có phiên bản mới của hàm ttyin như sau.</p>
<pre>
/* process response from /dev/tty (version 2) */

char ttyin()

{

  char buf[ BUFSIZ ];

  FILE *efopen( char*, char* );

  static FILE *tty = NULL;

  if( tty == NULL )

    tty = efopen( “/dev/tty”, “r” );

  for( ; ; ) {

    if( ( fgets( buf, BUFSIZ, fp ) == NULL ) || ( buf[ 0 ] == ‘q’ ) ) {

      exit( 0 );

    } else if( buf[ 0 ] == ‘!’ ) {

      system( buf + 1 ); /* BUG here!!! */

      printf( “!\n” );

    } else {

      return buf[ 0 ];

    }

  }

}</pre>
<p align="justify">Tuy nhiên, phiên bản này của ttyin có mỗi lỗi rất tinh vi. <strong>Các lệnh chạy bởi hàm system kế thừa standard input từ p</strong>. Khi p đọc dữ liệu vào từ một pineline hay một file, do standard input bị định hướng lại,  thì lệnh chạy bởi system cũng sẽ nhận dữ liệu từ chính standard input đó chứ không nhận từ người dùng thông qua terminal nữa. Trong trường hợp dưới đây, khi người dùng nhập vào dòng văn bản !ed, nhẽ ra chương trình ed sẽ được gọi và sẽ chờ người dùng nhập dữ liệu vào từ bàn phím. Tuy nhiên do standard input đã bị định hướng lại sang pinepline nên ed lại đọc dữ liệu từ chính file /etc/passwd và sau đó kết thúc ngay lập tức.</p>
<pre>$ cat /etc/passwd | p -1

root:3D.fHR5KoB.3s:0:1:S.User:/:!ed

?

!</pre>
<p align="justify">Lỗi này sẽ được giải quyết trong phần sau. Ở thời điểm này chúng ta chỉ cần nhớ rằng <strong>sử dụng hàm system có thể gây ra lỗi</strong>.</p>
<p align="justify">Chúng ta vừa phát triển hai chương trình vis và p, có thể coi là các biến thể của lệnh cat. Liệu hai chương trình này có nên được tích hợp vào cat dưới dạng các tham số tùy chọn –v và –p hay không? <strong>Chúng ta thường xuyên phải đứng trước hai sự lựa chọn: Viết một chương trình hoàn toàn mới hay thêm tính năng vào một chương trình cũ? Nguyên tắc cơ bản ở đây là: Mỗi chương trình chỉ nên làm một công việc cụ thể mà thôi</strong>. Một chương trình làm quá nhiều việc sẽ rất cồng kềnh, chạy chậm, khó bảo trì và khó sử dụng. Rất nhiều tính năng không được sử dụng đến bởi người dùng không thể nhớ được chúng!!! Chúng ta không nên nhập vis và cat vào làm một. cat thuần túy sao chép dữ liệu từ “input” sang “output” mà không xử lí gì cả. Trong khi đó, vis thực hiện chuyển đổi (transform) từ “input” sang “output”. Nhập vis và cat làm một sẽ tạo ra một chương trình làm hai việc khác nhau. Chúng ta cũng không nên gộp p và cat làm một. cat được dùng để in dữ liệu văn bản nhanh và hiệu quả. Trong khi đó, p được dùng để “duyệt” (browse) dữ liệu văn bản theo cách tiện lợi nhất đối với người dùng. Để ba chương trình cat, vis và p tồn tại riêng rẽ là một quyết định thiết kế hợp lí.</p>
<p align="justify"><strong>4. Một số hướng phát triển chương trình</strong></p>
<p>- p sẽ chạy như thế nào nếu giá trị pagesize nhỏ hơn 0?<br />
- Những tính năng nào có thể được thêm vào p? Bổ sung khả năng tìm kiếm một dòng theo vị trí hay nội dung theo cả hai chiều xuôi (fordward) và ngược (backward). Bổ sung khả năng in ra nội dung file theo từng khối nhỏ hơn pagesize.<br />
- Sử dụng hàm exec để sửa lỗi gây ra bởi hàm system.<br />
- Phiên bản hiện thời của p sẽ “im lặng” đợi người dùng nhập dữ liệu vào từ bàn phím. Sử dụng hàm isatty để khắc phục nhược điểm này.</p>
<p align="justify"><strong>Phần tiếp theo: Chương trình lựa chọn tham số PICK và chương trình chấm dứt các tiến trình ZAP</strong></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/48/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/48/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/48/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=48&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/01/22/unix-programming-with-standard-io-2/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
		<item>
		<title>Unix programming with standard I/O (1)</title>
		<link>http://openandfree.wordpress.com/2008/01/17/unix-programming-with-standard-io/</link>
		<comments>http://openandfree.wordpress.com/2008/01/17/unix-programming-with-standard-io/#comments</comments>
		<pubDate>Thu, 17 Jan 2008 10:49:20 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[standard I/O]]></category>
		<category><![CDATA[unix programming]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=42</guid>
		<description><![CDATA[Lời tựa
Dạo này đang thời kì nông nhàn nên quay lại với blog. Gần một tháng nay không có bài viết mới nào, có lẽ do các blogger đều đang bận bịu với những kế hoạch riêng của mình. Dạo này không thích những thứ loằng ngoằng phức tạp nữa, cũng chả thích mình là [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=47&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p align="justify"><strong>Lời tựa</strong></p>
<p align="justify">Dạo này đang thời kì nông nhàn nên quay lại với blog. Gần một tháng nay không có bài viết mới nào, có lẽ do các blogger đều đang bận bịu với những kế hoạch riêng của mình. Dạo này không thích những thứ loằng ngoằng phức tạp nữa, cũng chả thích mình là guru nữa, đau đầu lắm <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> . Bây giờ chỉ thích những thứ đơn giản nhưng đẹp đẽ. Ngẫm ra làm phần mềm cũng chẳng cần đến những thứ cao siêu hay phức tạp, vấn đề chỉ là kết hợp những thứ thật đơn giản thành một hệ thống lớn làm việc được mà thôi.</p>
<p align="justify">Đấy là lí do mà dạo này “tác giả” quay sang đọc cuốn “The Unix programming environment” của Brian W.Kernighan và Rob Pike. Công nhận hai bác này viết sách cực hay. Cuốn sách không chỉ trình bày các công cụ hay ngôn ngữ mà quan trọng hơn là nêu bật lên được triết lí của Unix: Viết các chương trình đơn giản, mỗi chương trình chỉ làm một nhiệm vụ nhưng có khả năng tương tác tốt với nhau để thực hiện những nhiệm vụ phức tạp hơn.</p>
<p align="justify">“Tác giả” bài viết này, trong thời gian tạm thời chưa bị phân tâm bởi việc fix bug nhàm chán thường nhật, muốn trích ra đây một số phần của cuốn sách, vừa để giải trí, vừa để ôn lại C và Unix, cũng vừa là để học tập tư duy lập trình trên Unix. Đồng thời, việc làm này cũng khôi phục lại phần nào tình cảm của “tác giả” đối với công việc lập trình, vốn đã bị phai nhạt khá nhiều sau một thời gian làm việc trong một dự án chán ngắt và không hề thể hiện được một chút gì gọi là “vẻ đẹp” của lập trình Unix.</p>
<p align="justify">Tác giả cũng khuyến cáo các độc giả (nếu có <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> ) nên tìm đọc bản tiếng Anh của cuốn sách (tác giả cũng không có bản mềm, chỉ có bản cứng), bởi trình độ kĩ thuật và tiếng Anh của tác giả đều chỉ ở mức sơ cấp.</p>
<p align="justify">Loạt bài viết này trích ra từ các chương 6, 7, 8, 9 của cuốn “The Unix programming environment”. Các chương này trình bày quá trình phát triển một số chương trình đơn giản, qua đó làm nổi bật lên các kĩ thuật và các triết lí lập trình trên Unix. Các chương trình này, theo tác giả cuốn sách, đều là các chương trình nhỏ và không có mặt trong 7th Edition của Unix. Nếu hệ điều hành hiện thời của bạn cũng không có chúng, bạn sẽ thấy chúng rất có ích. Trong trường hợp ngược lại, việc so sánh thiết kế của những chương trình sẵn có với những chương trình trong sách cũng mang lại nhiều điều bổ ích.</p>
<p align="justify">Chúng ta cùng bắt đầu với các chương trình nhập xuất dữ liệu (I/O).</p>
<h4 align="justify">Phần 1 Chương trình in ra các kí tự đặc biệt: VIS</h4>
<p align="justify"><strong>1. Standard input and output: vis</strong></p>
<p align="justify">Rất nhiều chương trình đọc dữ liệu từ một đầu vào và ghi dữ liệu ra một đầu ra. Khi đó việc sử dụng các thao tác vào ra (I/O) đối với đầu vào chuẩn (standard input) và đầu ra chuẩn (standard output) là đủ đáp ứng yêu cầu của chương trình.<br />
Chúng ta hãy bắt đầu xây dựng một chương trình đơn giản tên là vis (viết tắt của visible). vis sao chép dữ liệu từ standard input sang standard output, đồng thời hiển thị tất cả các kí tự vốn không thể in được (non-printing character) dưới dạng \nnn, trong đó nnn là giá trị octal của kí tự đó. Ví dụ: Chúng ta chuẩn bị file đầu vào x như sau:</p>
<pre>
$ cat x
abc
def
</pre>
<p align="justify">Sử dụng vis để hiện thị x, kí tự ctrl-a sẽ được hiển thị là 01, kí tự ctrl-b sẽ được hiển thị là 02.</p>
<pre>$ vis x
abc01
def02</pre>
<p align="justify">Chương trình vis sẽ có ích trong việc phát hiện các kí tự lạ được ghi vào file vì một lí do nào đó. Chú ý rằng phiên bản đầu tiên của vis sẽ chỉ nhận dữ liệu vào từ standard input chứ chưa có khả năng đọc file. Khi cần in ra nội dung của nhiều file, chúng ta có thể nhờ đến lệnh cat như sau:</p>
<pre>$ cat file1 file2 file3 … | vis</pre>
<p align="justify">Chương trình vis của chúng ta sẽ có chức năng giống với lệnh sed sau đây:</p>
<pre>$ sed –n l x
abc1
def2</pre>
<p align="justify">Tuy nhiên trong một số trường hợp sed chỉ làm việc được với các file văn bản (text), bởi vậy việc viết mới chương trình vis vẫn là cần thiết.</p>
<p align="justify">Trong phiên bản đầu tiên của vis, chúng ta sử dụng hai hàm getchar và putchar. Hàm getchar đọc vào kí tự tiếp theo từ standard input (mặc định là terminal, cũng có thể là một file hay một pipeline, các chương trình không biết điều đó). Hàm putchar ghi một kí tự ra standard output, mặc định cũng là terminal.</p>
<p align="justify">Dưới đây là phiên bản đầu tiên của vis</p>
<p><span id="more-47"></span></p>
<pre>
/* vis: make funny characters visible (version 1) */
#include
#include 

int main()
{
  int c;
  while( ( c = getchar() ) != EOF ) {

    if( isascii( c ) &amp;&amp;
        ( isprint( c ) || c == ‘\n’ || c == ‘\t’ || c == ‘ ‘ ) )
      putchar( c );
    else
      printf( “\\%03o”, c );
  }
  exit( 0 );
}</pre>
<p align="justify">Chú ý: EOF không phải một byte đọc được từ standard input. Đó là một giá trị được khai báo sao cho khác với tất cả các giá trị có thể chứa trong một byte nhằm đảm bảo sự phân biệt với các giá trị thật. Bởi vậy, biến c cần được khai báo có kiểu int thay vì char để đủ lớn để chứa được giá trị EOF.</p>
<p align="justify">Dịch và chạy chương trình như sau</p>
<pre>
$ gcc vis.c –o vis
$ ./vis
hello world
hello world07
ctrl-d
$</pre>
<p align="justify"><strong>2. Truyền tham số cho trương trình: vis phiên bản 2</strong></p>
<p align="justify">Khi chạy một chương trình C, các tham số dòng lệnh (command-line argument) được truyền vào hàm main thông qua hai tham số argc và argv. argc là số tham số dòng lệnh và argv là danh sách các tham số. argc luôn lớn hơn 0 vì theo quy ước tham số thứ nhất argv[ 0 ] là tên chương trình. Các tham số thực sự có ý nghĩa là argv[ 1 ] đến argv[ argc – 1 ]. Chú ý rằng các thao tác định hướng lại standard input và standard output bởi &gt; và &lt; được thực hiện bởi shell, do đó không được tính là các tham số dòng lệnh.</p>
<p align="justify">Để minh họa việc xử lí tham số dòng lệnh, chúng ta phát triển chương trình vis bằng cách thêm vào một tham số tùy chọn –s (viết tắt của strip). Khi chạy vis –s, các kí tự lạ trong file đầu vào sẽ bị loại bỏ. Tính năng này rất có ích cho việc “làm sạch” các file đến từ các hệ điều hành khác, chẳng hạn các hệ điều hành sử dụng CRLF làm kí tự xuống dòng thay vì kí tự new line. Dưới đây là mã nguồn của phiên bản mới của vis</p>
<pre>
/* vis: make funny characters visible (version 2) */
#include
#include 

int main( int argc, char *argv[ ] )
{
  int c, strip = 0;

  if( ( argc &gt; 1 ) &amp;&amp; ( strcmp( argv[ 1 ], “-s” ) == 0 )
    strip = 1;

  while( ( c = getchar() ) != EOF ) {

    if( isascii( c ) &amp;&amp;
        ( isprint( c ) || c == ‘\n’ || c == ‘\t’ || c == ‘ ‘ ) )
      putchar( c );
    else if( ! strip )
      printf( “\\%03o”, c );
  }
  exit( 0 );
}</pre>
<p align="justify"><strong>3. Truy nhập file: vis phiên bản 3<br />
</strong>Phiên bản 1 và 2 của vis chỉ đọc dữ liệu vào từ standard input. Bây giờ chúng ta sẽ phát triển vis để có thể đọc dữ liệu vào trực tiếp từ các file. Khi chạy lệnh</p>
<pre>
$ vis file1 file2 …</pre>
<p align="justify">vis sẽ đọc vào nội dung của từng file và xử lí thay vì đọc dữ liệu từ standard input. Tất nhiên khi không có tên file nào được truyền vào thì vis vẫn đọc dữ liệu từ standard input. Các hàm thường được sử dụng để xử lí file là: fopen, getc, putc.<br />
Một số điểm cần lưu ý: Khi chạy một chương trình, sẽ có ba file được mở sẵn với các con trỏ file là stdin, stdout và stderr. Chúng có thể được sử dụng ở bất cứ đâu mà một biến kiểu FILE* có thể có mặt. Hàm getchar() tương đương với getc( stdin ) và hàm putchar( c ) tương đương với putc( c, stdout ).<br />
Dưới đây là phiên bản 3 của vis. Nếu có mặt các tham số dòng lệnh là các tên file, chúng sẽ được xử lí lần lượt. Nếu không có mặt tham số dòng lệnh nào, vis vẫn đọc dữ liệu từ standard input.</p>
<pre>
/* vis: make funny characters visible (version 3) */
#include
#include 

int strip = 0;

int main( int argc, char *argv[ ] )
{

  int i;
  FILE *fp;
  void vis( FILE* );

  while( ( argc &gt; 1 ) &amp;&amp; ( argv[1 ][ 0 ] == ‘-‘ ) ) {

    switch( argv[ 1 ][ 1 ] ) {

      case ‘s’: /* -s: strip funny chars */
        strip = 1;
        break;

      default:
        fprintf( stderr, “%s: unknown arg %s\n”, argv[ 0 ], argv[ 1 ] );
        exit( 1 );
    }

    argc--;
    argv++;
  }

  if( argc == 1 )
    vis( stdin );
  else
    for( i = 1; i &lt; argc; i++ ) {

      if( ( fp = fopen( argv[ i ], “r” ) ) == NULL ) {
        fprintf( stderr, “%s: cannot open %s\n”, argv[ 0 ], argv[ i ] );
        exit( 1 );
      } else {
        vis( fp );
        fclose( fp );
      }
    }
  exit( 0 );
}

/* make chars visible in FILE *fp */
void vis( FILE *fp )
{
  int c;

  while( ( c = getc( fp ) ) != EOF ) {

    if( isascii( c ) &amp;&amp;
        ( isprint( c ) || c == ‘\n’ || c == ‘\t’ || c == ‘ ‘ ) )
      putchar( c );
    else if( ! strip )
      printf( “\\%03o”, c );
  }
}</pre>
<p align="justify">Chúng ta dùng một vòng lặp while để xử lí các tham số dòng lệnh với giả thiết rằng các tham số tùy chọn (optional argument, ví dụ -s) xuất hiện trước. Mặc dù phiên bản hiện thời của vis chỉ có một tham số tùy chọn là –s nhưng vòng lặp while ở trên có khả năng xử lí được nhiều tham số tùy chọn. Một số hệ điều hành cung cấp hàm getopt để xử lí các tham số dòng lệnh. Hãy tham khảo hàm này trước khi tự viết.<br />
Một điều cần chú ý là vis sẽ kết thúc khi không thể mở được một trong các file đầu vào. Thiết kế này là chấp nhận được bởi vis chủ yếu được sử dụng một cách tương tác và với chỉ một file đầu vào.</p>
<p align="justify"><strong>4. Một số hướng phát triển của vis</strong></p>
<p align="justify">- Phát triển vis sao cho các kí tự tab, backslash, backspace, formfeed…được in ra dưới dạng biểu diễn của ngôn ngữ C như \t, \\, \b, \f và các kí tự blank và xuống dòng cũng được đánh dấu. So sánh chương trình với lệnh</p>
<pre>
$ sed –n l</pre>
<p align="justify">- Phát triển vis sao cho các dòng quá dài bị cắt đi, chỉ được hiển thị với một độ dài hợp lí.</p>
<p align="justify">- Phát triển vis sao cho khi chạy chương trình với tham số -sn như sau</p>
<pre>
$ vis –sn</pre>
<p align="justify">sẽ chỉ in ra các xâu được tạo thành bởi n hay nhiều hơn các kí tự có thể in được (printable character). Nghĩa là các kí tự không in được và các xâu gồm các kí tự in được nhưng quá ngắn đều bị bỏ qua. Tính năng này rất hữu ích trong việc trích ra các phần văn bản từ các file không phải văn bản (non-text file), ví dụ các chương trình chạy (executable program). Một số hệ điều hành cung cấp chương trình strings thực hiện nhiệm vụ này. Suy nghĩ xem có cần thiết viết một chương trình hoàn toàn mới thay vì sửa đổi vis hay không?</p>
<p align="justify">- Viết một chương trình có tên printable in ra tên các file chỉ chứa các kí tự in được. Tên các file được đưa vào bằng các tham số dòng lệnh. Chương trình này có ích khi cần in nhiều file cùng lúc như sau</p>
<pre>
$ pr `printable *` | lpr</pre>
<p align="justify">Dùng lệnh man để xem chức năng của hai lệnh pr và lpr</p>
<p align="justify"><strong>Phần tiếp theo: Chương trình hiển thị nội dung file theo từng trang màn hình: P</strong></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/47/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/47/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/47/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=47&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/01/17/unix-programming-with-standard-io/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
		<item>
		<title>So sánh C++ và C (1) &#8211; Classes and methods</title>
		<link>http://openandfree.wordpress.com/2007/12/21/so-sanh-c-va-c-1-classes-and-methods/</link>
		<comments>http://openandfree.wordpress.com/2007/12/21/so-sanh-c-va-c-1-classes-and-methods/#comments</comments>
		<pubDate>Fri, 21 Dec 2007 07:26:55 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=40</guid>
		<description><![CDATA[Bài viết này là một trong hai bài viết so sánh giữa C và C++ bằng cách so sánh các đoạn mã trong C++ với đoạn mã tương ứng của nó được viết trong C. So sánh này sẽ cho bạn cái nhìn tốt hơn về sự khác biệt của performance giữa C và C++. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=45&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="text-align:justify;font-size:10pt;font-family:Verdana;">Bài viết này là một trong hai bài viết so sánh giữa C và C++ bằng cách so sánh các đoạn mã trong C++ với đoạn mã tương ứng của nó được viết trong C. So sánh này sẽ cho bạn cái nhìn tốt hơn về sự khác biệt của performance giữa C và C++. </p>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;">Trong bài viết đầu tiên, chúng ta sẽ xem xét ảnh hưởng tới performance của việc thực thi các phương thức trong C++. So sánh này được thực hiện bằng việc so sánh đoạn mã C++ và đoạn mã C tương ứng. </p>
<p><span id="more-45"></span></p>
<pre>
// Example class A contains regular and
// static member variables and methods.
class A
{
private:
  int m_x;
  static int g_y;
  int m_z;

  // Should be invoked when the object ends
  void InformEnd();
public:
  A(int x);
  ~A();

  void UpdateX(int newX);
  static void UpdateY(int newY);
};

// Initialization of the static variable
int A::g_y = 0;

// The non-static member variables
// are initialized in the constructor
A::A(int x)
{
  m_x = x;
  m_z = 0;
}

// Destructor invokes a private variable
A::~A()
{
  InformEnd();
}

// UpdateX checks the value of X against
// a static variable before updating the value
void A::UpdateX(int newX)
{
  if (g_y != 0 &amp;&amp; m_x UpdateX(8);

    // Example of a direct access
    a.UpdateX(9);

    // Example of static method call
    A::UpdateY(1000);

    // Deleting the object
    delete pA;
}
</pre>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;">Đoạn mã C sau đấy sẽ cung cấp một thể hiện tương ứng với đoạn mã C++ phía trên. Class trong C++ được chuyển tương ứng thành struct trong C. </p>
<pre>
/*
This code maps from the C++ code to the equivalent C code.
Mapping of the following entities is covered:
- classes                 - methods
- this pointer            - member variables
- constructors            - static methods
- destructors             - static variables
*/
#include
#include
#define TRUE 1
#define FALSE 0

typedef int BOOLEAN;

/*
Structure A represents the class A. Only the non-static member
variables are present in the structure
*/
struct A
{
  int m_x;
  int m_z;
};

/* Notice that g_y is not a part of struct A. Its a separate global variable. */
int g_y = 0;

/*
Prototype for the InformEnd method. The C++ version of this method
did not have any parameters but the C mapped function needs the this
pointer to obtain the address of the object. Note that all non-static
methods in the C++ code would map to a C function the additional this
pointer as the first parameter.
*/
void InformEnd(A *this_ptr);

/*
The constructor maps to function with the this pointer and the size of the
structure as parameters. this_ptr passed to the constructor is NULL when
the operator new is used to create the object. this_ptr contains a valid
pointer if the memory for the object to be constructed is already
allocated. (e.g. local variable or part of another structure.)
*/

A *A_Constructor(A *this_ptr, int x)
{
  /*Check if memory has been allocated for struct A. */
  if (this_ptr == NULL)  {
    /*Allocate memory of size A. */
    this_ptr = (A *) malloc(sizeof(A));
  }

  /* Once the memory has been allocated for A, initialise members of A. */
  if (this_ptr)  {
    this_ptr-&gt;m_x = x;
    this_ptr-&gt;m_z = 0;
  }

  return this_ptr;
}

/*
The following function is equivalent to a destructor. The this
pointer and a dynamic flag are passed as the two parameters to
this function. The dynamic flag is set to true if the object is
being deleted using the delete operator.
*/

void A_Destructor(A *this_ptr, BOOLEAN dynamic)
{
  InformEnd(this_ptr);

  /* If the memory was dynamically allocated for A, explicitly free it. */
  if (dynamic) {
    free(this_ptr);
  }
}

/*
A pointer this is passed as first argument. All member variables
in the code will be accessed through an indirecion from the this
pointer. Notice that static variables are accessed directly as
they do not belong to any instance.
*/

void A_UpdateX(A *this_ptr, int newX)
{
  if (g_y != 0 &amp;&amp; this_ptr-&gt;m_x m_x = newX;
  }
}

/*
Notice that this is not passed here. This is so because
A_UpdateY is a static function. This function can only access
other static functions and static or global variables. This
function cannot access any member variables or methods of class A
as a static function does not correspond to an instance.
*/

void A_UpdateY(int newY)
{
  g_y = newY;
}

main()
{
    /*
    Dynamically allocate memory by passing NULL in this arguement.
    Also initialize members of struct pointed to by pA.
    */
    A *pA = A_Constructor(NULL, 5);

    /* Define local variable a of type struct A. */
    A a;

    /*
    Initialize members of struct variable a. Note that the
    constructor is called with the address of the object as
    a has been pre-allocated on the stack.
    */
    A_Constructor(&amp;a, 6);

    /*
    Method invocations in C++ are handled by calling the
    corresponding C functions with the object pointer.
    */
    A_UpdateX(pA, 8);
    A_UpdateX(&amp;a, 9);

    /* UpdateY is a static method, so object pointer is not passed */
    A_UpdateY(1000);

    /*
    Delete memory pointed to by pA (explicit delete in
    original code).
    */
    A_Destructor(pA, TRUE);

    /*
    Since memory was allocated on the stack for local struct
    variable a, it will be deallocated when a goes out of scope.
    The destructor will also be invoked. Notice that dynamic flag
    is set to false so that the destructor does not try to
    free memory.
    */
    A_Destructor(&amp;a, FALSE);

}
</pre>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;"><strong>C++ Method Invocation</strong>: Tất cả các phương thức trong C++ được dịch thành các hàm trong C với một tham số bổ sung. Đó có thể là điểm gây khác biệt lớp về performance. Tuy nhiên trong thực tế thì đoạn mã trong C cũng phải truy nhập vào cấu trúc dữ liêu chung thông qua một chỉ số mảng hoặc một cơ chế nào đó.</p>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;"><strong>Khởi tạo đối tượng</strong>: Bất cứ khi nào một đối tượng được khởi tạo, C++ sẽ thực thi hàm khởi tạo (constructor). Đôi khi thì việc đó cũng thêm một chút phí tổn. Phí tổn đó có thể giảm đi bằng cách định nghĩa ham khởi tạo là inline. Tuy nhiên trong hầu hết các trường hợp, hàm khởi tạo thực sự thay thế một hàm mà được sử dụng để khởi tạo cấu trúc dữ liệu trong chương trình C.</p>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;">Nếu chương trình khai báo rất nhiều biến toàn cục, khởi tạo đối tượng có thể tiêu tốn nhiều lúc khởi tạo chương trình. C++ tham gia việc khởi tạo tất cả các đối tượng trước khi hàm main được gọi</p>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;"><strong>Hủy đối tượng</strong> Bạn có thể thấy từ đoạn mã C phía trên, bất cứ khi nào đối tượng ra khỏi scope của nó hoặc là bị delete thì C++ sẽ thực hiện hàm hủy của đối tượng đó. Sự tiêu tốn này có thể giảm đi bằng việc chỉ định nghĩa hàm hủy khi chúng thực sự cần thiết (ví dụ một vài hành động cần thiết khi đối tượng bị hủy). Các hàm hủy inline cũng có thể được sử dụng.</p>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;"><strong>Static access</strong>: Đoạn mã C phía trên chỉ ra rằng các hàm thành viên và biến tĩnh (static) không tích hợp với một đối tượng cụ thể nào cả. Vì vậy chúng có thể truy nhập trực tiếp không thông qua đối tượng. Nó có thể rất hữu ích trong việc định nghĩa một phương thức mà cần hàm  có calling convention của C. Một ví dụ tốt cho các hàm thành viên tĩnh là để thể hiện các hàm interrupt service routines (ISRs). ISR handlers cần phải là các hàm C. C++ static functions có thể trực tiếp sử dụng bởi các ISR handlers.</p>
<p style="text-align:justify;font-size:10pt;font-family:Verdana;">Tham khảo: http://www.eventhelix.com/RealtimeMantra/basics/ComparingCPPAndCPerformance.htm</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/45/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/45/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/45/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/45/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/45/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/45/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/45/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/45/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/45/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/45/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/45/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/45/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=45&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/12/21/so-sanh-c-va-c-1-classes-and-methods/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>
	</item>
		<item>
		<title>nested class, nested function và một ứng dụng thú vị của function object</title>
		<link>http://openandfree.wordpress.com/2007/12/20/nested-class-nested-function-va-m%e1%bb%99t-%e1%bb%a9ng-d%e1%bb%a5ng-thu-v%e1%bb%8b-c%e1%bb%a7a-function-object/</link>
		<comments>http://openandfree.wordpress.com/2007/12/20/nested-class-nested-function-va-m%e1%bb%99t-%e1%bb%a9ng-d%e1%bb%a5ng-thu-v%e1%bb%8b-c%e1%bb%a7a-function-object/#comments</comments>
		<pubDate>Wed, 19 Dec 2007 18:16:39 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[function object]]></category>
		<category><![CDATA[nested class]]></category>
		<category><![CDATA[nested function]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=39</guid>
		<description><![CDATA[Nguồn: http://www.gotw.ca/gotw/058.htm
Bài viết này không trình bày chi tiết về nested class trong C++ mà chỉ tập trung vào các kĩ thuật sử dụng nested class và function object để mô phỏng các nested function, một yếu tố không có trong C++. Các chi tiết về nested class có thể tìm thấy trong nhiều cuốn [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=44&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p align="justify">Nguồn: <a href="http://www.gotw.ca/gotw/058.htm">http://www.gotw.ca/gotw/058.htm</a></p>
<p align="justify">Bài viết này không trình bày chi tiết về nested class trong C++ mà chỉ tập trung vào các kĩ thuật sử dụng nested class và function object để mô phỏng các nested function, một yếu tố không có trong C++. Các chi tiết về nested class có thể tìm thấy trong nhiều cuốn sách C++ khác, ví dụ cuốn Thinking in C++, tập 1.</p>
<p align="justify">Bài viết đưa ra ba câu hỏi và sau đó lần lượt đi tìm các câu trả lời cho chúng. Ba câu hỏi là:</p>
<p align="justify">1- Nested class là gì? Tại sao chúng ta cần các nested class?<br />
2- Local class là gì? Tại sao chúng ta cần các local class?<br />
3- Trong C++ không có khái niệm nested function. Bởi vậy, chúng ta không thể viết một đoạn mã như sau</p>
<pre>int f( int i )
{
  int j = i*2;
  int g( int k )
  {
    return j+k;
  }
  j += 4;
  return g( 3 );
}</pre>
<p align="justify">Hãy đưa ra một giải pháp mô phỏng các hàm f và g sao cho đạt được một “hiệu ứng” tương tự như đoạn mã trên.</p>
<p align="justify"><strong>Trả lời</strong></p>
<p align="justify">C++ có rất nhiều công cụ hữu ích dùng để ẩn thông tin (information hiding) và quản lí sự phụ thuộc mã nguồn (dependency management). Các đoạn mã sau đây có thể chưa hoàn toàn chính xác về mặt cú pháp, chúng được dùng để minh họa cho các kĩ thuật thiết kế mà thôi.</p>
<p align="justify"><strong>1- Nested class là gì? Tại sao chúng ta cần nested class?</strong></p>
<p align="justify">Nested class là một class được “viết” (enclosed) bên trong phạm vi (scope) của một class khác.</p>
<pre>// Ví dụ 1: Nested class
//
  class OuterClass
  {
    class NestedClass
    {
      // ...
    };
    // ...
  };</pre>
<p align="justify">Trong đoạn mã trên, NestedClass là một nested class được “viết” bên trong class OuterClass. Các nested class rất hữu ích cho việc tổ chức mã nguồn, quản lí quyền truy nhập (access) và các phụ thuộc (dependencies). Các nested class tuân theo các quy tắc thông thường về quyền truy nhập giống như các dữ liệu thành phần và các hàm thành phần. Tức là, nếu NestedClass được khai báo là public thì chúng ta có thể sử dụng nó từ bất cứ đâu thông qua tên gọi OuterClass::NestedClass. Ngược lại, nếu NestedClass được khai báo là private thì chỉ có các thành phần và các hàm bạn (friends) của OuterClass là có quyền truy nhập đến nó. Thông thường, các nested class chứa các cài đặt riêng cho OuterClass, do đó thường được khai báo là private.</p>
<p align="justify">Chú ý rằng nested class khác với namespace. Các namespace chỉ thuần túy nhóm các tên lại với nhau chứ không mang lại khả năng quản lí quyền truy nhập. Nếu bạn muốn quản lí quyền truy nhập tới một lớp, một trong các giải pháp là viết nó thành nested class trong một class khác.</p>
<p align="justify"><strong>2- Local class là gì? Tại sao chúng ta cần các local class? </strong></p>
<p align="justify">Local class là một class được định nghĩa bên trong một hàm thông thường hoặc một hàm thành phần (member function). Trong ví dụ sau đây, LocalClass là một local class được định nghĩa bên trong một hàm thông thường có tên là f.</p>
<pre>// Ví dụ 2: Local class
//
  int f()
  {
    class LocalClass
    {
      // ...
    };
    // ...
  };</pre>
<p align="justify">Giống như nested class, local class là một công cụ hữu ích phục vụ việc quản lí những sự phụ thuộc về mã nguồn (code dependencies). Trong ví dụ 2, chỉ có đoạn mã trong thân hàm f mới được phép sử dụng LocalClass. LocalClass thường chứa những cài đặt riêng cho hàm f nên không cần thiết phải có khả năng truy nhập được từ bên ngoài.</p>
<p align="justify">Bạn có thể sử dụng local class gần như trong mọi tình huống có thể sử dụng class thông thường. Một ngoại lệ quan trọng cần ghi nhớ là: Các local class không thể đóng vai trò tham số kiểu (template parameter). Ví dụ dưới đây trích từ tài liệu chuẩn C++:</p>
<pre>A local type, a type with no linkage, an unnamed
type or a type compounded from any of these types
shall not be used as a template-argument for a
template type-parameter. [Example:
    template
    class X { /* ... */ };

    void f()
    {
      struct S { /* ... */ };
      X x3;  // error: local type used as
                //  template-argument
      X x4; // error: pointer to local type
                //  used as template-argument
    }
  -end example]</pre>
<p align="justify">Tóm lại, cả nested class lẫn local class đều là những công cụ hữu ích của C++ dùng để ẩn thông tin và quản lí quyền truy nhập và các phụ thuộc.</p>
<p align="justify"><strong>Nested Funtion</strong></p>
<p align="justify">Một vài ngôn ngữ (không phải C++) cho phép chúng ta viết các nested function. Giống như các nested class, nested function là một function được viết bên trong một function khác. Những đặc điểm quan trọng của nested function là:</p>
<p align="justify">- Các nested function có quyền truy nhập đến các biến cục bộ của hàm chứa nó.<br />
- Các nested function là “cục bộ”, nghĩa là không thể truy nhập tới chúng từ bên ngoài, trừ khi có một con trỏ trỏ đến nested function được cung cấp bởi hàm chứa.</p>
<p align="justify">Nếu như các nested class hữu ích bởi chúng cho phép điều kiển sự “ẩn hiện” (visibility) của một lớp thì các nested function hữu ích bởi chúng cho phép điều khiển sự “ẩn hiện” của một hàm.</p>
<p align="justify"><strong>Trả lời cho câu hỏi 3: Các giải pháp sử dụng class để mô phỏng nested function trong C++</strong></p>
<p align="justify">Chú ý: “mô phỏng” ở đây được hiểu theo nghĩa là: Xây dựng một class g bên trong một hàm f, sao cho f có thể sử dụng g như một nested function</p>
<pre>void f()
{
  class g { … };  g();
}</pre>
<p align="justify">Nói đến một class được sử dụng như một function, chúng ta nghĩ ngay đến các function object. Giải pháp đầu tiên mà hầu hết mọi người sẽ đưa ra là:</p>
<p><span id="more-44"></span></p>
<pre>// Giải pháp 3(a): Một giải pháp thô sử dụng function object
//
//
  int f( int i )
  {
    int j = i*2;
    class g_
    {
      public:
        int operator()( int k )
        {
          return j+k; // ERROR!!!: Không thể sử dụng j bên trong g_
        }
    } g;

    j += 4;

    return g( 3 ); // f sử dụng g như một nested function
  }</pre>
<p align="justify">Ý tưởng ở đây là mô phỏng nested function bởi một local class có tên là g_, sau đó gọi operator() của g_. <strong>(Xem lại bài viết: STL Function Object và các ứng dụng(1) để biết chi tiết về cách viết một function object)</strong>. Đây là một ý tưởng hay nhưng đáng tiếc là đoạn mã trên không chạy được! Lí do là một local class không thể sử dụng các biến của hàm bên ngoài (ở đây là biến j). Một giải pháp cho vấn đề này là truyền các biến của hàm bên ngoài vào trong local class thông qua các thành phần dữ liệu của class đó.</p>
<pre>// Giải pháp 3(b): Sử dụng function object.
// Thành phần dữ liệu của local class là các references trỏ tới
// các biến cục bộ của hàm bên ngoài (phức tạp, khó bảo trì)
//
  int f( int i )
  {
    int j = i*2;
    class g_
    {
      public:
        g_( int &amp;j ) : j_( j ) { }
        int operator()( int k )
        {
          return j_+k; // sử dụng j thông qua reference
        }
      private:
        int &amp;j_;
    } g( j );

    j += 4;

    return g( 3 );
  }</pre>
<p align="justify">Bây giờ thì local class g_ đã có thể sử dụng biến j của hàm bên ngoài thông qua thành phần dữ liệu j_ của nó và hàm f() ở trên đã chạy đúng. Tuy nhiên nhược điểm của giải pháp này là rất khó có thể mở rộng. Trong trường hợp class g_ cần sử dụng một biến khác nữa của hàm f() ngoài biến j (giả sử là biến k kiểu int). Khi đó chúng ta phải thực hiện những thao tác sau đây:</p>
<p align="justify">- Thêm vào class g_ một thành phần dữ liệu int &amp;k_;<br />
- Thêm tham số int k cho constructor của g_;<br />
- Thêm một phép khởi tạo cho k_: k_( k );</p>
<p align="justify">Sô thao tác cần thực hiện sẽ tăng lên rất nhiều nếu có nhiều local class trong f(). Vậy, chúng ta cần tìm ra một giải pháp tốt hơn.</p>
<p align="justify"><strong>Một giải pháp tốt hơn</strong></p>
<p align="justify">Hàm f được cải tiến bằng cách chuyển các biến cục bộ của nó thành các thành phần dữ liệu public của lớp g_</p>
<pre>// Giải pháp 3(c): Một giải pháp tốt hơn
//
  int f( int i )
  {
    class g_
    {
      public:
        int j;
        int operator()( int k )
        {
          return j+k;
        }
    } g;

    g.j = i*2;
    g.j += 4;

    return g( 3 );
  }</pre>
<p align="justify">Bây giờ thì hàm f đã trở nên đẹp đẽ và dễ bảo trì, phát triển. Giải pháp này gợi cho chúng ta suy nghĩ: Tại sao không “mô phỏng” các nested function của f() bởi các hàm thành phần x(), y(), z() của lớp g_? Đoạn mã sau đây hiện thực hóa ý tưởng này. Lớp g_ được đổi tên thành Local_ để mang tính mô tả tốt hơn</p>
<pre>// Giải pháp 3(d): Mô phỏng nested function bằng cách hàm thành phần
//
  int f( int i )
  {

    class Local_
    {
      public:
        int j;
        int g( int k )
        {
          return j+k;
        }
        void x() { /* ... */ }
        void y() { /* ... */ }
        void z() { /* ... */ }
    } local;

    local.j = i*2;
    local.j += 4;
    local.x();
    local.y();
    local.z();

    return local.g( 3 );
  }</pre>
<p align="justify">Giải pháp này không sử dụng function object. Nhược điểm của nó là j không được khởi tạo một cách linh hoạt do lớp Local_ không có constructor.</p>
<p align="justify"><strong>Giải pháp hoàn thiện</strong></p>
<p align="justify">Nếu f không nhất thiết phải là một hàm thông thường mà chỉ cần được sử dụng như một hàm thông thường, chúng ta có thể xây dựng f dưới dạng một function object và xây dựng các nested function dưới dạng các hàm thành phần như sau:</p>
<pre>// Giải pháp 3(e): Một giải pháp hoàn thiện. Dễ bảo trì, phát triển
//
//
  class f
  {
      int retval; // Giá trị trả về của “hàm” f
      int j;

      //Các hàm thành phần của lớp f
      //g(), x(), y(), z() có vai trò tương tự như các nested function
      int g( int k ) { return j + k; };
      void x() { /* ... */ }
      void y() { /* ... */ }
      void z() { /* ... */ }

    public:
      f( int i ) // constructor
        : j( i*2 )
      {
        j += 4;
        x();
        y();
        z();
        retval = g( 3 );
      }
      operator int() // operator() trả về giá trị cho “hàm” f
      {
        return retval;
      }
  };</pre>
<p align="justify">Giải pháp 3(e) là một giải pháp hoàn thiện cho việc mô phỏng nested function g cho một hàm f thông thường. Nếu f không phải là một hàm thông thường mà là một hàm thành phần của một lớp C nào đó thì sao? Khi đó, chúng ta mong muốn mô phỏng được đoạn mã sau đây, trong đó g là một nested function của hàm thành phần f của lớp C (tất nhiên đoạn mã này là không hợp lệ trong C++, vì C++ không cho phép nested function)</p>
<pre>// Đoạn mã cần mô phỏng: Một nested function bên trong một hàm thành phần
// Chú ý: Đoạn mã chỉ có tính minh họa, không hợp lệ trong C++
//
//
  class C
  {
      int data_;
    public:
      /* f là hàm thành phần của lớp C */
      int f( int i )
      {
        // g là một nested function của f
        int g( int i ) { return data_ + i; }
        return g( data_ + i*2 );
      }
  };</pre>
<p align="justify">“Bắt chước” giải pháp 3(e), cộng với một chút điều chỉnh, chúng ta có giải pháp 4(a) sau đây:</p>
<p align="justify">- Xây dựng C_f như một function object và là một nested class bên trong lớp C<br />
- Xây dựng g như một hàm thành phần của C_f<br />
- Hàm thành phần f của C gọi đến C_f</p>
<pre>// Giải pháp 4(a): Giải pháp hoàn thiện, giống 3(e) nhưng áp dụng cho hàm thành phần
//
//
  class C
  {
      int data_;
      friend class C_f;

    public:
      int f( int i );
  };

  class C_f
  {
      C* self;
      int retval;
      int g( int i ) { return self-&gt;data_ + i; }

    public:
      C_f( C* c, int i ) : self( c )
      {
        retval = g( self-&gt;data_ + i*2 );
      }
      operator int() { return retval; }
  };

  int C::f( int i ) { return C_f( this, i ); }</pre>
<p align="justify"><strong>Kết luận</strong></p>
<p align="justify">Cũng giống như những bài viết khác trong cùng loạt bài GotW của Herb Sutter, bài viết này đã đưa ra một yêu cầu thiết kế thú vị, sau đó tiến hành xem xét đánh giá những giải pháp khác nhau để cuối cùng chọn ra một giải pháp tốt nhất. Bài viết cung cấp nhiều kinh nghiệm lập trình C++, cũng như cho thấy một ứng dụng thú vị nữa của các function object.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/44/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/44/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/44/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=44&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/12/20/nested-class-nested-function-va-m%e1%bb%99t-%e1%bb%a9ng-d%e1%bb%a5ng-thu-v%e1%bb%8b-c%e1%bb%a7a-function-object/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
		<item>
		<title>Function Pointer</title>
		<link>http://openandfree.wordpress.com/2007/11/26/function-pointer/</link>
		<comments>http://openandfree.wordpress.com/2007/11/26/function-pointer/#comments</comments>
		<pubDate>Mon, 26 Nov 2007 10:28:03 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[function-pointer]]></category>

		<guid isPermaLink="false">http://openandfree.wordpress.com/2007/11/26/function-pointer/</guid>
		<description><![CDATA[Bài viết về function pointer được trích dịch từ tài liệu này: http://www.newty.de/fpt/index.html

1 Giới thiệu
Function Pointer cung cấp một kỹ thuật lập trình cực kỳ thú vị, hiệu quả và “đầy màu sắc”. Chúng ta có thể sử dụng nó để thay thế câu lệnh switch/if, xây dựng quá trình late-binding hoặc implement hàm callback. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=40&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Bài viết về function pointer được trích dịch từ tài liệu này: http://www.newty.de/fpt/index.html</span></p>
<p><!--TOC--></p>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">1 Giới thiệu</span></strong></h1>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Function Pointer cung cấp một kỹ thuật lập trình cực kỳ thú vị, hiệu quả và “đầy màu sắc”. Chúng ta có thể sử dụng nó để thay thế câu lệnh switch/if, xây dựng quá trình late-binding hoặc implement hàm callback. Tiếc thay, có thể vì sự phức tạp của nó mà nó được đề cập rất ít trong hầu hết sách và tài liệu. Nếu có thì nó chỉ được trình bày một cách rất tóm tắt và sơ sài. Thực ra thì nó ít gây ra lỗi hơn so với pointer bình thường bởi vì chúng ta không bao giờ phải allocate hoặc de-allocate bộ nhớ cả. Tất cả việc chúng ta cần làm là hiểu nó làm gì và học cú pháp của nó. Nhưng hãy luôn tâm niệm rằng: hãy tự hỏi bạn có thực sự cần đến function pointer hay không? Rất tuyệt để thể hiện cách thức late-binding, thế nhưng sử dụng cấu trúc hiện tại của C++ làm cho đoạn mã trở nên dễ đọc và rõ ràng hơn. Một khía cạnh khác của late-binding là runtime: nếu bạn gọi một virtual function, chương trình sẽ xác định hàm nào được gọi. Nó làm điều đó bằng cách sử dụng V-Table mà chứa tất cả những hàm có thể gọi. Điều đó có vẻ hơi lãng phí mỗi lần gọi, và có thể bạn sẽ tiết kiệm một chút nếu sử dụng function pointer thay vì virtual function. Cũng có thể không …</span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">1.1 Function Pointer là gì?</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Function pointer là một pointer mà nó chỉ đến địa chỉ của một hàm. Bạn phải luôn giữ trong đầu rằng một chương trình chạy sẽ chiếm một không gian bộ nhớ xác định trong bộ nhớ chính. Cả đoạn chương trình thực thi đã được dịch từ mã mà bạn viết và các biến sử dụng đều được đưa vào trong không gian bộ nhớ này. Vì vậy một function trong chương trình của bạn không có gì khác hơn là một địa chỉ trong bộ nhớ. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">1.2 Thay thế câu lệnh Switch như thế nào?</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Khi chúng ta muốn gọi một hàm DoIt() ở một label xác định trong chương trình, chúng ta phải để lời gọi tới hàm DoIt() tại label đó. Sau đó biên dịch và mỗi khi chương trình chạy tới label đó thì hàm DoIt() sẽ được gọi. Mọi thứ đều ok, nhưng sẽ làm gì nếu giả sử chúng ta không biết tại thời điểm build-time (thời gian dịch) hàm nào sẽ được gọi? Nghĩa là chỉ đến lúc chạy ta mới biết ở label đó thì nên chạy DoIt() hay một hàm nào khác. Đó chính là lúc chúng ta muốn sử dụng đến callback-function hoặc là sử dụng kỹ thuật lấy ra từ một “pool” chứa các possible function. Tuy nhiên thì chúng ta có thể giải quyết vấn đề này bằng cách sử dụng lệnh switch, và lựa chọn lời gọi đến hàm thích hợp ở những nhánh khác nhau tùy theo giá trị biểu thức của switch. Nhưng vẫn có một cách khác là sử dụng function pointer. Trong ví dụ sau đây chúng ta thực hiện nhiệm vụ của bốn toán tử toán học cơ bản (+, -, *, /). Cách đầu tiên sử dụng switch và cách thứ hai sử dụng function pointer. </span></p>
<p><span id="more-40"></span></p>
<pre>
//------------------------------------------------------------------------------
// 1.2 Introductory Example or How to Replace a Switch-Statement
// Task: Perform one of the four basic arithmetic operations specified by the
// characters '+', '-', '*' or '/'.
// The four arithmetic operations ... one of these functions is selected
// at runtime with a swicth or a function pointer
float Plus (float a, float b) {
    return a+b;
}
float Minus (float a, float b) {
    return a-b;
}
float Multiply(float a, float b) {
    return a*b;
}
float Divide (float a, float b) {
    return a/b;
}
// Solution with a switch-statement -  specifies which operation to execute
void Switch(float a, float b, char opCode) {
    float result;
    // execute operation
    switch(opCode) {
        case '+' :
        result = Plus (a, b);
        break;
        case '-' :
        result = Minus (a, b);
        break;
        case '*' :
        result = Multiply (a, b);
        break;
        case '/' :
        result = Divide (a, b);
        break;
    }
    cout &lt;&lt; "Switch: 2+5=" &lt;&lt; result &lt;&lt; endl; // display result
}
// Solution with a function pointer -  is a function pointer and points to
// a function which takes two floats and returns a float. The function pointer
// "specifies" which operation shall be executed.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float)) {
    float result = pt2Func(a, b); // call using function pointer
    cout &lt;&lt; "Switch replaced by function pointer: 2-5="; // display result
    cout &lt;&lt; result &lt;&lt; endl;
}
// Execute example code
void Replace_A_Switch() {
    cout &lt;&lt; endl &lt;&lt; "Executing function 'Replace_A_Switch'" &lt;&lt; endl;
    Switch(2, 5, /* '+' specifies function 'Plus' to be executed */ '+');
    Switch_With_Function_Pointer(2, 5, /* pointer to function 'Minus' */ &amp;Minus);
}
</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Chú ý: Một function pointer luôn trỏ đến một function đặc biệt nên tất cả những function mà chúng ta muốn sử dụng với cùng một function pointer thì phải có cùng tham số và giá trị trả về. Nói một cách khác là cùng prototype.</span></p>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">2 Syntax của C và C++ function pointer</span></strong></h1>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Dựa vào cú pháp thì có hai loại function pointer khác nhau: một là những function pointer trỏ đến C function hoặc static C++ member function, một là những function pointer tới non-static C++ member function. Sự khác biệt cơ bản là tất cả pointer đến non-static member function cần một tham số ẩn: con trỏ this tới instance của class. Vậy chỉ cần nhớ rằng có hai loại function pointer không tương thích với nhau. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.1 Define một function pointer</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Vì function pointer không khác gì hơn một biến nên nó phải được define giống như thông thương. Ví dụ dưới đây chúng ta khai báo các function pointer tên là pt2Function, pt2Member và pt2ConstMember. Chúng trở đến function và lấy một biến float và hai biến char và trả về một số int. Ở ví dụ C++ chúng ta giả sử rằng function mà function pointer trỏ đến là non-static member function của TMyClass. </span></p>
<pre>
// 2.1 define a function pointer and initialize to NULL
int (*pt2Function)(float, char, char) = NULL; // C
int (TMyClass::*pt2Member)(float, char, char) = NULL; // C++
int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL; // C++
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.2 Calling Convention</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Thông thường chúng ta không phải nghĩ về calling convention của một function. Trình biên dịch giả định rằng cdecl là convention mặc định nếu chúng ta không sử dụng một convention khác. Nếu bạn muốn tìm hiểu kỹ hơn hãy tìm đọc. Calling convention nói cho trình biên dịch biết cách truyền tham số và cách tạo ra một function. Ví dụ về những calling convention khác là stdcall, pascal, fastcall. Nếu function và function pointer khác calling convention thì chúng cũng không tương thích với nhau và không thể thực hiện phép gán function pointer vào địa chỉ của function kia. Đối với trình biên dịch của Borland và Microsoft thì cần khai báo calling convention ở giữa kiểu trả về và tên hàm hay tên function pointer. Đối với GNU GCC thì sử dụng từ khóa __attribute__: viết khai báo hàm theo sau bởi từ khóa __attribute__ và sau đó là trạng thái của calling convention ở trong (()). </span></p>
<pre>
// 2.2 define the calling convention
void __cdecl DoIt(float a, char b, char c); // Borland and Microsoft
void DoIt(float a, char b, char c) __attribute__((cdecl)); // GNU GCC
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.3 Gán một địa chỉ vào function pointer</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Rất dễ dàng để gán một địa chỉ của một function vào function pointer. Đơn giản chỉ cần lấy tên của function hoặc member function thích hợp. Mặc dù hầu hết các compiler support việc đó nhưng tốt hơn hết là chúng ta sử dụng toán tử địa chỉ &amp; và đặt trước các function name để viết những đoạn mã portable. Chúng ta cũng phải sử dụng tên đầy đủ của member function bao gồm tên lớp và toán tử scope (::).<span>  </span>Chúng ta cũng phải đảm bảo rằng chúng ta được quyền truy nhập vào function ở bên trong scope đó. </span></p>
<pre>
// 2.3 assign an address to the function pointer
// Note: Although you may ommit the address operator on most compilers
// you should always use the correct way in order to write portable code.
// C
int DoIt (float a, char b, char c) {
    printf("DoIt\n");
    return a+b+c;
}
int DoMore(float a, char b, char c)const {
    printf("DoMore\n");
    return a-b+c;
}
pt2Function = DoIt; // short form
pt2Function = &DoMore; // correct assignment using address operator
// C++
class TMyClass {
public:
    int DoIt(float a, char b, char c) {
        cout &lt;&lt; "TMyClass::DoIt"&lt;&lt; endl;
        return a+b+c;
    };
    int DoMore(float a, char b, char c) const {
        cout &lt;&lt; "TMyClass::DoMore" &lt;&lt; endl;
        return a-b+c;
    };
    /* more of TMyClass */
};
pt2ConstMember = &amp;TMyClass::DoMore; // correct assignment using address operator
pt2Member = &amp;TMyClass::DoIt; // note:  may also legally point to &amp;DoMore
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.4 So sánh các function pointer</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Chúng ta có thể sử dụng các toán tử so sánh (==, !=) như bình thường. Trong ví dụ dưới đây nó được kiểm tra xem pt2Function và pt2Member có thực sự chứa địa chỉ của hàm DoIt() và TMyClass::DoMore()</span></p>
<pre>
// 2.4 comparing function pointers
// C
if(pt2Function &gt;0) { // check if initialized
    if(pt2Function == &amp;DoIt)
        printf("Pointer points to DoIt\n");
} else
    printf("Pointer not initialized!!\n");
// C++
if(pt2ConstMember == &amp;TMyClass::DoMore)
    cout &lt;&lt; "Pointer points to TMyClass::DoMore" &lt;&lt; endl;
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.5 Gọi một hàm sử dụng function pointer</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Trong C, chúng ta gọi một hàm sử dụng function pointer bằng cách explicitly dereferencing nó bằng toán tử *. Một lựa chọn khác là sử dụng function pointer thay vì function name. Trong C++ hai toán tử .* và -&gt;* được sử dụng cùng với instance của một class để gọi một (non-static) member function. Nếu lời gọi diễn ra bên trong một member function khác, chúng ta có thể sử dụng con trỏ this.</span></p>
<pre>
// 2.5 calling a function using a function pointer
int result1 = pt2Function (12, 'a', 'b'); // C short way
int result2 = (*pt2Function) (12, 'a', 'b'); // C
TMyClass instance1;
int result3 = (instance1.*pt2Member)(12, 'a', 'b'); // C++
int result4 = (*this.*pt2Member)(12, 'a', 'b'); // C++ if this-pointer can be used
TMyClass* instance2 = new TMyClass;
int result4 = (instance2-&gt;*pt2Member)(12, 'a', 'b'); // C++, instance2 is a pointer
delete instance2;
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.6 Truyền function pointer như là một tham số</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Chúng ta có thể truyền function pointer như một tham số của một function được gọi khác. Điều đó rất cần thiết nếu chúng ta muốn truyền một con trỏ tới một callback function. Đoạn mã dưới đây chỉ cách truyền một pointer tới một function mà trả về một số nguyên và lấy một số float và 2 char làm tham số:</span></p>
<pre>
//------------------------------------------------------------------------------------
// 2.6 How to Pass a Function Pointer
//  is a pointer to a function which returns an int and takes a float and two char
void PassPtr(int (*pt2Func)(float, char, char)) {
    int result = (*pt2Func)(12, 'a', 'b'); // call using function pointer
    cout &lt;&lt; result &lt;&lt; endl;
}
// execute example code - 'DoIt' is a suitable function like defined above in 2.1-4
void Pass_A_Function_Pointer() {
    cout &lt;&lt; endl &lt;&lt; "Executing 'Pass_A_Function_Pointer'" &lt;&lt; endl;
    PassPtr(&amp;DoIt);
}
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.7 Trả về một function pointer</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Hơi mẹo một chút nhưng một function pointer có thể là giá trị trả về của một function. Trong ví dụ sau đây có hai giải pháp cho việc trả về một function pointer. Nếu muốn trả về một pointer vào một member function, chúng ta phải thay đổi definitions và declarations của tất cả các function pointer. </span></p>
<pre>
//------------------------------------------------------------------------------------
// 2.7 How to Return a Function Pointer
// 'Plus' and 'Minus' are defined above. They return a float and take two float
// Direct solution: Function takes a char and returns a pointer to a
// function which is taking two floats and returns a float.
// specifies which function to return
float (*GetPtr1(const char opCode))(float, float) {
    if(opCode == '+')
        return &Plus;
    else
        return &Minus;
} // default if invalid operator was passed
// Solution using a typedef: Define a pointer to a function which is taking
// two floats and returns a float
typedef float(*pt2Func)(float, float);
// Function takes a char and returns a function pointer which is defined
// with the typedef above.  specifies which function to return
pt2Func GetPtr2(const char opCode) {
    if(opCode == '+')
        return &Plus;
    else
        return &Minus; // default if invalid operator was passed
}
// Execute example code
void Return_A_Function_Pointer() {
    cout &lt;&lt; endl &lt;&lt; "Executing 'Return_A_Function_Pointer'" &lt;&lt; endl;
    // define a function pointer and initialize it to NULL
    float (*pt2Function)(float, float) = NULL;
    pt2Function=GetPtr1('+'); // get function pointer from function 'GetPtr1'
    cout &lt;&lt; (*pt2Function)(2, 4) &lt;&lt; endl; // call function using the pointer
    pt2Function=GetPtr2('-'); // get function pointer from function 'GetPtr2'
    cout &lt;&lt; (*pt2Function)(2, 4) &lt;&lt; endl; // call function using the pointer
}
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">2.8 Sử dụng một mảng các function pointer</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Sử dụng một mảng các function pointer khá thú vị. Nó cho phép khả năng lựa chọn một function sử dụng chỉ số (index). Cú pháp thì khá phức tạp và thường xuyên dẫn đến sự nhầm lẫn. Đoạn mã dưới đây bạn sẽ tìm thấy hai cách sử dụng một mảng các function pointer trong C và C++. Cách thứ nhất sử dụng typedef và cách thứ hai sử dụng trức tiếp cách khai bao mảng. Nó tùy thuộc vào bạn thích cách nào hơn. </span></p>
<pre>
//------------------------------------------------------------------------------------
// 2.8 How to Use Arrays of Function Pointers
// C ---------------------------------------------------------------------------------
// type-definition: 'pt2Function' now can be used as type
typedef int (*pt2Function)(float, char, char);
// illustrate how to work with an array of function pointers
void Array_Of_Function_Pointers() {
    printf("\nExecuting 'Array_Of_Function_Pointers'\n");
    // define arrays and ini each element to NULL,  and  are arrays
    // with 10 pointers to functions which return an int and take a float and two char
    // first way using the typedef
    pt2Function funcArr1[10] = {
                                   NULL
                               };
    // 2nd way directly defining the array
    int (*funcArr2[10])(float, char, char) = {
                NULL
            };
    // assign the function's address - 'DoIt' and 'DoMore' are suitable functions
    // like defined above in 2.1-4
    funcArr1[0] = funcArr2[1] = &DoIt;
    funcArr1[1] = funcArr2[0] = &DoMore;
    /* more assignments */
    // calling a function using an index to address the function pointer
    printf("%d\n", funcArr1[1](12, 'a', 'b')); // short form
    printf("%d\n", (*funcArr1[0])(12, 'a', 'b')); // "correct" way of calling
    printf("%d\n", (*funcArr2[1])(56, 'a', 'b'));
    printf("%d\n", (*funcArr2[0])(34, 'a', 'b'));
}
// C++-------------------------------------------------------------------------------
// type-definition: 'pt2Member' now can be used as type
typedef int (TMyClass::*pt2Member)(float, char, char);
// illustrate how to work with an array of member function pointers
void Array_Of_Member_Function_Pointers() {
    cout &lt;&lt; endl &lt;&lt; "Executing 'Array_Of_Member_Function_Pointers'" &lt;&lt; endl;
    // define arrays and ini each element to NULL,  and  are
    // arrays with 10 pointers to member functions which return an int and take
    // a float and two char
    // first way using the typedef
    pt2Member funcArr1[10] = {
                                 NULL
                             };
    // 2nd way of directly defining the array
    int (TMyClass::*funcArr2[10])(float, char, char) = {
                NULL
            };
    // assign the function's address - 'DoIt' and 'DoMore' are suitable member
    // functions of class TMyClass like defined above in 2.1-4
    funcArr1[0] = funcArr2[1] = &amp;TMyClass::DoIt;
    funcArr1[1] = funcArr2[0] = &amp;TMyClass::DoMore;
    /* more assignments */
    // calling a function using an index to address the member function pointer
    // note: an instance of TMyClass is needed to call the member functions
    TMyClass instance;
    cout &lt;&lt; (instance.*funcArr1[1])(12, 'a', 'b') &lt;&lt; endl;
    cout &lt;&lt; (instance.*funcArr1[0])(12, 'a', 'b') &lt;&lt; endl;
    cout &lt;&lt; (instance.*funcArr2[1])(34, 'a', 'b') &lt;&lt; endl;
    cout &lt;&lt; (instance.*funcArr2[0])(89, 'a', 'b') &lt;&lt; endl;
}
</pre>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">3 Implement Callback Functions in C and C++</span></strong></h1>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">3.1 Khái niệm về callback function</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Function pointer cung cấp khái niệm về callback function. Trong bài viết này sẽ giới thiệu về callback function thông qua một hàm về giải thuật sắp xếp nổi tiếng là qsort. Hàm này sẽ sắp xếp các phần tử theo một tiêu chuẩn sắp xếp mà user định nghĩa. Kích thước của một item và số item cần sắp xếp sẽ được truyền vào hàm này. Câu hỏi đặt ra là: làm thế nào để hàm qsort có thể sắp xếp các phần tử mà không có một thông tin gì về kiểu của phần tử sắp xếp? Câu trả lời rất đơn giản: hàm qsort sẽ nhận một function pointer tới một hàm so sánh như một tham số. Hàm so sánh sẽ nhận hai tham số kiểu con trỏ void tới hai phần tử và sẽ đánh giá thứ tự của hai phần tử đó và trả về một số int. Do đó mỗi lần giải thuật sắp xếp cần đánh giá ví trí của hai phần tử thì đơn giản chỉ là gọi tới hàm so sánh thông qua function pointer.</span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">3.2 Implement a callback function in C</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Hãy tham khảo khai báo của hàm qsort dưới đây:</span></p>
<pre>
void qsort(void* field, size_t nElements, size_t sizeOfAnElement,
           int(_USERENTRY *cmpFunc)(const void *, const void*));
</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">field trỏ tới phần tử đầu tiên của mảng cần sắp xếp, nElements là số phần tử cần sắp xếp, sizeOfAnElement là kích thước của một phần tử tính theo byte, và cmpFunc là function pointer tới hàm so sánh. Hàm so sánh này lấy hai phần tử const void* và trả về một số nguyên.</span></p>
<pre>
void qsort( ... , int(_USERENTRY *cmpFunc)(const void*, const void*))
{
    /* sort algorithm - note: item1 and item2 are void-pointers */
    int bigger=cmpFunc(item1, item2); // make callback
    /* use the result */
}
</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Ví dụ mẫu sử dụng qsort</span></p>
<pre>
//-----------------------------------------------------------------------------------------
// 3.3 How to make a callback in C by the means of the sort function qsort
#include  // due to: qsort
#include  // randomize
#include  // printf
// comparison-function for the sort-algorithm
// two items are taken by void-pointer, converted and compared
int CmpFunc(const void* _a, const void* _b) {
    // you've got to explicitly cast to the correct type
    const float* a = (const float*) _a;
    const float* b = (const float*) _b;
    if(*a &gt; *b)
        return 1; // first item is bigger than the second one -&gt; return 1
    else
        if(*a == *b)
            return 0; // equality -&gt; return 0
        else
            return -1; // second item is bigger than the first one -&gt; return -1
}
// example for the use of qsort()
void QSortExample() {
    float field[100];
    ::randomize(); // initialize random-number-generator
    for(int c=0;c&lt;100;c++) // randomize all elements of the field
        field[c]=random(99);
    // sort using qsort()
    qsort((void*) field, /*number of items*/ 100, /*size of an item*/ sizeof(field[0]),
          /*comparison-function*/ CmpFunc);
    // display first ten elements of the sorted field
    printf("The first ten elements of the sorted field are ...\n");
    for(int c=0;c&lt;10;c++)
        printf("element #%d contains %.0f\n", c+1, field[c]);
    printf("\n");
}
</pre>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">3.3 Implement a Callback to a static C++ Member Function</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Implement callback tới static C++ member function cũng giống như implement callback tới hàm C. Static member function không cần một object để thực thi vì vậy nó phải có cùng một prototype như là hàm C với cùng một calling convention, tham số và giá trị trả về. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">3.4 Implement a Callback to a non-static C++ Member Function</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Function pointer tới non-static member thì khác với là một con trỏ hàm C vì nó cần phải truyền một con trỏ this của object. Nếu bạn chỉ muốn gọi hàm callback tới một member function của một class cụ thể thì chỉ cần chuyển đoạn mã từ một function pointer bình thường tới con trỏ tới member function. Nhưng sẽ làm thế nào nếu muốn gọi hàm callback tới một non-static member của một class chưa rõ nào đó (có thể ở các class khác nhau). Nó khó hơn một chút. Chúng ta cần phải viết một static member function như là một wrapper. Static member function thì được coi như là một function bình thường. Sau đó thì chúng ta sẽ ép kiểu con trỏ tới object mà chúng ta muốn nó thực hiện member function, thành void* và truyền nó tới wrapper như là một tham số phụ thêm hoặc là thông qua một biến toàn cục. Tất nhiên chúng ta cũng truyền các tham số gọi tới member function. Wrapper sẽ ép kiểu con trỏ void thành con trỏ tới object của class tương ứng của nó và gọi member function (kỹ thuật này giống với kỹ thuật chúng ta xây dựng class Thread). Dưới đây là hai ví dụ:</span></p>
<p class="MsoNormal" style="text-align:justify;"><em><span style="font-size:10pt;font-family:Verdana;">Ví dụ A: </span></em><span style="font-size:10pt;font-family:Verdana;">Con trỏ tới object được truyền như là một tham số phụ thêm: Hàm DoItA() thực hiện với object của class TClassA để gọi hàm callback. Vì vậy con trỏ tới object của class TClassA và con trỏ tới static wrapper function TClassA::Wrapper_To_Call_Display được truyền tới DoItA(). Hàm wrapper này là callback function. Chúng ta có thể viết những class khác giống như TClassA và sử dụng chung với DoItA miễn là những class này cũng cung cấp các function cần thiết. Chú ý là giải pháp này có thể hữu dụng nếu chúng ta tự thiết kế giao diện của hàm callback. Nó tốt hơn rất nhiều so với giải pháp thứ hai sử dụng biến toàn cục. </span></p>
<pre>
//-----------------------------------------------------------------------------------------
// 3.5 Example A: Callback to member function using an additional argument
// Task: The function 'DoItA' makes something which implies a callback to
// the member function 'Display'. Therefore the wrapper function
// 'Wrapper_To_Call_Display is used.
#include  // due to: cout
class TClassA {
public:
    void Display(const char* text) {
        cout &lt;&lt; text &lt;Display(string);
}
// function does something which implies a callback
// note: of course this function can also be a member function
void DoItA(void* pt2Object, void (*pt2Function)(void* pt2Object, char* text)) {
    /* do something */
    pt2Function(pt2Object, "hi, i'm calling back using a argument <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> "); // make callback
}
// execute example code
void Callback_Using_Argument() {
    // 1. instantiate object of TClassA
    TClassA objA;
    // 2. call 'DoItA' for
    DoItA((void*) &amp;objA, TClassA::Wrapper_To_Call_Display);
}
</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;"><em>Ví dụ B</em>: Con trỏ tới object được lưu trữ trong một biến toàn cục. Hàm DoItB sẽ thực hiện với đối tượng của class B để gọi hàm callback. Một con trỏ tới static wrapper function TClassB::Wrapper_To_Call_Display được truyền vào DoItB. Hàm wrapper này là callback function. Wrapper sử dụng biến toàn cục void* pt2Object và phép ép kiểu thành một đối tượng của TClassB. Phải luôn chú ý khởi tạo biến toàn cục để chỉ đến đối tượng của class chính xác. Chúng ta có thể viết những class khác như TClassB và sử dụng chúng với DoItB miễn là những class đó cung cấp các function cần thiết. Cách này không phải là một giải pháp tốt bởi vì sử dụng biến toàn cục rất nguy hiểm và có thể gây ra những lỗi nghiêm trọng. </span></p>
<pre>
//------------------------------------------------------------------------------
// 3.5 Example B: Callback to member function using a global variable
// Task: The function 'DoItB' makes something which implies a callback to
// the member function 'Display'. Therefore the wrapper function
// 'Wrapper_To_Call_Display is used.
#include  // due to: cout
void* pt2Object; // global variable which points to an arbitrary object
class TClassB {
public:
    void Display(const char* text) {
        cout &lt;&lt; text &lt;&lt; endl;
    };
    static void Wrapper_To_Call_Display(char* text);
    /* more of TClassB */
};
// static wrapper function to be able to callback the member function Display()
void TClassB::Wrapper_To_Call_Display(char* string) {
    // explicitly cast global variable  to a pointer to TClassB
    // warning:  MUST point to an appropriate object!
    TClassB* mySelf = (TClassB*) pt2Object;
    // call member
    mySelf-&gt;Display(string);
}
// function does something which implies a callback
// note: of course this function can also be a member function
void DoItB(void (*pt2Function)(char* text)) {
    /* do something */
    pt2Function("hi, i'm calling back using a global <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> "); // make callback
}
// execute example code
void Callback_Using_Global() {
    // 1. instantiate object of TClassB
    TClassB objB;
    // 2. assign global variable which is used in the static wrapper function
    // important: never forget to do this!!
    pt2Object = (void*) &objB;
    // 3. call 'DoItB' for
    DoItB(TClassB::Wrapper_To_Call_Display);
}
</pre>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/40/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/40/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/40/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/40/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/40/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/40/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/40/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/40/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/40/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/40/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/40/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/40/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=40&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/11/26/function-pointer/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>
	</item>
		<item>
		<title>STL Function Object và các ứng dụng (1)</title>
		<link>http://openandfree.wordpress.com/2007/11/23/stl-function-object-va-cac-%e1%bb%a9ng-d%e1%bb%a5ng-1/</link>
		<comments>http://openandfree.wordpress.com/2007/11/23/stl-function-object-va-cac-%e1%bb%a9ng-d%e1%bb%a5ng-1/#comments</comments>
		<pubDate>Fri, 23 Nov 2007 05:13:10 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[function object]]></category>
		<category><![CDATA[operator()]]></category>
		<category><![CDATA[set]]></category>
		<category><![CDATA[sorting criteria]]></category>

		<guid isPermaLink="false">http://openandfree.wordpress.com/2007/11/23/stl-function-object-va-cac-%e1%bb%a9ng-d%e1%bb%a5ng-1/</guid>
		<description><![CDATA[Function Object là gì?
Function object là một object được sử dụng như một function. Với một function object của lớp Foo, khi viết Foo() nghĩa là chúng ta đang gọi đến operator() của lớp Foo. Viết một function object nghĩa là viết operator() cho một lớp. Chúng ta đã biết operator của một lớp được [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=39&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p align="justify"><strong>Function Object là gì?</strong></p>
<p align="justify">Function object là một object được sử dụng như một function. Với một function object của lớp Foo, khi viết Foo() nghĩa là chúng ta đang gọi đến operator() của lớp Foo. Viết một function object nghĩa là viết operator() cho một lớp. Chúng ta đã biết operator của một lớp được viết như sau</p>
<pre>
class Foo
{
  public:

    return_type operator() ( parameter list ) {

      statements;

    }

    /* Các public member khác */

  private:

    /* Các private member */

};</pre>
<p align="justify">Cài đặt cụ thể cho operator() tùy thuộc vào ngữ cảnh sử dụng của function object. Qua cái nhìn đầu tiên, chúng ta thấy rằng cách viết này chính là sự phức tạp hóa của một hàm bình thường sau đây</p>
<pre>
return_type foo( parameter list ) {

  statements;

}</pre>
<p align="justify">Sự phức tạp hóa này mang lại ba lợi ích</p>
<p align="justify">1- Các function object là các object, bởi vậy chúng có trạng thái, còn các hàm bình thường thì không.<br />
2- Các function object thuộc về một lớp nào đó. Bởi vậy, chúng ta có thể tham số hóa các kiểu dữ liệu bên trong function object thông qua template.<br />
3- Các function object thường chạy nhanh hơn các hàm thông thường.</p>
<p align="justify">Không nên mất thời gian suy nghĩ về ba lợi ích này làm gì! Hãy nghiên cứu các ứng dụng của function object, chúng ta sẽ dễ dàng hiểu được những lợi ích của chúng.</p>
<p align="justify"><strong>Ứng dụng của function object</strong></p>
<p align="justify">Các function object được sử dụng trong hai trường hợp sau đây<br />
1- Làm tiêu chí sắp xếp cho các container<br />
2- Làm tham số cho các STL algorithm<br />
Việc một function object được sử dụng ở đâu sẽ quyết định cách viết operator() của lớp đó.</p>
<p align="justify"><strong>Function Object làm tiêu chí sắp xếp cho các container</strong></p>
<p align="justify">Trong phần này, chúng ta sẽ xem xét các ứng dụng của function object trong việc tạo ra các tiêu chí sắp xếp cho các STL set. Các ví dụ này có thể mở rộng cho các STL associative container khác như multiset, map, multimap.</p>
<p align="justify">Khi chúng ta đưa các phần tử vào một set, chúng sẽ được sắp xếp sao cho hai phần tử liên tiếp phải thỏa mãn tiêu chí sắp xếp dành cho set đó. Nếu các phần tử của set là các kiểu cơ bản như int hay string, chúng ta có thể sử dụng các tiêu chí sắp xếp sẵn có như greater hay less. Ví dụ dòng khai báo dưới đây</p>
<pre>
std::set&lt; std::string, greater &gt; strSet;</pre>
<p align="justify">khai báo một set với các phần tử là các STL string được sắp xếp theo thứ tự tăng dần. Tuy nhiên, nếu các phần tử cần đưa vào set có kiểu do người dùng định nghĩa, ví dụ là các đối tượng của một lớp, thì làm sao để xác định thứ tự của chúng trong set? Có hai cách thực hiện: Một là vẫn sử dụng các tiêu chí sẵn có là less và greater. Tuy nhiên, cách này chỉ thực hiện được nếu lớp đã định nghĩa sẵn operator &lt; (cho tiêu chí less) hoặc operator &gt; (cho tiêu chí greater). Không phải lớp nào cũng cung cấp sẵn các operator này, mà không phải lúc nào chúng ta cũng có quyền “nhảy” vào để thêm mã cho lớp, mà giả sử chúng ta có quyền đi nữa thì cũng không nên làm phức tạp hóa một lớp sẵn có. Cách thứ hai là chúng ta tự định nghĩa một tiêu chí sắp xếp mới, đây chính là lúc cần đến function object. Xem ví dụ sau đây: Giả sử chúng ta cần lưu các đối tượng của lớp Person vào một set. Định nghĩa của lớp Person như sau:</p>
<p><span id="more-39"></span></p>
<pre>
class Person
{

  public:

    Person( const std::string &amp; name )
      : name_( name ) {  }

    std::string name() const {

      return name_;

    }

  private:

    std::string name_;

};</pre>
<p align="justify">Lớp Person có một thành phần dữ liệu là name, đại diện cho tên của một người. Giả sử chúng ta cần lưu các đối tượng của lớp Person vào một set theo thứ tự tăng dần của name. Nhớ lại rằng set sẽ sắp xếp các phần tử của nó sao cho hai phần tử kề nhau phải thoả mãn tiêu chí sắp xếp. Chính điều này quyết định cách viết của operator():</p>
<p align="justify">1- Giá trị trả về có kiểu bool, cho biết hai phần từ kề nhau đã đúng thứ tự hay chưa.<br />
2- Tham số nhận vào là hai const reference đến các phần tử cần sắp xếp. Trong đó, tham số thứ nhất là phần tử đứng trước, tham số thứ hai là phần tử đứng sau.<br />
3- Đoạn mã trong thân operator() tùy thuộc vào thứ tự sắp xếp (đối tượng đứng trước lớn hơn hay nhỏ hơn đối tượng đứng sau) và thành phần dữ liệu được mang ra so sánh.</p>
<p align="justify">Từ ba quy tắc này, chúng ta viết được một class như sau</p>
<pre>
class SortPerson
{

  public:

    bool operator() ( const Person &amp; p1, const Person &amp; p2 ) const {

      return p1.name() &lt; p2.name();

    }

};</pre>
<p align="justify">operator() của class này trả về true nếu name của đối tượng Person đứng trước nhỏ hơn name của đối tượng Person đứng sau, tức là các đối tượng sẽ được sắp xếp theo chiều tăng của name. Chú ý rằng các operator() của function object nên được khai báo là const. Cuối cùng, chúng ta khai báo một set có tên là personSet với các phần tử là các đối tượng kiểu Person và tiêu chí sắp xếp là SortPerson</p>
<pre>
std::set personSet;

personSet.insert( Person( “foo” ) );

personSet.insert( Person( “bar” ) );</pre>
<p align="justify">Mã nguồn đầy đủ của chương trình là</p>
<pre>
#include 

#include 

class Person

{

  public:

    Person( const std::string &amp; name )
      : name_( name ) {  }

    std::string name() const {

      return name_;

    }

  private:

    std::string name_;

};

class SortPerson

{

  public:

    bool operator() ( const Person &amp; p1, const Person &amp; p2 ) const {

      return p1.name() &lt; p2.name();

    }

};

int main()

{

  std::set personSet;

  std::set::iterator psIt;

  personSet.insert( Person( “foo” ) );

  personSet.insert( Person( “bar” ) );

  for( psIt = personSet.begin(); psIt != personSet.end(); ++psIt )

    std::cout &lt;name() &lt;&lt; std::endl;

  return 0;

}</pre>
<p align="justify">Kết quả chạy chương trình là</p>
<p align="justify">bar<br />
foo</p>
<p align="justify">Nếu muốn sắp xếp các đối tượng Person theo chiều giảm dần của name, chúng ta chỉ việc thay đổi đoạn mã trong thân operator() của lớp SortPerson thành như sau</p>
<pre>
return p1.name() &gt; p2.name();</pre>
<p align="justify">Khi đó, kết quả chạy chương trình là</p>
<p align="justify">foo<br />
bar</p>
<p align="justify">Chú ý rằng, SortPerson là một kiểu và là một thành phần tạo nên kiểu set. Nói đến kiểu của một set nghĩa là nói đến kiểu của các phần tử và kiểu tiêu chí sắp xếp. Các toán tử hai ngôi (binary operator) chỉ thực hiện được giữa các set cùng kiểu mà thôi. Bạn không thể gán một set lưu các số nguyên theo thứ tự giảm dần cho một set lưu các số nguyên theo thứ tự tăng dần, cũng như gán một set lưu các đối tượng Person theo tiêu chí SortPerson cho một set khác cũng lưu trữ các Person nhưng theo một kiểu tiêu chí sắp xếp khác.</p>
<pre>
std::set&lt; int, greater &gt;  intSet1;

std::set&lt; int, less &gt;     intSet2;

…

intSet1 = intSet2; //COMPILE ERROR!!!

std::set personSet;

std::set otherPersonSet;

…

personSet = otherPersonSet; //COMPILE ERROR!!!</pre>
<p align="justify">Tiêu chí sắp xếp của một set phải được xác định ngay từ giai đoạn khai báo. Tuy nhiên, thật bất tiện nếu với mỗi tiêu chí sắp xếp chúng ta lại phải xây dựng một lớp riêng mà định nghĩa của các lớp này chỉ khác nhau ở mỗi phép so sánh trong thân operator(). Đây chính là lúc lợi điểm thứ nhất của các function object phát huy tác dụng. Mặc dù cùng một kiểu tiêu chí sắp xếp là SortPerson nhưng với các đối tượng có trạng thái khác nhau, chúng ta có những tiêu chí sắp xếp thực tế khác nhau. Xem đoạn mã sau đây, trong đó chúng ta đưa thêm vào lớp SortPerson một thành phần dữ liệu để tạo ra hai “trạng thái”, mỗi trạng thái ứng với một cách sắp xếp.</p>
<pre>
class SortPerson

{

  public:

    SortPerson( const bool &amp; isAsc )
      : isAsc_( isAsc ) {  }

    bool operator() ( const Person &amp; p1, const Person &amp; p2 ) const {

      if( isAsc_ )

        return p1.name()  p2.name();

    }

  private:

    bool isAsc_;  //true : Sắp xếp theo chiều tăng của name
                  //false: Sắp xếp theo chiều giảm của name
};</pre>
<p align="justify">Sau đó, chúng ta khai báo các set với tiêu chí sắp xếp là các đối tượng cùng kiểu SortPerson, nhưng trạng thái thì khác nhau.</p>
<pre>
SortPerson sp1( 0 );

std::set personSet1( sp1 ); //personSet1 lưu các Person theo chiều

//giảm của name

SortPerson sp2( 1 );

std::set personSet2( sp2 ); //personSet2 lưu các Person theo chiều

//tăng của name</pre>
<p align="justify">Điều thú vị ở đây là personSet1 và personSet2 lưu các Person theo hai thứ tự khác nhau, nhưng chúng lại thuộc cùng một kiểu: Kiểu phần tử Person và kiểu tiêu chí sắp xếp SortPerson. Bởi vậy, chúng ta có thể gán chúng cho nhau. Vậy phải chăng chúng ta có thể dễ dàng thay đổi thứ tự của các Person trong set chỉ bằng một phép gán? Thật đáng buồn là KHÔNG, vì tiêu chí sắp xếp thực tế của chúng cũng bị gán theo! Nghĩa là nếu ta viết:</p>
<pre>
personSet2 = personSet1;</pre>
<p align="justify">thì personSet2 lại trở thành một set lưu các Person theo chiều giảm của name! Hàm thành phần swap() của set cũng cho kết quả tương tự như phép gán. Với một personSet1 đang chứa một bộ dữ liệu, không có một toán tử nào, một hàm thành phần nào hay một algorithm nào có khả năng tạo ra một personSet2 chứa chính bộ dữ liệu đó nhưng theo một thứ tự khác, giải pháp duy nhất là phải xây dựng lại personSet2 từ đầu, vấn đề là làm sao để giảm công sức của việc xây dựng này mà thôi. Phần tiếp theo sẽ đưa ra các giải pháp cụ thể cho vấn đề này.</p>
<p align="justify"><strong>Xây dựng các set tại thời điểm chạy chương trình (run-time)</strong></p>
<p align="justify">Trong thực tế, chúng ta thường có nhu cầu xây dựng các set chứa cùng một bộ dữ liệu nhưng sắp xếp theo những tiêu chí khác nhau (hoặc thậm chí là các “kiểu” tiêu chí khác nhau). Xét ví dụ sau đây: Giả sử chúng ta cần hai set cùng lưu các đối tượng Person nhưng theo hai tiêu chí: Tăng của name và giảm của name. Tùy từng điều kiện cụ thể xảy ra trong lúc chạy mà set nào sẽ được sử dụng. Sau đây là một số giải pháp:</p>
<p align="justify">- Cách 1: Tạo ra một set theo một tiêu chí nào đó, sau đó dùng một thao tác nào đó (gán, copy, hàm thành phần, algorithm…) để tạo ra một set chứa cùng bộ dữ liệu nhưng theo tiêu chí khác. Như đã nói ở phần trên, cách này không thực hiện được.</p>
<p align="justify">- Cách 2: Xây dựng sẵn luôn hai set ở thời điểm dịch chương trình (compile time) để lúc nào cần thì đem ra “xài”. Tuy nhiên, cách này rất lãng phí bộ nhớ nếu một trong hai set không được sử dụng vì một điều kiện không xảy ra trong khi chạy chương trình.</p>
<p align="justify">- Cách 3: Viết một đoạn code dùng if như sau:</p>
<pre>
if( condition ) {

  /* Xây dựng personSet1 */

} else {

  /* Xây dựng personSet2 */

}</pre>
<p align="justify">Nhược điểm của cách này là trùng lặp mã bởi đoạn mã xây dựng hai set có thể rất giống nhau. Hơn nữa với các associative container phức tạp hơn, chẳng hạn một map với các phần tử lại là các map mà mỗi map đều cần xây dựng theo hai tiêu chí sắp xếp khác nhau, thì đoạn mã sẽ trở nên phức tạp với quá nhiều lệnh if…else.</p>
<p align="justify">- Cách 4: Sử dụng function object. Đây là lúc các function object cùng kiểu nhưng khác trạng thái phát huy tác dụng. Chúng ta viết một hàm fillPersonSet có nhiệm vụ xây dựng một personSet theo kiểu tiêu chí SortPerson. Tham số truyền vào quyết định cách sắp xếp cụ thể trong personSet.</p>
<pre>
std::set fillPersonSet( const bool &amp; isAsc ) {

  SortPerson sp( isAsc );

  std::set personSet( sp );

  /* đưa dữ liệu vào personSet */

  return personSet;

}</pre>
<p align="justify">Hàm fillPersonSet được sử dụng như sau</p>
<pre>
/* Tạo personSet theo chiều tăng dần của name */

std::set personSet1 = fillPersonSet( true );

/* Tạo personSet theo chiều giảm dần của name */

std::set personSet2 = fillPersonSet( false );</pre>
<p align="justify">Một cách viết khác của hàm fillPersonSet cũng mang lại kết quả tương đương</p>
<pre>
void fillPersonSet( std::set &amp; personSet ) {

 /* đưa dữ liệu vào personSet */

}</pre>
<p align="justify">Khi đó, hàm được sử dụng như sau</p>
<pre>
/* Tạo set theo chiều tăng của name */

SortPerson sp1( true );

std::set personSet1( sp1 );

fillPersonSet( personSet1 );

/* Tạo set theo chiều giảm của name */

SortPerson sp2( false );

std::set personSet2( sp2 );

fillPersonSet( personSet2 );</pre>
<p align="justify">Trường hợp tổng quát hơn, nếu hai set lưu các đối tượng Person theo hai “kiểu” tiêu chí sắp xếp khác nhau (không còn cùng một kiểu SortPerson nữa ) thì sao? Xét ví dụ sau đây:</p>
<p align="justify">Lớp Person được mở rộng với hai thành phần dữ liệu là firstname và lastname. Chúng ta cần xây dựng các set lưu các đối tượng Person theo thứ tự tăng của fistname hoặc tăng của lastname.</p>
<pre>
/* Lớp Person mới */

class Person

{

  public:

    Person( const std::string &amp; firstname, const std::string &amp; lastname )

      : firstname_( firstname ), lastname_( lastname ) { }

    std::string firstname() const {

      return firstname_:

    }

    std::string lastname() const {

      return lastname_;

    }

};</pre>
<p align="justify">Chúng ta xây dựng hai kiểu tiêu chí sắp xếp như sau</p>
<pre>
/* Hai kiểu tiêu chí sắp xếp: Theo firstname và lastname */

class SortFirstname

{

  public:

    bool operator() ( const Person &amp; p1, const Person &amp; p2 ) const {

      return p1.firstname() &lt; p2.firstname();

    }

};

class SortLastname

{

  public:

    bool operator() ( const Person &amp; p1, const Person &amp; p2 ) const {

      return p1.lastname() &lt; p2.lastname();

    }

};</pre>
<p align="justify">Hàm fillPersonSet được viết lại, sử dụng template để tham số hóa kiểu tiêu chí sắp xếp.</p>
<pre>
/* Hàm fillPersonSet mới */

template 

void fillPersonSet( std::set personSet ) {

  /* đưa dữ liệu vào personSet */

}

/* Sử dụng fillPersonSet */

/* Tạo set theo chiều tăng của firstname */

std::set personSet1;

fillPersonSet( personSet1 );

/*  Tạo set theo chiều tăng của lastname */

std::set personSet2;

fillPersonSet( personSet2 );</pre>
<p align="justify">Còn tiếp&#8230;</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/39/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/39/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/39/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/39/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/39/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/39/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/39/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/39/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/39/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/39/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/39/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/39/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=39&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/11/23/stl-function-object-va-cac-%e1%bb%a9ng-d%e1%bb%a5ng-1/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
	</channel>
</rss>