如何设计你的第一个C++程序

在前期花一点时间思考如何设计程序将会带来更好的代码,并减少寻找和修复错误所需的时间。

现在,您已经学会了一些有关程序的基础知识,让我们更详细地看看如何设计一个程序。

当您开始编写程序时,通常会有某种想法,您希望为其编写程序。新程序员通常会困惑于如何将这个想法转化为实际的代码。但事实证明,您已经具备了许多您需要的问题解决技能,这些技能来自日常生活。

最重要的事情要记住(也是最难做到的)是在开始编码之前设计好您的程序。在许多方面,编程就像建筑一样。如果您尝试在没有遵循建筑计划的情况下建造房屋,会发生什么情况?除非您非常有天赋,否则您可能会得到一座存在许多问题的房屋:墙不直,屋顶漏水等等… 同样,如果在有一个良好的前进计划之前尝试编程,您可能会发现您的代码存在许多问题,您将不得不花费大量时间来修复本可以通过提前思考而完全避免的问题。

一点点的前期规划将会在长期内节省您时间。

在本课程中,我们将为将思想转化为简单的功能程序提供一个通用的方法。

设计步骤 1:定义目标

要编写一个成功的程序,首先需要定义您的目标是什么。理想情况下,您应该能够用一两句话来表述这一点。通常有必要将其表达为面向用户的结果。例如:

  • 允许用户组织一组姓名和相关电话号码。
  • 生成随机的地下城,以产生有趣的洞穴外观。
  • 生成具有高分红的股票推荐列表。
  • 模拟从塔上掉下来的球撞到地面所需的时间。

虽然这一步似乎很明显,但它也非常重要。最糟糕的事情是编写一个实际上不能做到您(或您的老板)想要的功能的程序!

设计步骤 2:定义需求

虽然定义问题有助于确定您想要的结果,但它仍然是模糊的。下一步是思考需求。

需求是一个复杂词汇,既包括解决方案需要遵守的约束条件(例如预算、时间表、空间、内存等),也包括程序必须展示的能力,以满足用户的需求。请注意,您的需求应该侧重于“什么”,而不是“如何”。

例如:

  • 电话号码应该被保存,以便以后可以检索。
  • 随机生成的地下城应该始终包含从入口到出口的通道。
  • 股票推荐应该利用历史价格数据。
  • 用户应该能够输入塔的高度。
  • 我们需要在7天内获得一个可测试的版本。
  • 程序应该在用户提交请求后的10秒内产生结果。
  • 程序在不到0.1%的用户会话中会崩溃。

一个单一的问题可能会产生许多需求,只有满足所有这些需求,解决方案才能被认为是“完成”的。

设计步骤 3:定义工具、目标和备份计划

当您是一个有经验的程序员时,通常在这一点上会发生许多其他步骤,包括:

  • 确定程序将在哪个目标架构和/或操作系统上运行。
  • 确定您将使用哪组工具。
  • 确定您是独自编写程序还是作

设计步骤 4:将复杂问题分解为简单问题

在现实生活中,我们经常需要执行非常复杂的任务。尝试弄清楚如何执行这些任务可能非常具有挑战性。在这种情况下,我们通常使用自顶向下的问题解决方法。也就是说,与其解决一个复杂的任务,不如将该任务分解为多个子任务,每个子任务都较容易解决。如果这些子任务仍然太难解决,它们可以进一步分解。通过不断将复杂任务分解为简单任务,最终您可以达到每个个体任务是可管理的,如果不是微不足道的程度。

让我们看一个例子。假设我们想要打扫房子。我们当前的任务层次结构如下:

  • 清理房子 清理整个房子是一个相当大的任务,难以一次完成,因此让我们将其分解为子任务:
  • 清理房子
    • 吸尘地毯
    • 清洁浴室
    • 清洁厨房 这更容易管理,因为现在我们有可以单独关注的子任务。然而,我们甚至可以进一步分解其中的一些:
  • 清理房子
    • 吸尘地毯
    • 清洁浴室
      • 擦洗马桶(呃!)
      • 洗手池
    • 清洁厨房
      • 清理台面
        • 清理台面
        • 擦洗水槽
      • 倒垃圾 现在我们有一个任务层次结构,其中没有一个特别困难。通过完成每个这些相对易于管理的子项,我们可以完成更难的整体任务,即清理房子。

创建任务层次结构的另一种方法是从底部开始。在这种方法中,我们将从一组简单的任务列表开始,并通过将它们分组来构建层次结构。

例如,许多人在工作日必须上班或上学,因此假设我们想要解决“去上班”的问题。如果有人问您早上从床上到工作中所做的任务,您可能会列出以下列表:

  • 挑选衣服
  • 穿衣服
  • 吃早餐
  • 去上班
  • 刷牙
  • 起床
  • 准备早餐
  • 骑自行车
  • 淋浴

使用自底向上的方法,我们可以通过寻找具有相似性的项目来将这些项目组织成一个层次结构:

  • 从床上到工作
    • 卧室事物
      • 关掉闹钟
      • 起床
      • 挑选衣服
    • 浴室事物
      • 淋浴
      • 穿衣服
      • 刷牙
    • 早餐事物
      • 泡咖啡或茶
      • 吃麦片
    • 交通工具事物
      • 骑自行车
      • 去上班

事实证明,这些任务层次结构在编程中非常有用,因为一旦您拥有任务层次结构,您本质上已经定义了整体程序的结构。顶级任务(在这种情况下是“清理房子”或“去上班”)成为 main()(因为它是您试图解决的主要问题)。子项成为程序中的函数。

如果发现其中一个项目(函数)太难实现,只需将该项目拆分为多个子项/子函数。最终,您应该达到程序中的每个函数都很容易实现的程度。

设计步骤 5:找出事件的顺序

现在,您的程序有了结构,现在是确定如何将所有任务连接在一起的时候了。第一步是确定将执行的事件顺序。例如,当您早上起床时,您会按照什么顺序完成上述任务?可能会是这样:

  • 卧室
  • 浴室
  • 早餐
  • 交通工具

如果我们正在编写一个计算器,我们可能会按照以下顺序执行:

  • 从用户获取第一个数字
  • 从用户获取数学操作
  • 从用户获取第二个数字
  • 计算结果
  • 打印结果

到了这一步,我们已经准备好进行实现。

实施步骤 1:概述您的主要职能

现在我们准备开始实施。上述序列可用于概述您的主程序。暂时不用担心输入和输出。

int main()
{
//    doBedroomThings();
//    doBathroomThings();
//    doBreakfastThings();
//    doTransportationThings();

    return 0;
}

或者在计算器的情况下:

int main()
{
    // Get first number from user
//    getUserInput();

    // Get mathematical operation from user
//    getMathematicalOperation();

    // Get second number from user
//    getUserInput();

    // Calculate result
//    calculateResult();

    // Print result
//    printResult();

    return 0;
}

请注意,如果您要使用这种“大纲”方法来构建程序,那么您的函数不会编译,因为函数定义尚不存在。将函数调用注释掉,直到准备好实现函数定义是解决此问题的一种方法(我们将在这里展示)。或者,您可以为函数创建存根(创建带有空函数体的占位符函数),以便您的程序可以编译。

实施步骤 2:实现每个函数

在这一步中,对于每个函数,您将执行三个操作:

  • 定义函数原型(输入和输出)
  • 编写函数
  • 测试函数

如果您的函数足够细粒度,那么每个函数应该都相当简单明了。如果某个函数看起来仍然过于复杂以至于无法实现,那么也许需要将其拆分成可以更容易实现的子函数(或者可能您按照错误的顺序进行了操作,需要重新查看事件的排序)。

让我们从计算器示例中的第一个函数开始:

#include <iostream>

// getUserInput函数的完整实现
int getUserInput()
{
    std::cout << "输入一个整数:";
    int input{};
    std::cin >> input;

    return input;
}

int main()
{
    // 从用户获取第一个数字
    int value{ getUserInput() }; // 请注意,我们在这里包含了代码来测试返回值!
    std::cout << value << '\n'; // 调试代码以确保getUserInput()正常工作,稍后我们将删除这部分

    // 从用户获取数学操作
    // getMathematicalOperation();

    // 从用户获取第二个数字
    // getUserInput();

    // 计算结果
    // calculateResult();

    // 打印结果
    // printResult();

    return 0;
}

首先,我们已确定getUserInput函数不接受任何参数,并将返回一个int值给调用者。这在函数原型中反映为返回值为int且没有参数。接下来,我们编写了函数的主体,其中包含了4个简单的语句。最后,我们在main函数中实现了一些临时代码,用于测试getUserInput函数(包括其返回值)是否正常工作。

我们可以多次运行此程序,使用不同的输入值,并确保程序在这一点上的行为符合我们的预期。如果发现某些内容不起作用,我们知道问题出在我们刚刚编写的代码中。

一旦我们确信程序在这一点上按预期工作,就可以删除临时测试代码,然后继续实现下一个函数(getMathematicalOperation函数)。在本课程中,我们不会完成整个程序,因为我们需要先涵盖一些其他主题。

记住:不要一次性实现整个程序。按步骤进行工作,每一步都要在继续之前进行测试。

实施步骤 3:最终测试

一旦您的程序“完成”,最后一步是测试整个程序并确保其按预期工作。如果它不起作用,请修复它。

编写程序时的建议

  • 从简单的程序开始。通常,新程序员对他们希望程序执行的所有任务都有一个宏伟的愿景。“我想编写一个带有图形、声音、随机怪物和地下城的角色扮演游戏,玩家可以访问城镇,出售在地下城中找到的物品”。如果您尝试从一开始编写过于复杂的东西,您可能会感到不知所措,因为您的进展不足。相反,尽量使您的第一个目标尽可能简单,确保它绝对可以实现。例如,“我想能够在屏幕上显示一个二维场”。
  • 逐步添加功能。一旦您的简单程序工作得很好,然后可以逐步添加功能。例如,一旦您能够显示您的场景,就添加一个可以四处走动的角色。一旦您可以四处走动,就添加可以阻碍您前进的墙壁。一旦有了墙壁,就用它们建造一个简单的城镇。一旦有了城镇,就添加商人。通过逐步添加每个功能,您的程序将逐渐变得更加复杂,而不会在过程中使您感到不知所措。
  • 专注于一个领域。不要一次性编写所有代码,也不要将注意力分散到多个任务上。专注于一项任务。拥有一个正常工作的任务和五个尚未开始的任务比拥有六个部分工作的任务要好得多。如果分散注意力,您更有可能犯错误并忘记重要的细节。
  • 随时测试每段代码。新程序员通常会一次性编写整个程序。然后,当他们首次编译它时,编译器会报告数百个错误。这不仅令人望而却步,如果代码不起作用,也可能很难找出问题所在。相反,编写一段代码,然后立即编译并测试它。如果它不起作用,您将知道问题出在哪里,而且很容易修复。一旦您确定代码可行,就转到下一个部分并重复。可能需要更长的时间来完成编写代码,但是当您完成整个任务时,整个代码应该正常工作,而且您不必花费两倍的时间来弄清楚为什么不起作用。
  • 不要投资于完善早期代码。一个功能(或程序)的第一稿通常不太好。此外,程序往往随着时间的推移而发展,因为您添加功能并找到更好的组织方式。如果您过早地投资于优化代码(添加大量文档、完全符合最佳实践、进行优化等),则在需要更改代码时,您可能会失去所有这些投资。相反,让您的功能最低程度地工作,然后继续前进。随着您对解决方案的信心增加,可以逐步添加更多的精细调整。不要追求完美 – 非平凡的程序从来都不是完美的,总会有更多可以做的事情来改进它们。达到“足够好”的水平,然后继续前进。
  • 优化可维护性,而不是性能。新程序员通常会花费太多时间考虑如何微调他们的代码(例如,试图弄清楚两个语句中哪一个更快)。这很少重要。大多数性能优势来自于良好的程序结构,使用适合手头问题的正确工具和功能,以及遵循最佳实践。额外的时间应用于改进代码的可维护性。查找冗余并删除它。将长函数拆分为较短的函数。用更好的东西替换笨拙或难以使用的代码。最终结果将是更容易改进和优化的代码(在确定确实需要优化的地方之后),以及更少的错误。我们在后面的课中提供了一些建议,帮助您在问题成为问题之前找到问题。

总结

许多新程序员会跳过设计过程(因为它似乎需要大量工作和/或不像编写代码那么有趣)。然而,对于任何不平凡的项目,遵循这些步骤将在长远来看为您节省大量时间。前期的一点规划可以节省后期大量的调试工作。

原创文章,作者:jkhxw,如若转载,请注明出处:https://www.jkhxw.com/design-your-first-cpp-programs/

(0)
上一篇 4天前
下一篇 3天前

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注