罗剑锋的 C++ 实战笔记
罗剑锋
前奇虎 360 技术专家,Nginx/OpenResty 开源项目贡献者
34779 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 32 讲
结束语 (1讲)
罗剑锋的 C++ 实战笔记
15
15
1.0x
00:00/00:00
登录|注册

05 | 面向对象编程:怎样才能写出一个“好”的类?

你好,我是 Chrono。
如果按照前几节课的惯例,今天应该是讲运行阶段的。但是,运行阶段跟前面的编码、预处理和编译阶段不同,它是动态的、实时的,内外部环境非常复杂,CPU、内存、磁盘、信号、网络套接字……各种资源交织在一起,可谓千变万化(正如我在第 1 节课里所说,每一个阶段的差异都非常大)。
解决这个阶段面临的问题已经不是编程技术了,更多的是要依靠各种调试、分析、日志工具,比如 GDB、Valgrind、Systemtap 等。
所以,我觉得把这些运行阶段的工具、技巧放在课程前面不是太合适,咱们还是往后延一延,等把 C++ 的核心知识点都学完了,再来看它比较好。
那么,今天要和你聊哪些内容呢?
我想了想,还是讲讲“面向对象编程”(Object Oriented Programming)吧。毕竟,它是 C++ 诞生之初“安身立命”的看家本领,也是 C++ 的核心编程范式。
不管我们是否喜欢,“面向对象”早就已经成为了编程界的共识和主流。C++、Java、Python 等流行的语言,无一不支持面向对象编程,而像 Pascal、BASIC、PHP 那样早期面向过程的语言,在发展过程中也都增加了对它的支持,新出的 Go、Swift、Rust 就更不用说了。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《罗剑锋的 C++ 实战笔记》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(72)

  • 最新
  • 精选
  • eletarior
    关于老师的思考题,我个人的想法是本节的知识点还不足以写一个好的类,文中编码准则和常用技巧里的介绍只是在编码层面给出了建议,但是缺少方法论。少用继承,多用组合,这个建议可以再扩展深入点,比如有的鸟不会飞的例子,其实可以将Fly从Bird类抽离出来,将Fly实现成一个独立的接口类,和Bird类进行组合。 另外既然面向对象的核心是 抽象 和封装,封装可以不言自明,但是抽象是个啥,没有言明,抽象具体到编码里,其实还是需要虚基类和继承语法的。 总而言之,本篇基本是术,而缺少道的深度,所以看罢全文,我还是写不出一个“好”的类。 当然,缺少设计模式思维才是写好一个类最大的障碍,设计模式大部分都是要基于继承关系的,所以老师提到的少用继承,我想并不是说继承不好,而是别使用很多层次的继承,造成不必要的风险和维护难度吧。

    作者回复: 1.我的理解,抽象就是“建模”,用类在代码里建立一个现实对象的映射。 2.设计模式更强调对象组合,而不是继承,类模式很少。 3.继承是一种“硬”代码复用,关系比较强,不如组合灵活,我建议少用。当然这还是要建立在对继承等特性有比较深刻认识的基础上。

    2
    30
  • 甘俊
    老师您好,这一段有点没看明白,能举个例子体现explicit的作用么? 因为 C++ 有隐式构造和隐式转型的规则,如果你的类里有单参数的构造函数,或者是转型操作符函数,为了防止意外的类型转换,保证安全,就要使用“explicit”将这些函数标记为“显式”。

    作者回复: 比如说,有个构造函数A(int x),那么,A a = 1,这里就会有一个隐式构造。大多数时候这个不是问题,但有的时候会导致意外的转换。 使用explicit可以禁止隐式转换,防止意外错误,总是显式的构造,更加安全。

    5
    20
  • Jason
    老师讲的很实用,读完很有收获,赞。小贴士里面提到的5,耳目一新,确实很有道理,其他语言如Java、C#、Python的源码文件都是一种类型,只有咱们c++是头文件和实现文件。曾经有Java同事跟我闲聊,你们C++这个头文件啊,鸡肋,我楞了一下,思考了几秒钟,竟然没有反驳的理由。

    作者回复: 因为C++继承C,而C这么做是有历史原因的,当时的计算机性能弱内存小,头和实现分离才方便处理。 现在的计算机性能大幅度提升,所以这种方式也就没有太多必要了,我建议尽量用hpp的方式,和其他语言保持一致。

    9
    15
  • robonix
    定义一个新的名字空间,把内部类都“提”到外面,降低原来类的耦合度和复杂度。老师,这句话没看懂,能加一个简单的代码示例吗?

    作者回复: 大概就是这样 ~~~ namespace xxx { class inner_class {...}; class big_class {...}; } ~~~

    3
    13
  • Eglinux
    在 .h 中将类的定义和实现写在一起,这样不是默认所有成员函数都内联了吗?

    作者回复: 是的,但是否内联是由编译器决定的,通常只有小的函数才会内联,大的函数不会内联,因为反而成本高不划算。

    10
  • _smile滴水C
    课程让我醍醐灌顶,请教下老师关于成员变量初始化的问题,记得以前启蒙老师反复强调不要试图在头文件分配内存给变量赋值,至今为止任不明白为何?难道为了include的时候不会有额外内存开销吗?

    作者回复: 头文件会被多个源文件包含,所以在头文件里声明变量就会导致变量被定义了多份,导致编译错误。 但头文件里的类只是定义/声明,并不是实体,所以类的成员变量是没有任务问题的。 回答的可能不是太准确,有不清楚的地方可以再问。

    6
    9
  • 軟件賺硬幣
    罗老师,我看标准库和boost库很多继承都超过3层哦。比如iostream里面的,ios_base到ios,再到istream,再到iostream(同时继承ostream),再到fstream。用了三四层继承和虚继承(多重继承)

    作者回复: 现实中有很多深层次继承的例子,但不是说这就是好的,实际上iostream就被很多人批评过。

    4
    9
  • 58
    using set_type = std::set; 类似这种真的不建议,如果用多了,反而不容易阅读代码。

    作者回复: 别名需要控制使用范围,也就是作用域,不能是全局的,而是限制在类或者函数内部,在一个特定的上下文里它才能发挥作用。

    2
    8
  • 嵇斌
    1. 面向对象的首要原则 SRP,单一职责原则。这一点特别赞同。另外就是慎用继承,尽量使用组合去实现。分享一个小故事,之前因为代码要写单元测试,使用到了Google Mock,一开始以为 只有虚类才没被Mock,导致很多完全没有必要使用virtual的类使用了virtual,直到有一天看了文档:https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#mocking-non-virtual-methods-mockingnonvirtualmethods 恍然大悟。 2. 类的设计最好遵循RAII,即在构造时完成资源的初始化。但是我觉可能Chrono可能会在后续内存管理的课程中讲这个。

    作者回复: 现在C++有很多工具,比如智能指针,可以在外部帮着管理资源,还有对象池模式,集中申请释放。 所以RAII还是要看情况,当然,如果资源确实是与类强相关,那么就用RAII自己管理。

    7
  • Eason Tai
    有个小问题,为什么要在一些类里面多次书写 public 和 private 关键字呢?是增加可读性或者强调什么嘛?

    作者回复: 是的,这大概是借鉴了一点java吧,用public、private来分组不同的逻辑段落,增强可读性。

    4
收起评论
显示
设置
留言
72
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部