一直以来,我在教授C++语言编程课程的时候,总是在泛型上慌得很,生怕自己的半吊子C++知识贻笑大方。还好我发现同学们和我一样都是糊涂虫,只满足于把STL的容器和算法当作简单类型用就完了。印象深刻的一次尴尬就是学生问我:丁老师,怎么你给的例子的链表,和std::list的链表差别这么大,学了泛型,怎么还是看不懂STL库的代码呢?
我只能说,师傅领进门,修行靠个人,我也不懂哩!其实老师眼都看花了,也是看不懂。最近终于看完了,更觉得头大。通过20年的教学,悲哀的发现一个事实,** 哪怕学完一本教材,让我实现一个链表或者字典,也绝对不会长得像STL库 **。每次试着研究STL库、BOOST库的源码,总是觉得在鞭挞我的灵魂,PUA我,让我知道我只是一个C语言学了一半的三流本科野路子。这种情况让我痛不欲生,直到看到了Qt的源码,才重新心情好起来:原来Qt的源码风格非常适合我的口味,每次一打开,我受伤的灵魂就得到了安慰。
Qt实现可读性好
对于这种泛型库,都是没有CPP的,也就是在头文件里直接引入所有代码。可以认为是内联的。但哪怕只有.h文件,可读性也有不同。
文本排版
虽然对头文件来说,ALLinOne可以不管排版,但一般来说,我们实现一个类,还是喜欢不把实现直接写在类的声明里。这样会显著分散接口的文本描述,让维护者不知道这个类有多少方法。STL库大部分方法是直接写在A的声明里:
class A{
FunA()
{
}
FunB()
{
}
}
Qt把比较冗长的方法的声明、实现分开陈述。
class A{
FunA();
FunB();
}
A::FunA();
{
}
A::FunB()
{
}
Qt这样做的好处是,这个类的全貌尽收眼底。就像是你写论文的时候,有个目录,而不是上来就第一段。STL库这样写,2000多行的程序,上来就把人打蒙了。
此外,Qt的缩进、大括号的配对都比较美观。相对起来g++的STL就随意多了。
命名风格
二者的命名风格大不相同。就拿 list::push_front来看 STL:
void
push_front(const value_type& __x)
{ this->_M_insert(begin(), __x); }
#if __cplusplus < 201103L
void
_M_insert(iterator __position, const value_type& __x)
{
_Node* __tmp = _M_create_node(__x);
__tmp->_M_hook(__position._M_node);
this->_M_inc_size(1);
}
#else
template<typename... _Args>
void
_M_insert(iterator __position, _Args&&... __args)
{
_Node* __tmp = _M_create_node(std::forward<_Args>(__args)...);
__tmp->_M_hook(__position._M_node);
this->_M_inc_size(1);
}
#endif
#endif
Qt:
inline void push_front(const T &t) { prepend(t); }
template <typename T>
inline void QList<T>::prepend(const T &t)
{
if (d->ref.isShared()) {
Node *n = detach_helper_grow(0, 1);
QT_TRY {
node_construct(n, t);
} QT_CATCH(...) {
++d->begin;
QT_RETHROW;
}
} else {
if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) {
Node *n = reinterpret_cast<Node *>(p.prepend());
QT_TRY {
node_construct(n, t);
} QT_CATCH(...) {
++d->begin;
QT_RETHROW;
}
} else {
Node *n, copy;
node_construct(©, t);
QT_TRY {
n = reinterpret_cast<Node *>(p.prepend());;
} QT_CATCH(...) {
node_destruct(©);
QT_RETHROW;
}
*n = copy;
}
}
}
STL库的变量名,太多的下划线(__)和奇怪的前缀,比如_M_, 可读性太差。namespace是可以嵌套的,既然这么怕重名,还不如定义namespace呢。
其实Qt吸引我的远不止上述这些,我在开发工具上的个人口味,决定了我在用Windows开发时,只爱 MSYS2, 以及里面的Qt。
个人口味
我一直觉得自己水平很差,还敢教学生写代码。唉,真的惭愧。不过我发现可能我这样水平的人,真的不适合学习纯粹的C++、不适合学Java,不适合学Python,不适合学C#,还是学习Qt吧。没办法,写了这么多年程序,从Logo语言(中华学习机)、GWBasic, Qbasic, VisualBasic, TC2.0, TC3.1, Borland C++/BCB,VC/MFC 到 GNU C++, Java, Python,node.js, plpgSql, plsql , Excel 以及VBScript, bash,……需要啥子,就学啥子,但是不代表我都喜欢——有些东西,我一直难以习惯,即使看得懂,也很难喜欢——不合口味。
讨厌的口味
- 下划线、非字母开始的变量 :如Bash的 $(),以及STL实现里的__xxx
- 全大写的东西 : 老式Basic语言, 很多公司建立的SQLServer, Oracle模型。
- 超过两层或者10个字符的引用: Java的一堆包.Axxx.Byyyyy…,C#的System.Math.XXXXX,以及 boost/ C++20 的 std::chrono::system_clock::now
- 把实现直接写在Class里: Java、C#, STL都这样干。
- 弱类型语言: 在竞赛时,曾经因为把 backaside 变量在某处误写为 backside,结果程序不报错,痛失冠军。从此抛弃任何不需要显式定义变量的语言。
- 发布时需要另外携带解释器、虚拟机、子安装包:Python, Java(jvm),C#(clr), Basic, Node.js, Matlab,vcredist等等。
- 发布、运行时要使用注册表、环境变量:注册表一堆东西,而后在环境变量配置一堆东西。
- 运行时文件夹文件太多,又小又碎:把程序发布给终端用户,还要携带大量html,png,资源,携带大量脚本。一个文件夹里上万个文件(人家是要使用,不是开发)。
- 深埋文件夹:把自己安装在user/UserName/appData/…下,并在Local里有好多垃圾文件。
- C++库、COM/OLE、.Net:一定是C的库,C的库是最简单,兼容性最高的。哪怕用C++,用Qt,都要导出C接口的库,而不是C++的。
- 文本接口: JSON和XML可读性好,但是解释这些东西很浪费时间。宁可解决大小端、二进制兼容性问题,也不想为了解释文本引入第三方库。
- 非原生IDE:IDE应该一开始就是为了核心工具链研发的,而不是万精油的。哪怕我喜欢Qt,也尽量避免在VS里使用Qt。
喜欢MSys2Qt的原因
- 开发环境无污染:msys2是文件夹封闭的,在线安装后,重装系统了,直接释放原来的备份压缩包,一切如初。
- 发布环境无污染:msys2 Qt的依赖是封闭的,发布时,直接放在文件夹里,拷贝齐了动态库,就不用要求用户安装任何东西。对比起来,msvc Qt可能需要安装VC运行时。重要的是文件夹里放的东西不是很碎。
- 开源、跨平台:存粹喜欢开源。现在犯了强迫症,自己写的代码总是要在x86,x64,arm,arm64上编译,都能用了才开心。用MSYS2 Qt,基本很省心。
- 库较为完整:在windows下,基本上常见的Linux C库都有实现,且不用配置。MSYS2就是一个完整的小bash环境。
- 效率高:与Cygwin相比,用的是Mingw,没有API转接,效率很高。
- API优雅 Qt的API非常优雅,一致性很高。
- 文档优雅 我觉得,看过的文档里,MSDN、Archlinux、Qt、PostgreSQL这四个东西的文档给我的印象很好,除了MSDN,其他三个和MSYS2都有交集。
- 源码优雅 源码我看得懂!不PUA我,不鄙视我。
总观点
MSYS2 + Qt是给一个一般水平的科研工作者开发出工业级别生产力工具的极佳解决方案。不需要很多的投入,就可以开发出跨平台的、绿色版的高度可用的生产力工具。
|