CppCon 2014 学习:0xBADC0DE

这段内容看起来像是调试信息或代码注释,夹杂了一些符号和表达bug的语句:

主要内容理解

  • 0xBADC0DE
    这是一个典型的“魔数”或标记,常用于表示程序中的错误状态或非法值。它看起来像“BAD CODE”的谐音,暗示这里可能出错了。
  • 符号 <:]{%>[]{}
    这些像是调试时打印的特殊符号,或者代码中用来表示某种状态、错误分支的符号占位。
  • “(empty) C++11 Lambda”
    这里可能指一个空的C++11 Lambda表达式,或是调试信息提示某处存在lambda但为空。
  • goto fail;
    代码中使用了goto fail;,这是一个常见的错误处理方式,跳转到错误处理代码块。可能暗示这里跳转到了错误路径。
  • “Should not have happened / But it did! / It’s a bug.”
    这说明程序遇到了不该出现的状态,确认这里是个bug。
  • “A bug can result from bad code.”
    直接点明bug的原因是代码写得不好。

综合理解

这段话传达的是:

  • 程序检测到一个异常或错误状态(标记为0xBADC0DE)。
  • 代码进入了错误处理(goto fail)。
  • 这是一个程序BUG,因为出现了不该出现的状态。
  • 可能是在处理某个空Lambda或其他特殊操作时出错。

你想了解的重点?

  • 这是某种程序错误的日志或示例,想帮你分析具体原因?
  • 需要示例代码中如何避免这种“goto fail”错误?
  • 还是想知道C++11 lambda相关的陷阱?

这段内容带点幽默和反思,结合代码错误和软件开发的现实。

0xBADC0DE
  • 这是程序中常用的“魔数”(magic number),用来标识错误、非法状态,谐音“BAD CODE”,暗示代码出错。
● ● ●
  • 这些符号一般用作占位符或强调,表现出现错误时的“空洞”或“警告”。
Your own code? Maybe the code of the person next to you? your boss? …
  • 这里在调侃和反思:这段“坏代码”可能是你写的,也可能是同事写的,甚至是老板写的。
  • 说明代码中出错,责任可能在任何人。
Weeks of coding can save you hours of planning!
  • 这是一句讽刺的话,意思是“花几周写代码,反而能省下几小时的规划时间”,其实是反话,提醒规划和设计的重要。
  • 出自“unknown programmer”(匿名程序员),强调很多bug是因为没有好好设计导致的。
Poor mans C++
  • “穷人的C++”,意思是用简单粗暴的方式写C++代码,而不是真正深入理解和用好这门语言。
  • 这里暗示写代码时可能没有充分利用C++的高级特性和良好实践。
(What me originally made think about this topic)
  • 这是作者自述,表达最初是因为“穷人的C++”概念触发了对代码质量和设计的思考。
Who is the poor man?
  • “穷人”指的是那些主要关注实现功能,而不是语言本身的人。
„A person whose main concern is not C++, C++ is seen in the role of a tool“
  • 这句话定义了“穷人的C++”:

    主要关注的是完成任务,而非精通C++语言本身,把C++当作一个工具而已。

综合理解

这段话是在:

  • 用“0xBADC0DE”这个“坏代码”魔数,引出对代码质量的反思。
  • 调侃说坏代码可能出自自己、同事或老板,强调代码责任无处不在。
  • 用一句幽默的反话提醒大家“规划比写代码更重要”。
  • 提到“Poor man’s C++”,暗指很多程序员用C++只是为了完成任务,而不是深入掌握语言。
  • 这其实是在呼吁:写代码要用心,不能只把C++当成“随手可用的工具”,否则容易写出“坏代码”。

“穷人的C++”概念,并且很人性化地说明了这类开发者的处境和面临的问题,同时也传递了积极的态度。:

逐句分析

This is just one category
  • 这是“穷人的C++”中的一种情况,说明并非所有人都是一样的。
The poor man usually is not poor
  • “穷人”并不是经济上的穷,而是指技术或知识层面的“穷”。
just not a C++ Expert, basic („poor“) C++ knowledge
  • 这些人往往不是C++专家,只具备基础甚至“贫瘠”的C++知识。
Often is an expert but in a different domain e.g. scientists, other programming languages
  • 这些人可能在其他领域非常专业,比如科学家,或者精通其他编程语言,但对C++不熟悉。
Its maybe not even his fault
  • 这可能并不是他们的错。
As C++ is only seen as a tool, time to improve skills is limited, „But this works too“
  • 因为他们只是把C++当成工具,提升C++技能的时间有限;常见心态是“这也能用”。
Copy & Paste Evolution
  • 他们经常采用复制粘贴代码,再稍微修改满足需求的方式。
C & P old solution, Mutate the things you need, Old code can live very long
  • 旧代码经过多次复制和改动,能存活很久。
Typical Problems
  • 常见问题:
  • poor design knowledge —— 设计能力不足
  • mixing old techniques and C into C++ —— 混合使用旧C语言技巧和C++代码
  • C with Classes —— 把C代码简单封装成类
  • Old C++ Books —— 过时的学习资料影响
  • new Problems —— 新问题出现,比如内存泄漏
  • clash of styles —— 编程风格冲突
  • loops vs. algorithms —— 手写循环与使用算法库的冲突
There is hope!
  • 但依然有希望!
The ‘poor man’ can be educated! as experts, they’re willing to learn
  • “穷人”是可以被教育和提升的,他们通常也很愿意学习新东西。

总结

这段文字非常贴切地描述了:

  • 许多程序员因为背景不同或资源有限,导致C++基础薄弱。
  • 他们多半不是C++专家,但在其他领域有专业技能。
  • 由于C++只是工具,他们往往只能用“能用就行”的心态写代码,常常依赖旧代码和复制粘贴。
  • 这些原因导致代码质量参差不齐,出现设计不足、内存问题、风格不统一等。
  • 好消息是,他们乐于学习改进,经过教育和训练,完全能成长为真正的C++开发者。

这段内容继续深入讲述“0xBADC0DE”问题,也就是糟糕代码的典型表现和原因,特别是在C++开发中常见的挑战:

逐句理解

  • 仍然有希望解决这些问题!
  • 但希望难以实现。
  • 主要是因为工作量大,压力大。
  • C++不是他们的核心关注点。
  • “没坏为啥修?”——一种常见的心态,导致代码问题被忽视。

0xBADC0DE 的示例问题(糟糕代码)

  • 新出现的更多问题。
  • 多层次的工程问题。
  • 类设计问题。
  • 巨型类或函数——代码臃肿,复杂难维护。
  • 初始化模式可能设计不佳。
  • 可能暗示资源有限,影响代码质量。

内存泄漏 Memory leaks

  • 过度使用new操作符(动态分配内存)。
  • 很多人忘了释放内存(deletedelete[])。
  • 类似Java风格的C++写法,没有显式释放内存。
  • 这并不总是致命问题,但容易积累问题。

解决思路

  • 引入所有权(ownership)概念能减少这类问题。
  • 使用智能指针(如std::unique_ptrstd::shared_ptr)管理内存。
  • 使用有明确所有权层级的对象体系,比如Qt的QObject体系。

总结

  • “0xBADC0DE”代表的是复杂且容易出错的代码状态。
  • 代码中常见问题是设计臃肿,内存泄漏,糟糕的初始化模式。
  • 这些问题一部分是因为工作量大、非核心语言和“没坏为啥修”的心态导致。
  • 过度使用裸指针new,缺少释放,导致内存泄漏。
  • 采用智能指针和清晰的所有权模型是解决内存问题的有效手段。
  • 有系统的类设计和对象层级管理有助于提升代码质量。

Qt中关于对象(尤其是对话框MyDialog)的不同创建和销毁方式,重点是内存管理和资源泄漏问题。结合你之前提到的“内存泄漏”和“所有权”概念:

代码片段和解释

void MainWindow::on_action()
{
  MyDialog* dlg = new MyDialog(0, "bad code");
  if(dlg->exec()) ...
}
  • 问题点new了一个对话框,父对象指针传入0(nullptr)。
  • 后果:对话框没有父对象,Qt的对象树不会管理它的生命周期。
  • 结果:执行完exec()后没有删除dlg,导致内存泄漏(Memory leak)。
  • 同时,也可能导致资源泄漏,比如对话框占用的窗口资源不会自动释放。
void MainWindow::on_action()
{
  MyDialog* dlg = new MyDialog(this, "bad code");
  if(dlg->exec()) ...
}
  • 改进一点dlg的父对象是this(主窗口)。
  • Qt对象树会自动删除子对象,但这里有问题:
  • exec()是模态对话框执行,会阻塞代码,但dlg指针依然没被显式delete
  • 不安全:如果MainWindow活得比dlg短,可能提前删除,或者如果没有等待事件处理,dlg内存依然未释放。
  • 可能依然有内存或资源泄漏风险
void MainWindow::on_action()
{
  MyDialog dlg(this, "ok if parent lives longer");
  if(dlg.exec())...
}
  • 局部对象方式dlg是栈上对象,作用域结束时自动销毁。
  • 父对象是this,保证对话框依赖主窗口。
  • 只要父对象活得比对话框长,这样写是安全且推荐的方式
  • 优点:无需手动释放,自动调用析构函数,避免内存泄漏。
void MainWindow::on_action()
{
  auto *dlg = new MyDialog(this, "noexcept");
  …
  dlg->deleteLater(); // Qt Framework specific
  // pending events are processed
}
  • 动态分配 + 智能释放
  • deleteLater()是Qt特有方法,告诉Qt事件循环“在安全时机删除这个对象”。
  • 避免了立即删除可能带来的崩溃(比如当前还在使用这个对象)。
  • 这是一种线程安全且合理的释放动态对象的方式

总结

  • 不要new不带父对象的Qt对象,除非你自己管理生命周期,否则容易内存泄漏。
  • 优先用**栈对象(局部变量)**管理对话框,简单且安全。
  • 如果必须用new,要么负责delete,要么使用deleteLater()让Qt帮你安全删除。
  • Qt的父子关系机制很强大,合理利用可以减少资源泄漏风险。

内存泄漏(Memory leaks)以及解决内存管理问题的智能指针(smart pointers)概念,结合RAII(资源获取即初始化)技术。

重点理解:

  • Memory leaks(内存泄漏):程序分配了内存但没有释放,导致内存浪费甚至程序崩溃。
  • Smart pointers(智能指针):C++中用来自动管理动态分配对象生命周期的工具,避免手动newdelete带来的错误。
    • 例如:std::unique_ptrstd::shared_ptrstd::weak_ptr
  • RAII(Resource Acquisition Is Initialization):资源(如内存、文件句柄)在对象构造时获取,在析构时释放,利用C++对象的生命周期自动管理资源,防止泄漏。
  • 现实问题
    • 很多人对RAII和智能指针等技术仍不熟悉。
    • 有些人“指针狂热症”,过度使用裸指针(raw pointers)或者智能指针,尤其是shared_ptr的滥用,导致复杂且难以维护的代码。
  • 推荐的指针管理层次
    • 栈对象(Stack) > 智能指针(Smart pointer) > 裸指针(Raw owning pointer)
    • 意思是:尽量用栈变量(局部对象)管理资源,次之用智能指针,最后才用裸指针(且必须非常小心管理它的生命周期)。

总结

  • 智能指针是防止内存泄漏的利器,但用不好也会带来新问题。
  • 学习并应用RAII理念,优先使用栈对象或智能指针,避免裸指针。
  • 理解不同智能指针的语义和适用场景,避免shared_ptr滥用导致性能和设计问题。

重构(Refactoring),特别是在代码中引入智能指针时遇到的挑战和经验。

理解重点:

  • 重构: 对已有代码结构进行改进,但不改变外部行为,目的是提升代码质量、可维护性和性能。
  • 引入智能指针:
    • 用智能指针替代裸指针,自动管理内存,减少内存泄漏风险。
    • 但在复杂系统中,指针之间的相互依赖(interdependencies)可能使得引入智能指针变得困难。
  • 指针滥用(Pointer overusage) vs 智能指针滥用(Smart pointer overusage)
    • 过度使用裸指针容易出错,导致内存问题。
    • 过度或不合理使用智能指针也会带来性能问题,甚至导致程序变慢。
  • 实际经验:
    • 有时为了保证性能,部分关键程序不使用智能指针(比如delete操作),反而更快。
    • 说明智能指针虽好,也需要结合实际场景,权衡性能和安全。

总结

  • 引入智能指针时要小心,不要盲目全部替换,要考虑指针间关系和性能影响。
  • 重构是个逐步的过程,需要持续测试和验证。
  • 不能把智能指针当成万能钥匙,合理使用才是关键。

项目维护和代码演进中的现实情况,特别是在多层次工程项目里:

理解要点:

  • 分成多部分(n Parts)
    大项目通常会随着时间分成多个模块或层次,代码历史悠久且庞杂。
  • 示例:有较长历史的项目
    • 经常有老代码区域是“别碰”的,怕改坏。
    • 不是去彻底重构(refactor)老代码,而是倾向于在老代码基础上加新层,避免破坏已有功能。
  • 文档差(Poor documentation)
    老代码缺乏文档,维护难度大,重构成本高。
  • 工程层次(Layers of Engineering)优先级
    现实中维护工作优先级通常是:
    1. 新增功能 >
    2. Bug修复 >
    3. 重构 >
    4. 文档完善
      也就是说,文档往往是最后才考虑的,导致文档跟不上代码变化。

总结

  • 长期项目中,维护重点往往是“赶进度”,而非理想的代码重构或文档补充。
  • 新功能和修复优先,重构和写文档反而变成“次要任务”。
  • 这是一种常见的技术债务现象。

这段内容主要讨论类设计(Classdesign)中常见的一些问题和挑战:

理解要点:

  • Monster classes(巨兽类)
    类设计过于庞大、复杂,一个类承担了太多职责,代码难以维护和理解。
  • Dependency Hell(依赖地狱)
    类与类之间的依赖关系过于复杂和紧密,导致修改一个类会引发连锁反应,增加维护难度。
  • OOP Overusage(面向对象过度使用)
    过度使用继承、多态等面向对象特性,反而让代码结构臃肿、难以理解。
  • Interface vs. Implementation(接口与实现)
    接口设计不清晰,或者接口和实现混在一起,导致代码耦合度高,难以扩展和替换。
  • Example(示例)
    这里提示有具体示例,通常是展示上述问题的实际代码案例。

总结

  • 良好的类设计应避免“巨兽类”和过度依赖。
  • 要合理划分接口和实现,降低耦合。
  • 面向对象设计要适度,避免滥用复杂特性。

这段代码展示了类设计中 方法重载和代码复用 的一个典型问题和解决方案,特别是关于 validate 方法的设计。

代码分析与理解:

1. 问题示例(Problem)
class Parameter {
public:
    virtual bool validate(FieldID id) { return true; }   // 以 FieldID 验证
    virtual bool validate(QString fieldname) { return true; }  // 以字段名验证
};
class MyParameter : public Parameter {
public:
    virtual bool validate(FieldID id) {
        /* 长的验证逻辑 */
    }
    virtual bool validate(QString fieldname) {
        return true;  // 这里简单返回true,没有调用id版本的验证
    }
};
  • 问题点
    MyParameter 重载了两个 validate 函数,但 validate(QString) 没有调用更详细的 validate(FieldID),导致验证逻辑重复,甚至可能被忽略。
2. 解决方案(Solution)
class Parameter {
public:
    virtual bool validate(FieldID id) {
        /* 长的验证逻辑 */
    }
    virtual bool validate(QString fieldname) {
        return validate(name2fieldID(fieldname)); // 把字符串转换成FieldID,然后调用同一个验证函数
    }
};
  • 改进点
    统一验证逻辑放在 validate(FieldID) 中,validate(QString) 只负责做转换,然后调用前者,实现了代码复用和维护方便。

总结

  • 避免代码重复:不同重载函数应该共享核心逻辑,避免验证逻辑写两遍或不一致。
  • 接口设计清晰:用一个统一的验证函数实现具体逻辑,其他重载函数做转换调用。
  • 继承时保持一致性:子类只需覆盖核心验证函数,避免子类遗漏调用。
    理解:
    这段内容是在讲类设计中的方法重载和继承问题,特别是validate函数的设计和实现。

问题描述(Problem):

  • 基类 Parameter 定义了两个重载的 validate 函数:
    • validate(FieldID id) 用于根据枚举类型 FieldID 验证字段。
    • validate(QString fieldname) 用于根据字符串字段名验证。
  • 子类 MyParameter 继承自 Parameter 并重写了 validate(FieldID id),实现了较复杂的验证逻辑。
  • 但子类没有重写 validate(QString fieldname),或者重写了却没调用重载的validate(FieldID),这样调用 validate(QString) 时验证逻辑会被跳过或者不正确,造成错误。

解决方案(Solution):

  • 在基类中,实现 validate(QString fieldname) 调用 validate(FieldID),通过 name2fieldID(fieldname) 将字段名转换为 FieldID
class Parameter {
public:
  virtual bool validate(FieldID id) {
    // 这里写具体的验证逻辑
  }
  virtual bool validate(QString fieldname) {
    return validate(name2fieldID(fieldname));  // 转换后调用统一的验证函数
  }
};
  • 这样子类只需要重写 validate(FieldID id),而不必担心 validate(QString),避免代码重复,也保证了验证逻辑的一致性。

核心思想:

  • 统一核心逻辑放在一个函数(这里是validate(FieldID)),其他重载函数调用这个核心函数,避免逻辑分散和代码重复。
  • 子类继承时只需重写核心函数,降低出错风险。
  • 代码更易维护和扩展。
    你需要的话,我可以帮你写更详细的示例代码。
class Parameterpublic : virtual bool validate(FieldID id) {
    return true;
};  // FieldID is an enum
virtual bool validate(QString fieldname) { return true; }class MyParameter : public Parameter  // Problempublic : virtual bool validate(FieldID id) { /*long validation*/ }
// don't forget to fix your code!class Parameter {  // Solution
public:
    virtual bool validate(FieldID id) { /*long validation*/ }
    virtual bool validate(QString fieldname) { return validate(name2fieldID(fieldname)); }

类设计中的方法重载和继承问题,特别是validate函数的设计和实现。

问题描述(Problem):

  • 基类 Parameter 定义了两个重载的 validate 函数:
    • validate(FieldID id) 用于根据枚举类型 FieldID 验证字段。
    • validate(QString fieldname) 用于根据字符串字段名验证。
  • 子类 MyParameter 继承自 Parameter 并重写了 validate(FieldID id),实现了较复杂的验证逻辑。
  • 但子类没有重写 validate(QString fieldname),或者重写了却没调用重载的validate(FieldID),这样调用 validate(QString) 时验证逻辑会被跳过或者不正确,造成错误。

解决方案(Solution):

  • 在基类中,实现 validate(QString fieldname) 调用 validate(FieldID),通过 name2fieldID(fieldname) 将字段名转换为 FieldID
class Parameter {
public:
  virtual bool validate(FieldID id) {
    // 这里写具体的验证逻辑
  }
  virtual bool validate(QString fieldname) {
    return validate(name2fieldID(fieldname));  // 转换后调用统一的验证函数
  }
};
  • 这样子类只需要重写 validate(FieldID id),而不必担心 validate(QString),避免代码重复,也保证了验证逻辑的一致性。

核心思想:

  • 统一核心逻辑放在一个函数(这里是validate(FieldID)),其他重载函数调用这个核心函数,避免逻辑分散和代码重复。
  • 子类继承时只需重写核心函数,降低出错风险。
  • 代码更易维护和扩展。

几个关于**类设计(Classdesign)**中的重要问题和建议,理解如下:

1. Non Virtual Interfaces(非虚接口)

  • 这是一种良好的面向对象设计模式(来自“Gang of Four”设计模式书),但在实际应用代码中很少见。
  • 它的核心思想是:
    • 基类定义非虚函数作为接口,统一调用流程。
    • 通过这些非虚函数调用一个或多个虚函数来完成具体操作。
  • 这样做的好处是可以在基类控制调用顺序和公共逻辑,减少子类出错,提高代码一致性和可维护性。

2. Monster Classes 和 Monster Methods(怪兽类与怪兽函数)

  • Monster Classes 指的是功能过于庞大、承担职责过多的类,导致代码难以维护。
  • 产生原因之一是层层叠加的特性和不断增加的新功能,导致类臃肿。
  • 类似的,Monster Methods 指的是那些非常长、复杂的函数,通常有很多条件分支(switches)和复制粘贴的代码。
  • 建议用工具检测函数的代码行数(LoC,Lines of Code)来评估是否需要重构。

3. Init ‘Pattern’(初始化模式)

  • 许多代码中,会有专门的初始化函数 init() 用来完成对象构造后的初始化工作,特别是调用虚函数的场景。
  • 这通常是因为构造函数中调用虚函数可能导致未定义行为或错误,所以选择延迟初始化。
  • 建议
    • 尽量用构造函数完成初始化,避免用单独的init函数。
    • 如果确实需要复杂初始化,可以用工厂函数或静态构造方法,并且将构造函数设为私有。
    • 遵守C++的Rule of Zero/Three/Five,正确实现拷贝构造、赋值操作符和移动操作符。
  • 案例提到Samsung的Bada SDK中也有类似“init模式”的用法。

4. 关于Money类型的建议

  • floatdouble来存储现金金额会导致精度丢失,可能会“丢失一分钱”。
  • 建议定义一个Money类,内部用整数(比如以分为单位)来存储金额。
  • 这样避免精度问题,使得金额计算更准确。

总结

  • 采用非虚接口设计模式,保证接口统一和安全。
  • 避免类和函数变成“怪兽”,适时重构。
  • 尽量用构造函数完成初始化,避免不安全的初始化模式。
  • 针对特定领域数据类型(如货币)设计专用类型,避免使用浮点数带来的问题。

这段内容介绍了**反模式(Anti-Patterns)**和设计模式的关系,并列举了一些常见的反模式,理解如下:

1. 设计模式(Design Patterns)

  • 设计模式是软件设计中被公认的、有效的解决方案集。
  • Gang of 4(GoF)是设计模式领域的经典著作,系统总结了多种设计模式。

2. 反模式(Anti-Patterns)

  • 反模式是指在软件设计或开发中常见的不良做法或错误习惯,它们可能导致代码难以维护、扩展和理解。
  • 反模式通常是开发中遇到的问题的“坏解决方案”,应尽量避免。

3. 常见反模式例子

  • Singleton(单例模式)
    • 虽然单例是一种设计模式,但滥用时容易导致代码难以测试、隐藏依赖,变成反模式。
  • God Objects(上帝对象)
    • 指一个类承担了过多职责,控制了太多功能,导致代码臃肿和耦合度高。
  • Monster Classes(怪兽类)
    • 类功能庞大,方法复杂,和God Object类似,是代码难以维护的根源。
  • OO Overuse(过度面向对象)
    • 过度使用面向对象概念,可能导致设计复杂、性能问题或不必要的抽象。
  • C++11/14: new/delete滥用
    • 新标准引入了智能指针和资源管理,仍使用裸指针和手动管理内存是一种反模式。

4. Antipattern Catalog

  • 表示有一份反模式的目录或列表,用于学习和避免常见的设计错误。

总结

这段内容强调:

  • 设计模式是好的实践,反模式是坏的实践。
  • 开发中要学会辨别和避免反模式,合理使用设计模式。
  • 特别在C++中,要利用好现代语言特性,避免传统的错误用法。

宏(MACROS)的弊端如何面对坏代码 以及 重构的现实,理解如下:

1. MACROS are EVIL(宏是有害的)

  • 宏是一种预处理指令,虽然强大,但带来很多问题,比如:
    • 缺少类型检查,容易出错
    • 调试困难,因为宏展开后代码不直观
    • 影响代码可读性和维护性
  • 宏的问题“就在你附近的代码库”,也可能出现在你未来的工作中。

2. Dealing with / Using bad code(如何处理坏代码)

  • 面对坏代码,有三种常见方式,优先级从高到低是:
    • Fixing(修复):尽可能修复代码中的问题。
    • Dealing(应对):当无法修复时,至少要知道如何安全使用它。
    • Using(使用):如果连应对都做不到,只能继续用现有的坏代码。
  • 现实情况中往往是:
    Fixing < Dealing < Using
    也就是说,使用坏代码的情况最多,修复的最少。

3. Fixing

  • 尽可能修复代码,但不要变成“堂吉诃德”——不能无谓地对抗“风车”浪费精力。
  • 坏代码可能只是表象,真正的问题可能更复杂(比如架构问题、设计问题)。
  • 有时候你无法修复代码,只能接受事实,学会如何安全地“应对”它。

4. On Refactoring(关于重构)

  • 引用了Martin Fowler(重构大师)在OOP 2014会议上的观点。
  • 任何代码只要被用到,就一定存在。坏代码无处不在。
  • 修复坏代码(重构或重写)并不总是可行,尤其是当代码库庞大、复杂或者你不了解所有细节时。
  • 新代码或者未知部分可能更容易重构,但遗留代码要谨慎对待。

总结

  • 宏滥用是坏代码的一大来源。
  • 面对坏代码,最理想是修复,但现实中更多是应对和继续使用。
  • 重构很重要,但不是万能,受制于时间、人力和项目状况。
  • 关键是认识坏代码,理性处理,持续改进。

如何应对坏代码,主要提到了利用一些工具和手段来辅助分析和改善代码质量:

Dealing with bad code(应对坏代码)

  • Static code analysis(静态代码分析)
    • 利用工具自动分析代码,找出潜在问题,减少人工检查的工作量。
    • 推荐工具:
      • CppCheck(开源,专门为C++设计)
      • Clang Static Analyzer(基于Clang的静态分析器)
      • 商业静态分析工具(功能更强大,但一般收费)
    • 静态分析工具可以帮你快速获得代码质量的第一手概览,告诉你哪些地方可能需要修复。
  • Clang modernize
    • 用来自动将旧的C++代码现代化,比如从C++98升级到C++11/14,提升代码质量和可维护性。
  • Documentation(文档)
    • 使用 doxygen + graphviz 生成文档和代码依赖图,有助于理解代码结构和调用关系,方便维护。

总结

  • 面对旧代码和坏代码,不要只靠人工判断,要借助静态分析工具和自动化工具。
  • 生成良好的文档帮助你和团队更好地理解和维护代码。

如何使用坏代码时的应对策略预防措施,以及关于代码组织和库的建议:

Dealing with using bad code(应对使用坏代码)

  • 有时候,坏代码无法立刻修复,但我们可以学会应对:
    • 不要让坏代码继续扩散,避免影响新的代码和模块。
    • 把坏代码限制在“安全范围”内,比如封装、模块化,减少它对其他部分的影响。
    • 尽量在之后找时间修复它,不要放任不管。

Prevention(预防)

  • 教育团队和同事,包括管理层,让大家都意识到代码质量的重要性。
  • 分析并改进团队代码质量,持续提升编码水平。
  • 更新公司内部的C++学习资料,避免使用过时的技术和思维方式。
  • 避免重复造轮子,多使用已有的成熟库,提升效率和质量。

关于库和模块化

  • 建议以模块/库的形式开发代码,即使是应用程序代码也如此。
  • 这样做的好处:
    • 强制思考和定义清晰的接口(Interface),有利于代码复用和维护。
    • 降低耦合度,方便团队协作和独立开发。
      总结:
  • 坏代码不可怕,关键是要管理和控制它。
  • 通过教育和标准化流程,预防坏代码产生。
  • 模块化开发和使用库是良好的软件工程实践。

将你的应用程序模块化(Modularize your Application),从而提升可维护性、可测试性和代码质量

原始结构(不推荐):

YOUR APPLICATION
└── Libraries
    └── C++ Standard Library, Qt, Boost, ...

这种结构意味着你的整个应用程序是一个大块代码,直接依赖库,缺乏清晰的分层或模块划分,不利于测试和重用。

改进结构(推荐):

YOUR APPLICATION (Stub/Thin Shell)
├── YOUR UNIT TESTs
├── Application Layer of Libraries
│   └── Your domain-specific logic
├── Libraries
│   └── Your utilities, helpers, core logic
└── C++ Standard Library, Qt, Boost, ...

含义解析:

  • YOUR APPLICATION Stub:只是一个“壳子”,负责拼装和调用模块(如 main() 函数),不含业务逻辑
  • Application Layer of Libraries:你应用的主要逻辑模块,这里是可重用和可测试的代码。
  • YOUR UNIT TESTs:专门针对 Application Layer 的测试,不需要依赖 GUI 或真实的应用上下文
  • Libraries:你自己写的或外部使用的工具库。
  • 标准库/Qt/Boost 等:外部依赖。

为什么这样更好?

更容易测试(测试逻辑代码,而不是界面或框架)
更高的模块复用性(逻辑不被 main() 或 GUI 绑定)
更易于维护和替换(不同层之间有清晰边界)
更清晰的架构(可读性和可扩展性都更强)
总结:
应用程序应该是模块的组合,业务逻辑不应该紧耦合在主程序或 UI 中。拆分为库 + 应用壳 + 测试,是专业软件架构的标准做法。

这部分内容深入探讨了 “Bad Code Culture”(糟糕代码文化) 在企业和开发环境中的根本原因及其后果,尤其是在 C++ 行业中常见的问题。

总结核心思想:

0xBADC0DE 的根源不仅仅是程序员
  • 很多“坏代码”并不是程序员本人的错,而是 工作环境、流程和管理结构的问题
  • 管理层(产品经理、项目经理)常常不理解软件开发本身,导致以“功能优先”为目标的开发节奏:
    新功能 > 修 bug > 重构 > 文档 > 测试
    
    这会让代码质量越来越差,而团队总是忙于“救火”。

测试文化缺失的后果

  • “我们当然测试啊” → 实际上并没有写单元测试。
  • 测试不是行业标准。
  • 工具和 IDE 并不默认集成测试支持(比如 VS、Qt Creator 等)。
  • 教程和书籍也鲜少包含高质量测试示例。
有哪些可用测试框架?
  • boost::test
  • Google Test / Google Mock
  • CppUnit
  • Catch (轻量、现代 C++ 风格)

不健康的 IT 行业现象

  • 长时间高强度、低质量的工作环境让人筋疲力尽。
  • 软件开发者应该拥有更健康的工作方式:
    • 可维护、可测试的代码
    • 拒绝“写完交付就算完事”的文化
    • 如果改变不了环境,那就换工作!

提倡的思维方式

  1. 优先使用库代码而不是写“应用粘合逻辑”

    库代码更容易测试、重用、维护

  2. 建立良好的工程文化:

    • 支持测试、重构和文档
    • 提倡模块化开发
    • 培训管理者和非技术同事理解“代码质量”的长期价值
  3. 倡导变革,而不是忍受坏习惯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值