Skip to content

Latest commit

 

History

History
189 lines (115 loc) · 15.2 KB

how_to_ask.rst

File metadata and controls

189 lines (115 loc) · 15.2 KB

如何询问 C++ 代码问题

当一个人寻求建议, 但却从不接受任何建议, 因为他们确信这些建议实际上与他们的问题无关……最终, 建议者会停止提供建议——他们觉得就算建议了也毫无意义.

——Kate Gregory

英语原文见此, 翻译有一些排版上的调整.

最近有很多人在写 C++ 代码时求助于我. 我通常很高兴并且能够提供帮助. 但有时候, 我要么帮不上忙, 要么选择不帮忙. 我认为对其中的一些情况稍加解释可能会有所帮助. 因为社区里的其他成员很可能也有一套类似于我的标准, 用于决定什么情况帮助别人写代码以及什么情况不帮助别人.

前言

警告: 这篇文章很长, 但绝对值得全部阅读.

你以为我会说 TLDR (太长了别看)? 实际上, 你只需提问就可能得到来自成功人士的免费指导和商议! 但如果你期望帮助者帮上你绝大部分忙, 并且接受你的指示和引导, 那么除非你知道如何有效地提问, 你能得到的帮助将很少很少.

提问的地点和方式

第一个选择是提问的地点和方式.

人们经常给我发电子邮件, 在 LinkedIn 上给我发消息, 在 Twitter 上给我发私信, 在 #include <C++> Discord 上给我发私信等等, 希望从我这里得到个性化、即时而一对一的帮助.

这不是对我时间的很好利用. 我更喜欢在其他人能看到问题和答案的地方提供帮助. 这可以帮助更多的人. 它还使更多的人能够提供帮助——因此它可以提供更好的建议并帮助这些人学习和成长. 通常, 我能从其他人插的话中学到很多.

因此, 我鼓励您在 StackOverflow (如果你有适合该网站的、类似于一问一答形式的问题), 或者在 #include <C++> Discord 或其他公开场合上提问. 随着时间推移, 很多人都能看到问题和答案.

换位思考

其余的问题都在于提问者希望我做多少工作, 或者他们会为我省多少精力.

想一想你的第一个问题是怎么问的. 比较一下:

  • "这是包含了我项目中所有文件的一个压缩包, 你能告诉我它有什么问题吗?"
  • "这是一个在线编译器 (:godbolt:`Godbolt <jTY7jbrh4>`, Wandbox 等) 的链接, 在其中第 43 行有一个我不明白的编译器错误; 你能告诉我如何解决这个错误吗?"

指望着别人安装一些东西, 翻阅许多文件, 猜测你的问题, 然后解决你的问题并耐心向你解释这一切, 实在是太过分了. 至少要告诉别人你的问题到底是什么!

经常有人在寻求帮助时说 "它不工作" 或者 "这有什么问题" 一类的话, 而我面对这种问题时甚至不知道它是一个编译器错误、运行时错误还是一个运行正常但计算错误的答案. 所以举例来说, 在你发帖之前, 尽量有一个干脆利落的问题, 而不仅仅是 "救命啊, 它没正常运行".

向我们展示你的代码和错误信息, 并且千万不要是以截图的形式!

  • 你可以把代码复制到 :godbolt:`Godbolt <jTY7jbrh4>` 或者 Wandbox 这样的在线编译器中, 或者如果它少于 20 行左右, 你可以把它粘贴到聊天窗口中——但在这么做之前, 记得格式化代码.
  • 将错误信息复制并粘贴为文本, 从而使它们更加容易阅读并方便我们在向你解释的时候复制和粘贴部分内容.
  • 如果你因为代码太大或者因工作原因不能展示你的真实代码, 那就创造一个有相同问题的小例子并展示给我们.

无论你以什么形式提问, 永远不要随便地在聊天框里编写一些自认为展示了相同问题的代码, 然后每当人们指出缺少分号或未声明的变量时, 回答说: "啊对对对, 但那不是我真正的代码, 只是我输入的一些东西, 给你一个大致的概念."

编译器 (和运行环境) 是很挑剔的, 通过与你真正的代码大差不差的东西寻求帮助是毫无意义的.

.. seealso::

  - :doc:`/formatter/main` 中介绍了常见软件的格式化代码方法.

与答疑者对话

现在假设你设法与试图帮助你的人进行对话. 例如, 他们解决了那个编译器错误, 但现在你又有了另一个错误.

在这个过程中, 我经常向人们提出建议却被他们拒绝. 在我看来, 他们以为这些建议只是为了让他们自己未来更轻松, 因为他们总是说着 "等问题解决了, 我会这么做的" 之类的话. 于是我又不得不花费大量精力进行解释: 我希望他们现在就按我的建议来做, 以便我可以帮助他们解决问题.

这些建议包括:

写好变量名和函数名

如果你的变量都叫 :cpp:`i`, :cpp:`n`, :cpp:`c`, :cpp:`r`, :cpp:`s` 等等, 我不知道它们代表什么.

如果我要求你把这些改成 :cpp:`next`, :cpp:`rate`, :cpp:`total` 等或改成你自己想出来的其他名字, 那是因为如果没有一些帮助, 我无法理解你的代码 (我不知道你想解决什么问题).

好名字不是某一天才去做的事情, 你绝不该在代码正常运行后才去考虑它. 它们是你让别人能读懂你的代码的途径. 既然你要求我阅读这段代码, 那就应该让它变得可读.

使用调试器 (Debugger)

当我问 "你有没有在调试器中查看循环之前的 :cpp:`a` 的值?" 时, 回答 "我不知道如何使用调试器, 我今天没时间学习" 是结束我们对话的好方法.

真正的程序员会使用调试器. 我们可没在脑子里安装什么神奇的编译器模拟器, 可以读取代码并告诉你它是否通过编译, 而且我们也没有神奇的运行环境模拟器.

当然, 也许我一眼就能看出 :cpp:`a` 在循环之前是 :cpp:`0`, 这就是为什么它不能工作, 但在这种情况下我会告诉你. 更有可能的是, 我想让你快速检查一下它到底是不是 :cpp:`0`. 当你拒绝调试时, 你会使帮助你的工作变得更加困难. 你需要学习使用任何你可以使用的调试器, 并且你可能当天就会因学习了它节省很多时间, 从而弥补了你学习它的时间.

.. seealso::

  - :doc:`/debugger/main` 中以 Visual Studio 2022 为例解释了断点调试的使用.

添加一些测试

你不需要去学习整个单元测试框架. 但是, 如果你正在写一个函数来做某些事, 人为地算出它对简单的值会产生什么结果, 并写一个测试用例 (test harness) 来传递这些简单的值. 然后你可以调试这个测试用例来查看函数的返回值, 并确认它对简单的值是否能正确运行.

你可能是在原地反转一个字符串、计算斐波那契数列、计算事物的总和……无论如何, 你应该能够想出一些简单的测试案例, 并用它们来测试你的代码.

最终, 你应该在写代码的同时写测试. 这是你现在就该开始养成的好习惯.

int add(int lhs, int rhs) {
  return lhs + rhs;
}

void test_add() {                         // 测试用例
  std::cout << (4 == add(1, 3)) << '\n';  // 4 == add(1, 3) 则输出 1; 否则输出 0
}

拆解大块的东西

你不必 (译注: 也不应该) 做到完全的面向对象编程, 或者写纯粹的函数式程序, 但不要给我 1000 行代码, 让我把它装进我的脑子里. 写一些函数. 还有求你一定要添加一些注释和空行.

展示出你的代码结构, 这样它就不是一堵文字墙了.

发挥主观能动性

还有一些好习惯能让你变得更好:

试试别人告诉你的替换方法

很多时候, 遇到困难的人不想学习新东西. 我自己就经常遇到这种情况. 我已经很沮丧了, 我已经花了比我预计的更多的时间, 我不能理解其中哪怕一点, 我不希望你现在告诉我去学习别的东西.

不过, 多年来我积累了许多经验, 其中包括: 有时, 放弃所有那些一知半解的、不能真正发挥作用的乱七八糟的东西而去做一些更简单的事情, 是最好的前进方式.

如果有人告诉你 vector 在这里会更好并且提供了几行示例代码, 那么坚持己见而拒绝尝试并不能让你学会什么. 如果你所遇到的问题是因为你尝试手动管理内存却忘记了复制等等, 那么使用智能指针、或者完全放弃指针而使用栈上的对象, 将会使一大堆麻烦消失.

建议你尝试这样做的人知道这将节省多少精力. 你不知道, 所以你才会来寻求帮助. 一个初学者坚持用困难的且没有任何好处的方法做一件事, 做错了, 并且拒绝接受除了用这种困难方法写出的代码以外的任何帮助……这真的很令人沮丧.

我不想再用那种困难的方法写代码了: 凭什么我要为了你写那种代码?

如果帮助你的人告诉你, 你的代码按你的方式更难读, 那就试试那些对你不重要的东西

比如在构造函数中使用 :cpp:`:` 语法而不是在函数体内初始化成员, 或者添加一些 :cpp:`using` 语句: 这些对你来说可能并不重要, 但对于一个忙碌的帮助者来说, 让事情变得太难, 就可能意味着这个帮助者太 "忙" 了于是今天不会帮助你——甚至永远不会.

我不想教你养成坏习惯, 我不想教你等到问题解决后才 "把代码弄漂亮", 我也不想费心竭力去阅读难读的代码只因为你嫌麻烦不打算做正确的事情.

另外, 当一个人寻求建议, 但却从不接受任何建议, 因为他们确信这些建议实际上与他们的问题无关……最终, 建议者会停止提供建议——他们觉得就算建议了也毫无意义.

自己编写代码

如果我告诉你 "问题是你没有初始化 x", 不要要求我为你编辑代码, 发送给你新版本的函数或其他什么. 你需要明白你犯了什么错, 而这需要你自己纠正过代码才能明白.

如果你不明白如何解决别人告诉你的问题, 就问他们 "我如何解决这个问题"; 如果你不能理解他们的回答, 就说 "我不知道 [xxx] 是什么, 你能解释一下或展示给我看吗".

不要只问 "那一行代码改好后是什么样子的", 那感觉就像你在要求我为你打字.

不管是谁在和你说话, 都要和他协作

也许当你第一次问的时候, 某个人有一些想法, 这些想法很好. 但是当你在修改你的代码、验证是否有效的时候, 其他人也加入进来. 这很好. 这本就是群聊.

不要告诉他们你正在与 xxx 合作或类似的事情而拒绝他们的帮助. 考虑你得到的所有建议. 如果你正在和某人交谈, 然后他们不回复了, 这也没什么. 许多人会在等待电话会议开始时, 或在吃午饭时, 会突然进入群聊 5 或 10 分钟, 但不会呆太久. 他们也可能会离开自己的键盘.

不要开始骚扰对方让他们回来, 或者问他们是否有更多的想法, 或者私聊说你仍然没搞懂. 如果你仍然没搞懂, 你完全可以告诉整个房间或频道, 也许其他人会有一些想法. 你的问题可能会在一个小时左右的时间里与三个不同的人解决.

解决即是胜利!

结语

我知道, 这里给出了许多建议. 但问题在于, 如果你以正确的方式提问, 你就会从互联网上的陌生人那里得到很多帮助; 如果你问的方式不对, 大多数人只会耸耸肩, 说: "看起来你有一个问题." 然后继续网上冲浪.

他们甚至不会告诉你为什么他们不帮助你!

为了获得奇妙的免费帮助并真正加入社区, 你必须付出一些努力. 相信我, 这绝对值得!