找回密码
 立即注册
楼主: 闲鱼技术01

C++ 学习或编程中「误入歧途」是怎样一种表现?

[复制链接]
发表于 2021-4-16 10:18 | 显示全部楼层
不看标准看汇编。
越low越老越稳定。
不懂什么是UB。
不懂什么是implementation defined behaviour.
Google说好的一定是好的。
Google style guide说不好的一定是辣鸡。
异常很危险。
我不throw异常就和我没关系了,哈哈!
我觉得我记性很好,new完肯定记得delete。
看我函数结尾一个delete!那资源泄露他一定找不上我!YEE!
C++不需要设计模式,像Java一样折腾一定是过设计。
满地乱用template。
该用template的时候不敢用。
认为int是原子的,++i是原子操作。
一地的匈牙利命名法。(来人,把匈牙利人拖出去斩了!
把Windows平台和工具链的特性当成世界标准。
把Windows API、COM、MFC等一系列Windows的东西当成C++的一部分看待。
把Boost当洪水猛兽,以不使用Boost为荣。
把STL当洪水猛兽,以不使用STL为荣。(上一条的重症化版,差不多快仙逝了
过于醉心constexpr,就为了能避免运行时算那么几个到几百个数字,提高性能
认为constexpr和template TMP黑科技到极致真的可以做到编译时完成全部计算(你写计算密集的服务,数据都是客户端发请求提供,来你亢一个我看看
热衷于把a库链接成so库,甚至要把第三方的a库链接进自己的so库。
或者丧心病狂到把第三方的a库里的object文件拿出来打包进自己的a库。
发表于 2021-4-16 10:20 | 显示全部楼层
稍微举例下吧。
面向过程方面

    重复造轮子(而且通常是劣质的轮子)
如在我们已经有了std::swap的情况下,却总有人写出:
  1. #define SWAP(a,b) (a ^= b ^= a ^= b)
复制代码
本代码事实上不会比使用第三个变量交换的方法更快,更不可能比std::swap更快
    沉醉于C语言的过气写法,酷爱使用指针(却总是用错)
  1. std::string str("Hello World");
  2. char *p = &str[0];
  3. printf("%s", p);
复制代码
需要从std::string获取const char*字符串应该使用c_str()成员函数,而不是用指针方法
此外C++中的字符串类型应该是const char *,而不是char *
    死抠语法实现细节,而且通常以某种编译器实现作为标准(即滥用UB)
  1. f(i++, (++i)+3);
复制代码
他们通常会写出这种代码,并且以自己学会了前置++和后置++的区别而自豪
面向对象方面

    热衷于使用设计模式,无论程序规模与设计如何。非常喜欢使用异常、模板等高级功能
  1. class CErrorLog {//...};
  2. class IPrinter {//...};
  3. class CStreamPrintert : public IPrinter {//...};
  4. class IStringMaker { //... };
  5. class CHelloWorldStringMaker : public IStringMaker {//...};
  6. IPrinter *StreamPrinterFactory(ostream &os)
  7. {
  8.     return new CStreamPrinter(os);
  9. }
  10. IStringMaker *HelloWorldFactory()
  11. {
  12.     return new CHelloWorldStringMaker;
  13. }
  14. CErrorLog g_ErrorLog;
  15. int main()
  16. {
  17.     IPrinter *myPrinterObject = nullptr;
  18.     IPrinIStringMaker *myHelloWorldObject = nullptr;
  19.     try {
  20.         myPrinterObject = StreamPrinterFactory(cout);
  21.         myHelloWorldObject = HelloWorldFactory();
  22.         myPrinterObject.put(myHelloWorldObject.str());
  23.     } catch(...)
  24.     {
  25.         delete myPrinterObject;
  26.         delete myHelloWorldObject;
  27.         g_ErrorLog.write("Error Construction...");
  28.         throw;
  29.     }
  30.     delete myPrinterObject;
  31.     delete myHelloWorldObject;
  32.     return 0;
  33. }
复制代码
泛型编程方面有待更新……
发表于 2021-4-16 10:24 | 显示全部楼层
我觉得你在学习一门语言的时候,去研究误入歧途是什么样,就已经开始误入歧途了(手动笑 cry)
在下 C++ 从业十年,主要搞游戏引擎技术,我说的都是个人经验,有狭隘性,可能不对或者不全面,当成参考吧(在下没有能力给别人指点江山)。
你问的问题,还有看的书,我假设你是 C++ 这门语言的初学者,如果不是,那这个回答不用看了,不适合你。
首先我要说,Primer 虽然叫 Primer,但是它真的不适合初学看,至少不适合作为入门书。原因是它过于晦涩,有点接近于工具书了,谁会拿着词典学外语?会打击你的积极性。这本书适合作为参考书来查阅。
入门应该选择偏向于教程类的图书,篇幅比较短小的,文风比较轻松的。例如《Thinking in C++》就是一个很好的选择。就算喜欢读大本,也应该选择像 C++ 之父的《The C++ programming language》(简称 TCPL)这种由浅入深的。
并且,在学习的过程中,一定要结合实践。理论和实践必须两条腿走路。我曾见过理论很好但做不出东西的人,也曾见过喜欢动手但由于理论不深而进步缓慢的人。关于如何结合实践,在后面说。
上面这种基础的教科书烂熟了之后,就可以稍微提升一下本领了。这时候可以读类似《Effective C++》、《More Effective C++》这种经验总结书,可以帮你树立正确的编码价值观,避免你踩坑犯错。
根据我的观察,多数 C++ 程序员的水平可能就止步于这里了。
如果你开始觉得有趣了,想更加深入了,可以读《Inside the C++ Object Model》。它虽然讲的是 C++ 对象实现的内部原理,但是并不难读,而且会让你豁然开朗,明白了内部原理,能写出更加稳健的代码。
如果想深入了解一下 STL,可以看《STL 源码剖析》,连算法也一起巩固了。很多老的 C 程序员会中伤 STL,认为其效率低。其实那都是主观片面的想法,要么就是没搞清楚 STL 到底怎么用,要么就是没搞懂 STL 应该用在哪里。
如果想研究泛型,可以看类似《Modern C++ Design》这种书(就是那个 Loki)。虽然现在 C++ 已经有了新的标准,书里某些繁琐的神操作已经不再必要(纳入语言特性了),但是其思想和实践仍然是无法取代的重要一课。
修炼到这里,你的深度就有了,可以学习一些新奇的玩意,流行的库,C++ 质量最高的库说是 boost 大家应该没意见。一个库想加入 boost 可能前后要修订几年之久。boost 是准标准库,C++ 标准库的试炼场。现在已经演变成了庞然大物,许多新的新奇玩意都加了进来。有一些很实用,例如 asio 异步网络库。
作为号称最难的语言,可以说掌握的差不多了。之后是思想的问题。不能停留在编码的层面止步不前。
接下来学什么呢?《Design Patterns》。如果这本书觉得太晦涩,可以读《Head First Design Patterns》生动又有趣。别担心,你肯定能读懂,虽然你根本没学过 Java。因为现在你已经超越语言学思想了。为什么要学设计模式呢?结构。不是数据结构,而是软件的结构。有了结构的能力,你才能设计制造一些更大的玩意。这里要掌握的是抽象思想,要巩固模块化思想,要强调接口设计。其实接口设计很有学问,都可以单独出书,有兴趣可以学一学,这里不再赘述。
学语言目前我就想到这么多,与语言无关的书就不一一列举了,其实学到这里你应该会自己找书了才对。哦,还是想说一个比较重要的,那就是《Computer Systems: A Programmer's Perspective》(简称 CSAPP)。别担心,它不是讲硬件的,这本书好就好在它以程序员视角描述了整个计算机系统。这是一本号称与黄金等值的计算机系统图书。学完了这本书,你会飞跃一个层次,因为你可以以计算机的视角来看问题了。实际上写程序的时候,你经常需要在人类和计算机之间切换思维(也就是问题空间和解空间)。
剩下的如何调试、如何分析、如何优化,都是你需要在实践中掌握的。
关于如何实践?首先书里面的练习题要做一下,除非你确定你绝对能写出来(而不是知道怎么写但却写不出来,那等于还是没完全掌握)。小的练习都游刃有余了,比较无聊了,这时候可以给自己设定小习作,实现一个什么具体的功能,可以是有点实际意义的。例如我是搞游戏的,那我就可以先做一些最简单的游戏,例如俄罗斯方块、贪食蛇什么的。如果是爱好网络,可以实现一些比如即时通讯的小工具,不需要太复杂,一开始能实现两个人聊天也行的,把程序发给你的好朋友。这里根据自己的水平和爱好发挥想象。这样锻炼自己,既有趣,又有意义,能在做的过程中发现问题并解决问题。这样接近真实的项目开发,毕竟程序不是写出来的,而是调试出来的。需要用到 Windows API 就查 MSDN,需要用到其它库,就去查官方文档。一定要看英文原版,尽量别看翻译的,因为你在实际工作中经常会用到,每一天。小习作循序渐进,最后做大项目。
最后,祝你成为一名优秀的软件工程师。
(什么?!你的目标是厨师?!)
(手机打字这么累,我为什么不用电脑,明明就是一步之遥  )




更新:我发现书名前面写的是英文,后面写的中文。现在统一一下全写英文,英文搜索更精准。这些书都有中译本。另外 Head First 那本我写错了,因为《Head First Java》是一本很有名的书,我一不留神写成了它,现在已经改过来了,希望没有坑人(手动笑 cry)。
发表于 2021-4-16 10:33 | 显示全部楼层
1. 认为计算机技术就是编程技术(认为编程技术就是计算机技术)
这种情况应该不少见,甚至许多信息学院的学生也有这种想法。计算机技术包括了多媒体,计算机网络,人工智能,模式识别,管理信息系统等等这些方面。
而编程工作,只是在这些具体技术在理论研究/工程实践的过程中,表达算法的过程。编程的人不一定就对计算机技术很了解。
2. 咬文嚼字的孔已己作风
有一本书叫《计算机网络原理》,所谓《原理》,即是需要掌握它为什么这样做,学习 why,而不是 how(怎样做)。特别认真的人会背下以太网的网线最大长度,数据帧的长度,每个字段的意义,IP 报头的格式等等,但是忘了路由的原则,忘了 TCP/IP 协议设计的宗旨。总之,有很多人花了大量的时间,把书背得滚瓜烂熟却等于什么也没学。
在学习编程的时候有些人也是这样,他们确切地记得 C++ 语法的各个细节。看完了 C++ 教程后看《Thinking in C++》,《Inside C++》,《C++ reference》,this C++,that C++ ……,然后是网上各种各样的关于 C++ 语法的奇闻逸事,然后发现自己又忘了 C++ 的一些语法,最后回头继续恶补…
3. 不顾基础,盲目追赶时髦技术
学习新技术对已经进入职业领域的程序员或者项目经理来说是合理的,因为 IT 技术进步很快,不跟进就是失业。但是对于初学者来说(尤其是时间充裕的大学在校生),这是令人费解的。一个并未进入到行业竞争中来的初学者最大的资本,便是他有足够的时间沉下心来学基础的东西,学习 why 而不是 how。
基础的课程,比如说数据结构,操作系统原理等,虽然不能让你马上实现一个 Linux,但它们能够显著减少你在学习新技术时学习曲线的坡度。而且对于许多关键的技术来说,是不可或缺的。
发表于 2021-4-16 10:37 | 显示全部楼层
《C++ Primer Plus》这书就时误入歧途,不知道为啥身边那么多人都喜欢买这本,还各种说网上说这本比Primer基础些,适合新手入门blabla,然后我被问过几次问题,结果都是因为书上写错了……
高票回答总结得很棒了,但毕竟知乎是故事会,还是讲下我的误入歧途。
当初上算法课,之前刚看了《STL源码剖析》,于是我所有的算法代码都写成函数模板,学STL用迭代器表示范围,尽可能用双向迭代器而非直接操作数组下表,看着自己的代码,满脑子的享受。比如这段我完成课后作业的代码
  1. template <typename Iter, typename Pred>
  2. inline void quick_sort(Iter first, Iter last, Pred pred) {
  3.     using value_type = typename std::iterator_traits<Iter>::value_type;
  4.     // 忽略剩余代码,以及比较迭代器it1和it2指向元素的大小使用pred(*it1, *it2)
  5. }
复制代码
写其他数据结构也差不多这样,先把框架搭出来,然后沉浸其中美美地欣赏一番。
然而写成这样又如何?
  1. void quick_sort(int a[], int n) {
  2.     // ...
  3. }
复制代码
写成了C++大牛们嗤之以鼻的一切的原罪的C style?


说到C style,之前体会到了C++的魅力,然后看大牛们说学C++最好的入门方式就是直接学C++不学C。OK,于是拒绝C代码。
后来发现用iostream格式化输出好麻烦啊,还记不住用法,搜一下发现C++的iostream是败笔,性能差还不支持C风格格式化,于是彻底用stdio取而代之,即使是简单地打印一个数也要用printf,看到一个cout就不舒服,看别人代码里出现cout就内心微微一笑:呵,不懂C++的大猪蹄子。
再后来觉得C++的string真烂啊,重载一大堆,实用的没几个,大牛们都这么说,于是vector<char>加STL算法才是处理字符串的正确方式。 当时还第一次知道string不要求底层连续存储,不同于vector ,更加不用string了。(PS:C++11已经规定string底层连续存储了)
再后来看了APUE这种书,又看了大牛们说C++没有针对性的算法,new的性能还不如那些GC语言,于是为了提高性能,又拒绝使用vector<char> ,不过是简单操作个缓冲区嘛,又不是大量数据,需要担心栈空间不足嘛,直接用字符数组代替,你们GC再厉害也不可能比我栈上更快吧。
再后来知道了可重入,觉得一切不可重入的函数都是有害的,坚决不用strerror(errno), 而是用下面这种写法
  1. char buf[100];
  2. printf("error: %s\n", strerror_r(errno, buf, sizeof(buf));
复制代码
然后想这样每个函数内部都得定义一个局部字符数组,会不会多次移动栈指针导致性能开销大呢?以及如果开多线程,或者创建了多个对象,会不会栈空间不够呢?
再后来发现C++的异常安全特别坑爹,比如你在用new创建对象传入智能指针的时候,如果new失败了, 创建的对象也不能被析构。又搜了会发现异常拖性能,虽然有个什么zero-cost model,但是不是所有编译器支持啊,于是错误码就是一切,异常就是垃圾。
  1. // 声明
  2. std::pair<Object, bool> createObject();  
  3. // 调用
  4. Object obj;
  5. {
  6. auto res = createObject();
  7. if (res.second) exit(1);
  8. obj = std::move(res.first);
  9. }
复制代码
然后发现不对,虽然用大括号避免了检查错误码的res被重复定义(可能后面还要用到类似的函数),但是这里Object必须得有个默认构造函数,如果用在大括号外定义指针而非对象,那么还不如写个Object* createObject()这种C函数,然后检查指针为空呢。虽然我已经抛弃了很多C++的糟粕,但我写的是C++,我不容许C风格的麻烦代码,还要指针&和*麻烦死了,哪有我写的这段代码这么容易阅读?
好像不对。
  1. auto obj_ptr = createObject();
  2. if (!obj_ptr)
  3.     exit(1);
复制代码
这种代码似乎也行?这种代码一点也不C++权,为了拒绝C的原生指针,我必须再加一行代码
  1. auto& obj = *obj_ptr;
复制代码
补充下正经话吧,最误入歧途的就是过度极端化,C++的自由使得C++更容易极端化。另一方面也源于和C的比较,导致C++er更容易对C抱有优越感,毕竟很多特性是宣称比C好的,标准库也是宣称比C强大的。
但C++就是处于一个尴尬的境地,写底层很多特性没什么用,写高层标准库又太简陋。标准库除了STL容器+算法的部分也都很尴尬。
推崇现代C++是为了利用新版本C++对同样性能的代码下可读性的提升,但这也不意味着是最佳选择,甚至还可能被极端地误解,比如有人说绝对不用宏。
6. 其他 C++ 特性 - Google 开源项目风格指南
这时候就需要一份类似的编码规范,比如看了这里你就知道,说尽可能不用宏,是在哪些具体情景下。假设团队没有规范,也要考虑到团队其他成员的水平,你C++11/14甚至17/20随便用,如果可能导致其他人阅读出现问题,为何不采用简单易懂的写法呢?至于一个人自己的项目,最容易遇到的问题就是提前优化,严格说传值、传指针/引用的问题不在于此,其实规则也很简单(结构体/类的size小于指针的size就传值,大于就传指针/引用)。
举个我的例子,曾经写过小项目,用到了同步队列,当时我直接用C++11的<mutex>和<condition>库包装了 std::queue实现的,后来发现性能有问题。没想着做性能测试,结果花了很久去把同步队列改成无锁的,结果性能一样有问题。后来发现瓶颈根本不在这里……无锁有锁没区别。
我这还算好的了,毕竟当时是写出来才发现性能有问题的,只不过我没有选择去测试而是修改细枝末节的东西。如果是有提前优化习惯的,一开始就浪费时间仔仔细细设计一个无锁同步队列,最后发现瓶颈不在这,于是在编码期就浪费了很多时间,推迟了可以进行测试的时间。
另外,C++太庞大导致学习分不清主次也是误入歧途的表现。最显著的就是模板元编程,这也是大牛们最容易带偏新人的地方,大牛们在模板元相关的回答下各种炫技,很容易让新人觉得好厉害,我也想学。
说句极端的话,模板元编程P用也没。
说句稍微好点的评价,对99.999%的C++程序员而言,学会模板元编程P用也没。(10万人里有1人做模板元编程吗?)
有研究这个的闲情雅致,去看看其他专业知识的实际收益要大得多,毕竟编程语言不是全部,C++更不是全部。
发表于 2021-4-16 10:47 | 显示全部楼层
当你学C++的各种特性不是为了增进了解完成任务,而是……就是想用用时,你已经误入歧途了。
发表于 2021-4-16 10:51 | 显示全部楼层
C++唯一的歧途是模板元编程。
发表于 2021-4-16 10:52 | 显示全部楼层
你本来想学会编程这个技能,结果却陷入了C++这门语言的各种复杂的语言知识上。这样你就误入歧途了。编程其实是一种技能,需要一定量的练习,然后掌握这种技能。很多教编程的书,只是在罗列语言的知识点,这就很容易误入歧途了。就好像你要学游泳,却学了一堆水的浮力啥的物理知识,还学的很深入。这不就误入歧途了嘛。为啥说C++更容易误入歧途呢,大概是因为C++里的“知识点”比较多。
发表于 2021-4-16 10:55 | 显示全部楼层
谢大锤 @海克斯科技大冰锤 邀
本人对于误入歧途这个词有过切身的体会,而且应该是最适合题主描述的。


C++这门语言我是从四年前开始学的。
那时我就发觉我在写C++的时候经常会被编译错误整崩溃。这实际上是因为C++这门语言本质上是非常庞大杂乱的。


通常我们只会使用(和熟悉)C++的一个子集——例如c with class,一旦我们进入到不熟悉的领域
就很容易原地爆炸
发表于 2021-4-16 11:02 | 显示全部楼层
用了一大堆花哨的模板,写了个编译期的itoa,然后…

Error:递归过于复杂
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-15 04:26 , Processed in 0.069425 second(s), 20 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表