说一下我为何回归 C++ 了吧 : 简单: 生命有限。
用 C , 固然是只有一个范式, 学起来容易, 上手简单, 可是需要操心的问题太多了: 内存泄露, 野指针, 各种断错误。 可能你会说, 内存管理,小心点就可以了。 但我觉得,如果你总是用 200% 的精力去避免内存泄露,你就没有精力开发正常的软件了。你会对软件的逻辑进行折中,因为你实在没有精力开发需要你花很多时间写逻辑的代码了。 于是, 各种因为”实现起来麻烦” 为借口进行功能精简。所以我重新拿起了高级语言。
那么, 就面临者选择: 1 c++ 2 python 3 java etc
那么, 为啥选择了 c++ 呢? —— 因为 C++ 的抽象能力是所有语言中最高的
我为啥怎么说呢?
你比如说java,为啥java是个烂语言呢
这个和jvm无关
博士说过性能问题是狗屎, 我不喜欢考虑性能问题。 java的烂和jvm无关。
java 烂就烂在 GC 和面向对象
面向对象+GC 就组合成了一个臭不可闻的烂语言。
为啥 GC 和面向对象被我那么贬低呢?
因为 对象 的抽象能力实在是太弱了, 并且也不是正确的抽象
比如 人
你要用 class human 抽象?
那人能干啥?
这都是你不得不考虑的问题
还有
你觉得应该搞继承
然后人从 哺乳动物继承
but ~~~~
多年以后, 你会发现你的分类错了
咋办?
原有的代码都高度依赖这个了
这就是c++ 和 java 的面向对象都被人诟病的地方
因为 OOP 本身就是个骗局
既然 OOP 本身就是骗局, 自然是要避免以 OOP 为基础 建立的语言
那么 java 就不能用了, 因为它是一个强迫你使用 OOP 这种烂技术的语言
程序是一种状态机, 图灵早就说了。
状态机的意思是, 程序是由多个状态构成的, 调用语句是为了切换状态。 而面向对象却认为程序是对象构成的, 简直是 bullshit。
对象压根就不能重用, 因为你根本找不到一个唯一正确的通用封装
结果就是大量的轮子, 大家都按照自己的方式封装对象。
大家不理解 C++ , 是因为大家把 C++ 当作了 C 的 java
把 C++ 当作了能编译成本地代码的 java, 有指针的 java。实际上大家还是在用 java。 这是要绝对避免的。
我坚定的反对 OOP。
OOP 容易指导你写出很烂的代码
剩下 python 和 C++
那么, 为啥是 c++而不是 python 呢?
因为 “工程性”。
python 不是一门具有工程性的语言。
什么叫工程性?
工程性的定义是: 认为程序员是人 , 是人就会犯错, 要从语言上提供机制帮助程序员避免犯错. 同时对那些 控制狂, 不能在语言上束缚他们。
python 束缚太多。 因为 python 想做一个”安全” 的语言。所以束缚多。
如果这个可以忍受, 那么第一条, “从语言上提供机制帮助程序员避免犯错”, python也没有做到!
因为 python 是个”安全的语言” , 所以, python 默认行为是 “面条代码”。 也就是, 绝对避免崩溃退出 除非发生了严重的错误, 而且程序员故意为之, 否则 python 死不崩溃。
死不出现段错误 (否则python也和C一样在段错误中挣扎怎么行呢)
死不挂掉
也就是说, python 鼓励程序员写面条代码
就拿简单的一个 python gui 程序来说
可能他能载入 gtk 模块, 然后界面显示了
but , 一个功能缺失, 导致的结果不是程序拒绝启动, or 崩溃
而是界面显示不全
除非程序员故意做了检查, 抛出异常 , 强制崩溃
否则 python 的默认行为就是 “面条代码”
这就是一个不具备工程性的语言
还有, python 可能在运行时抛出语法错误
这就很要命了, 一个有语法问题的 py 代码, 居然能运行。 只有偶然执行到某个有问题的代码才会崩溃抛出错误。
也就是说, 如果你没有 100% 测试覆盖到代码, 你就甚至不能肯定你的程序是 100% 语法正确的。
这就不是一个具备工程性的语言
写点玩具还可以
但是一旦工程变大, 包含数十万甚至是百万千万的代码
那么光是确保”没有语法错误” 这个很基本的东西, python 就要花费大量的人力
这就是一个没有 工程实践 的程序语言
所以 python 是一门很烂很烂的语言
烂就烂在没有工程性。
可能有人会提到 C#
为啥我一定会说 C# 是个烂语言呢?
我一定是有原因的,我不会无缘无故的说某个语言是烂语言
因为 C# 就是 M$ 版的 java
java 烂的, C# 一样都学习过来了
所以你如果不考虑 java , 就压根不需要考虑 c#
还别提 C# 压根就是不跨平台的
那么, 接下来, 我分析一下 c++到底好在哪里, 为啥 C++ 是这样设计的
首先一个问题, 是容易被 C 程序员忽略的, 什么是 “类型” ?
C 程序员如果没有意识到这点, 通常在学习 C++ 的时候会变成个垃圾 C++ 程序员。
因为 C++ 一切的一切 , 都是建立在 “类型” 上的。
C++的核心在 “类型” 而非对象,不理解 类型 , 就不能理解 c++
很多人(即便只用C)也很容易把 指针 和数组混淆。这就是不理解类型的原因。
那么啥是 “类型” 呢?
简单的来说, 一个类型 就意味着包含了相同的操作符。
我是在写 QBASIC 编译器的时候才真正的理解了 类型
比如 int , 是一个类型。int 是一个什么样的类型呢? 则由 int 这个类型支持的运算符定义。int 支持的运算符有 +-/* 等等 。这就是类型
[] 也是运算符, 下标运算符。显然 int这个类型并不支持 [] 运算符。可是 int[], int* 类型却支持 [] 运算符。所以他们必定是不同的类型。
所有支持 [] 运算符的类型, 都有一个共性, 那就是支持 [] 进行下标访问,这个共性被称为 “数组”。
所有支持 * (不是乘法, 指针的那个解引用运算符) 的运算符 , 都有一个共性, 那就是支持 * 解引用 , 这个共性就被成为 “指针”。
指针有一个特点, 就是支持 * 访问所引用的对象, 以及可以使用 ++ – 来变更指向的对象。( 显然 int[] 这个类型不支持 ++ –, 显然数组和指针不是一个类型。)
这种能力在 STL 里被称为 “迭代器”。 显然, 迭代器指的是支持 “ ++ – * “ 3种运算的类型。
那么博士介绍到这里大家应该就比较清楚的知道了, 所谓类型, 就是依据支持的运算符定义的。
那么, C++ 为啥要这样设计? C++ 为啥提供了 “运算符重载” ?
因为 C++ 要 “能编译”
就这么简单。 比如 动态语言, 天生就支持了 map, list 这样的数据结构。
但是 c++ 不能。
他不能在语言里提供这些, 为啥不能呢? “因为 C++ 必须被编译”。
这就意味着, 核心语言只能提供某种机制让你达到可以做到将 map. list 当作内置类型看待, 而不是直接内置这些功能。
c++ 要做到这点, 就需要一个强大的功能 : 运算符重载。
vector 类, 要重载 [], 以便表现的和 数组一致。提供 [] 运算符重载, 你就能实现出 安全的数组容器。
重载 * , 你才能写出智能指针, 还和内置的指针用法保持一致。而不是语言一开始就是提供智能指针。
重载 – ++ . 你才能实现出迭代器,还和内置的指针用法保持一致。而不是语言一开始就是提供迭代器。 以便就算你内部实现上对象都不是连续存放的, 都可以屏蔽到这些细节
C++是一个看重语义的语言。
所以用库对语言扩展,最好保持语法在形式上的统一。用库扩展了安全的数组容器,要在形式上使用相同的下表运算符。用库实现了智能指针,要在语法形式上提供和内置指针一样的用法, 等等。
也就是说, c++ 里的一切重载, 都是为了将复杂类型用法和内置类型统一化并且仍然保留 “可编译” 这一个目标奔过去的。
否则, 编译器插入个解释器, 解释执行不就完了, 就像 lisp 那样。
所以说, 要理解 “类型” 是 c++ 里最重要的概念, “可编译” 是 C++ 里最重要 设计原则
那么为啥 有模板呢?
模板同样是为 “可编译” 服务的。否则, 你拿什么实现通用的容器呢?
让大家都从 CObject 继承? 那太恶心了
java 会这么干, 不代表 c++ 会这么干。说实话, MFC 就这么干了,所以MFC是烂库,绝对的烂库。
23:30:28 qq(海盗):博士的见解确实高明,我一直觉得重载强大,但就是不知道强大在何处。。今天明白了
c++ 对 通用容器的回答是 “模板” 而不是让大家都从一个 CObject 继承 因为 c++ 就是一个设计上要避免绑架程序员的语言
cpp里, 是注重类型的
23:38:39 qq(jackarain):人家10年cpp经验, 也没领悟到你这么些东西
23:39:13 qq(ywk/?_have_fun):受教
23:39:29 qq(jackarain):模板是多态最重要的表现, 而不是继承
23:39:39 qq(jackarain):以不变应万变
23:40:00 qq(jackarain):并且老老实实保留着类型信息.
23:40:07 qq(jackarain):不会丢失
23:40:44 qq(jackarain):继承, 不一样, 通常是使用cobject这种root class来以不变应万变.
23:40:59 qq(jackarain):但效果就像void*一样
23:57:44 qq(jackarain):几小时不用c++, 就心痒
Comments