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