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

<channel>
	<title>Solstice | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/articles/author/solstice/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Thu, 10 Oct 2013 03:43:08 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>C++11的Lambda使用一例：华容道求解</title>
		<link>https://coolshell.cn/articles/10476.html</link>
					<comments>https://coolshell.cn/articles/10476.html#comments</comments>
		
		<dc:creator><![CDATA[Solstice]]></dc:creator>
		<pubDate>Wed, 09 Oct 2013 07:50:21 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Lambda]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=10476</guid>

					<description><![CDATA[<p>（感谢网友 @bnu_chenshuo 投稿） 华容道是一个有益的智力游戏，游戏规则不再赘述。用计算机求解华容道也是一道不错的编程练习题，为了寻求最少步数，求解...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/10476.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/10476.html">C++11的Lambda使用一例：华容道求解</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>（感谢网友 <a href="http://weibo.com/u/1701018393?source=webim" target="_blank"><img decoding="async" alt="" src="http://tp2.sinaimg.cn/1701018393/50/1297990315/1" /></a><a title="bnu_chenshuo" href="http://weibo.com/u/1701018393?source=webim" target="_blank"> @bnu_chenshuo </a>投稿）</strong></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="wp-image-10490 alignright" alt="" src="https://coolshell.cn/wp-content/uploads/2013/10/huarong.png" width="365" height="227" srcset="https://coolshell.cn/wp-content/uploads/2013/10/huarong.png 522w, https://coolshell.cn/wp-content/uploads/2013/10/huarong-300x186.png 300w, https://coolshell.cn/wp-content/uploads/2013/10/huarong-435x270.png 435w" sizes="(max-width: 365px) 100vw, 365px" /></p>
<p>华容道是一个有益的智力游戏，游戏规则不再赘述。用计算机求解华容道也是一道不错的编程练习题，为了寻求最少步数，求解程序一般用广度优先搜索算法。华容道的一种常见开局如图 1 所示。</p>
<p>广度优先搜索算法求解华容道的基本步骤：</p>
<ol>
<li>准备两个“全局变量”，队列 Q 和和集合 S，S 代表“已知局面”。初时 Q 和 S 皆为空。</li>
<li>将初始局面加入队列 Q 的末尾，并将初始局面设为已知。</li>
<li>当队列不为空时，从 Q 的队首取出当前局面 <code>curr</code>。如果队列为空则结束搜索，表明无解。</li>
<li>如果 <code>curr</code> 是最终局面（曹操位于门口，图 2），则结束搜索，否则继续到第 5 步。</li>
<li>考虑 <code>curr</code> 中每个可以移动的棋子，试着上下左右移动一步，得到新局面 <code>next</code>，如果新局面未知（<code>next</code> ∉ S），则把它加入队列 Q，并设为已知。这一步可能产生多个新局面。</li>
<li>回到第2步。</li>
</ol>
<p>其中“局面已知”并不要求每个棋子的位置相同，而是指棋子的投影的形状相同（代码中用 mask 表示），例如交换图 1 中的张飞和赵云并不产生新局面，这一规定可以大大缩小搜索空间。</p>
<p>以上步骤很容易转换为 C++ 代码，这篇文章重点关注的是第 5 步的实现。</p>
<p><span id="more-10476"></span></p>
<pre data-enlighter-language="cpp" class="EnlighterJSRAW">// 第 1 步
std::unordered_set&lt;Mask&gt; seen;
std::deque&lt;State&gt; queue;

// 第 2 步
State initial;
// 填入 initial，略。
queue.push_back(initial);
seen.insert(initial.toMask());

// 第 3 步
while (!queue.empty())
{
  const State curr = queue.front();
  queue.pop_front();

  // 第 4 步
  if (curr.isSolved())
    break;

  // 第 5 步
  for (const State&amp; next : curr.moves())
  {
    auto result = seen.insert(next.toMask());
    if (result.second)
      queue.push_back(next);
  }
}</pre>
<p>在以上原始实现中，<code>curr.move()</code> 将返回一个 <code>std::vector&lt;State&gt;</code> 临时对象。一种节省开销的办法是准备一个 <code>std::vector&lt;State&gt;</code> “涂改变量”，让 <code>curr.move()</code> 反复修改它，比如改成：</p>
<pre data-enlighter-language="cpp" class="EnlighterJSRAW">// 第 1 步新增一个 scratch 变量
std::vector&lt;State&gt; nextMoves;

// 第 3 步
while (!queue.empty())
{
  // ...
  // 第 5 步
  curr.fillMoves(&amp;nextMoves);
  for (const State&amp; next : nextMoves)
  { /* 略 */ }
}</pre>
<p>还有一种彻底不用这个 <code>std::vector&lt;State&gt;</code> 的办法，把一部分逻辑以 lambda 的形式传给 <code>curr.move()</code>，代码的结构基本不变：</p>
<pre data-enlighter-language="cpp" class="EnlighterJSRAW">// 第 3 步
while (!queue.empty())
{
  // ...
  // 第 5 步
  curr.move([&amp;seen, &amp;queue](const State&amp; next) {
    auto result = seen.insert(next.toMask());
    if (result.second)
      queue.push_back(next);
  });
}</pre>
<p>这样一来，主程序的逻辑依然清晰，不必要的开销也降到了最小。</p>
<p>在我最早的实现中，<code>curr.move()</code> 的参数是 <code>const std::function&lt;void(const State&amp;)&gt; &amp;</code>，但是我发现这里每次构造 <code>std::function&lt;void(const State&amp;)&gt;</code> 对象都会分配一次内存，似乎有些不值。因此在现在的实现中 <code>curr.move()</code> 是个函数模板，这样就能自动匹配lambda参数（通常是个 struct 对象），省去了 <code>std::function</code>的内存分配。</p>
<p>本文完整的代码见 <a href="https://github.com/chenshuo/recipes/blob/master/puzzle/huarong.cc">https://github.com/chenshuo/recipes/&#8230;/puzzle/huarong.cc</a>，需用 GCC 4.7 编译，求解图 1 的题目的耗时约几十毫秒。</p>
<p><strong>练习：</strong>修改程序，打印每一步移动棋子的情况。<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/22422.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-150x150.png" alt="是微服务架构不香还是云不香？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22422.html" class="wp_rp_title">是微服务架构不香还是云不香？</a></li><li ><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg" alt="Rust语言的编程范式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_title">Rust语言的编程范式</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg" alt="API设计原则 &#8211; Qt官网的设计实践总结" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_title">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/12012.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif" alt="State Threads 回调终结者" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12012.html" class="wp_rp_title">State Threads 回调终结者</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/10476.html">C++11的Lambda使用一例：华容道求解</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/10476.html/feed</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
		<item>
		<title>C++面试中string类的一种正确写法</title>
		<link>https://coolshell.cn/articles/10478.html</link>
					<comments>https://coolshell.cn/articles/10478.html#comments</comments>
		
		<dc:creator><![CDATA[Solstice]]></dc:creator>
		<pubDate>Wed, 09 Oct 2013 07:40:38 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[面试]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=10478</guid>

					<description><![CDATA[<p>（感谢网友 @bnu_chenshuo 投稿） C++ 的一个常见面试题是让你实现一个 String 类，限于时间，不可能要求具备 std::string 的功...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/10478.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/10478.html">C++面试中string类的一种正确写法</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>（感谢网友 <a href="http://weibo.com/u/1701018393?source=webim" target="_blank"><img decoding="async" alt="" src="http://tp2.sinaimg.cn/1701018393/50/1297990315/1" /></a><a title="bnu_chenshuo" href="http://weibo.com/u/1701018393?source=webim" target="_blank"> @bnu_chenshuo </a>投稿）</strong></p>
<p>C++ 的一个常见面试题是让你实现一个 String 类，限于时间，不可能要求具备 std::string 的功能，但至少要求能正确管理资源。具体来说：</p>
<ol>
<li>能像 int 类型那样定义变量，并且支持赋值、复制。</li>
<li>能用作函数的参数类型及返回类型。</li>
<li>能用作标准库容器的元素类型，即 vector/list/deque 的 value_type。（用作 std::map 的 key_type 是更进一步的要求，本文从略）。</li>
</ol>
<p>换言之，你的 String 能让以下代码编译运行通过，并且没有内存方面的错误。</p>
<pre data-enlighter-language="cpp" class="EnlighterJSRAW">void foo(String x)
{
}

void bar(const String&amp; x)
{
}

String baz()
{
  String ret(&quot;world&quot;);
  return ret;
}

int main()
{
  String s0;
  String s1(&quot;hello&quot;);
  String s2(s0);
  String s3 = s1;
  s2 = s1;

  foo(s1);
  bar(s1);
  foo(&quot;temporary&quot;);
  bar(&quot;temporary&quot;);
  String s4 = baz();

  std::vector&lt;String&gt; svec;
  svec.push_back(s0);
  svec.push_back(s1);
  svec.push_back(baz());
  svec.push_back(&quot;good job&quot;);
}</pre>
<p><span id="more-10478"></span>本文给出我认为适合面试的答案，强调正确性及易实现（白板上写也不会错），不强调效率。某种意义上可以说是以时间（运行快慢）换空间（代码简洁）。</p>
<p>首先选择数据成员，最简单的 String 只有一个 char* 成员变量。好处是容易实现，坏处是某些操作的复杂度较高（例如 size() 会是线性时间）。为了面试时写代码不出错，本文设计的 String 只有一个 char* data_成员。而且规定 invariant 如下：一个 valid 的 string 对象的 data_ 保证不为 NULL，data_ 以 <code>'\0'</code> 结尾，以方便配合 C 语言的 str*() 系列函数。</p>
<p>其次决定支持哪些操作，构造、析构、拷贝构造、赋值这几样是肯定要有的（以前合称 big three，现在叫 copy control）。如果钻得深一点，C++11的移动构造和移动赋值也可以有。为了突出重点，本文就不考虑 operator[] 之类的重载了。</p>
<p>这样代码基本上就定型了：</p>
<pre data-enlighter-language="cpp" class="EnlighterJSRAW">#include &lt;utility&gt;
#include &lt;string.h&gt;

class String
{
 public:
  String()
    : data_(new char[1])
  {
    *data_ = &#039;&#092;&#048;&#039;;
  }

  String(const char* str)
    : data_(new char[strlen(str) + 1])
  {
    strcpy(data_, str);
  }

  String(const String&amp; rhs)
    : data_(new char[rhs.size() + 1])
  {
    strcpy(data_, rhs.c_str());
  }
  /* Delegate constructor in C++11
  String(const String&amp; rhs)
    : String(rhs.data_)
  {
  }
  */

  ~String()
  {
    delete[] data_;
  }

  /* Traditional:
  String&amp; operator=(const String&amp; rhs)
  {
    String tmp(rhs);
    swap(tmp);
    return *this;
  }
  */
  String&amp; operator=(String rhs) // yes, pass-by-value
  {
    swap(rhs);
    return *this;
  }

  // C++ 11
  String(String&amp;&amp; rhs)
    : data_(rhs.data_)
  {
    rhs.data_ = nullptr;
  }

  String&amp; operator=(String&amp;&amp; rhs)
  {
    swap(rhs);
    return *this;
  }

  // Accessors

  size_t size() const
  {
    return strlen(data_);
  }

  const char* c_str() const
  {
    return data_;
  }

  void swap(String&amp; rhs)
  {
    std::swap(data_, rhs.data_);
  }

 private:
  char* data_;
};</pre>
<p>注意代码的几个要点：</p>
<ol>
<li>只在构造函数里调用 new char[]，只在析构函数里调用 delete[]。</li>
<li>赋值操作符采用了《C++编程规范》推荐的现代写法。</li>
<li>每个函数都只有一两行代码，没有条件判断。</li>
<li>析构函数不必检查 data_ 是否为 NULL。</li>
<li>构造函数 <code>String(const char* str)</code> 没有检查 str 的合法性，这是一个永无止境的争论话题。这里在初始化列表里就用到了 str，因此在函数体内用 assert() 是无意义的。</li>
</ol>
<p>这恐怕是最简洁的 String 实现了。</p>
<p><strong>练习1</strong>：增加 operator==、operator&lt;、operator[] 等操作符重载。</p>
<p><strong>练习2</strong>：实现一个带 int size_; 成员的版本，以空间换时间。</p>
<p><strong>练习3</strong>：受益于右值引用及移动语意，在 C++11 中对 String 实施直接插入排序的性能比C++98/03要高，试编程验证之。（g++的标准库也用到了此技术。）</p>
<p>陈皓注：同时，大家可以移步看看我的一篇老文《<a href="http://blog.csdn.net/haoel/article/details/1491219" target="_blank">STL中String类的问题</a>》<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/7965.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg" alt="一个fork的面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7965.html" class="wp_rp_title">一个fork的面试题</a></li><li ><a href="https://coolshell.cn/articles/4162.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt="又一个有趣的面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4162.html" class="wp_rp_title">又一个有趣的面试题</a></li><li ><a href="https://coolshell.cn/articles/3961.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg" alt="“火柴棍式”程序员面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3961.html" class="wp_rp_title">“火柴棍式”程序员面试题</a></li><li ><a href="https://coolshell.cn/articles/3738.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg" alt="打印质数的各种算法" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3738.html" class="wp_rp_title">打印质数的各种算法</a></li><li ><a href="https://coolshell.cn/articles/3445.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="输出从1到1000的数" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3445.html" class="wp_rp_title">输出从1到1000的数</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/10478.html">C++面试中string类的一种正确写法</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/10478.html/feed</wfw:commentRss>
			<slash:comments>40</slash:comments>
		
		
			</item>
	</channel>
</rss>
