C++语言的一些问题

论坛上看到的有人提出的关于C++的一些问题,真是细致(下面还有回复说他对C++只知其一;不知其二的),一直觉得本身的C++仍是能够的,但是看了以后真是内牛满面,为何本身历来没有想的这么深刻,差距真的有这么大吗?泪奔~,之后不再敢说本身会C++了。html

=== 数据类型 === 
  
1. 你知道bool类型的变量占一个字节,可是殊不知道bool类型在内存里是如何存储的。true是0吗?false是1吗? 
  
固然,你能够说,程序员不该该关心它在内存里如何存储。但是,C++却恰恰容许你使用union类型以及对指针进行类型转换,让你偷看到它的表示。 
  
但是,即便如此,你保证不一样的编译器都用一样的方式表示true和false吗? 
  
2. const char*(指向字符常量的指针变量)和char*(指向字符变量的指针变量)是两个不一样的类型。若是一个函数接受char*类型参数,那么若是给它传入const char*型参数,编译器会警告。可是这真的有必要吗?若是一个函数只是在某些状况下要修改参数中指针指向的数组呢?或者它还要将参数再传给别的函数呢? 
  
3. 你知道char, short, int, long, long long分别占几个字节吗?你知道42,42L,42LL分别是什么类型吗?它们在32位x86和64位x86_64机器上分别占几个字节吗?你怎么写一个整数常量,保证它是64位的?(这是个人同窗遇到的真实问题) 
  
4. int a; a=9; if (a=42); { cout<<"a="<<a<<endl; }为何老是输出"a=42"?C++明明有bool类型,可是为何这样的代码竟然编译通得过? 
  
=== 运算符 === 
  
5. 看代码: 
  java

1
2
3
string place, item;
  cout<< "There is a " <<item<< " in " <<place<<endl;
  cout<< "在" <<place<< "里面有一个" <<item<<endl;


  
以上代码没法国际化,由于几个字句的顺序依赖于语言的语法。C++不让你写一个串,对全部语言都使用。仍是Java的MessageFormat好 
  python

1
2
3
4
String englishPattern = "There is a {0} in {1}." ;
  String chinesePattern = "在{1}里面有一个{0}。" ;
  MessageFormat.format(englishPattern, "Windows" , "IE" );  // There is a IE in Windows
  MessageFormat.format(chinesePattern, "Windows" , "IE" );  // 在Windows里面有一个IE


  
6. 你知道算数运算符+ - * / %以及位运算符<< >> & | ^ ~以及关系运算符== != < > <= >=谁的优先级高吗? 
  
表达式(3+1<<2)的值是几?我猜你会猜错。写个代码试试。 
  
若是我想判断a和b异或的结果是否等于0,这样写(a^b==0)对吗? 
  
7. 笔试的时候常常有人问你“i = i++ + i++ + i++”以后i的值等于几,可是你却没有拒绝这个公司的勇气。 
  
=== 函数 === 
  
8. 以下代码 
  mysql

1
2
3
4
template < class T>
  void swap(T &a, T &b) {
      T tmp=a; a=b; b=tmp;
  }


  
这段代码必须放在头文件里,只要一改,全部用到它的代码都要从新编译。并且编译阶段会把每一个用到的数据类型编译一份上述代码,若是每一个.cpp文件里都用到某个共同类型,那么编译器也会给每一个.cpp都编译一份这个函数,连接时还无法优化。 
  
=== 面向对象的编程 === 
  
9. 以下代码: ios

1
2
string a = "foo" ;
  string b = a + "bar" + a + "baz" ;


  
你说不清楚以上代码究竟建立了多少个string对象。 
  
10. 给你一个指针SomeClass* ptr;,你怎么知道ptr指向的对象是SomeClass呢,仍是它的子类呢?或者这么问:给你一个指针,你怎么知道一个指针void *ptr;指向的目标是否是SomeClass呢?只需编程求解“是”仍是“不是”。 
  
答案是:SomeClass *ptr2 = dynamic_cast<SomeClass*>(ptr);。若是ptr真的指向SomeClass,那么它返回这个指针自己;若是不是,则返回NULL。 
  
什么?没人告诉过你?但是Java里有“instanceof”这个运算符,专门用来检测对象的类型的。 
  
11. private继承:你的儿子动不了你的东西,可是你的朋友(friend)能够。这是岂有此理? 
  
12. 菱形继承: 
class A { int x; }; 
class B1 : public A; 
class B2 : public A; 
class C : public B1, public B2; 
那么C里面有几份x? 
若是我建立一个对象C c;那么c.x是哪一个x? 
  
13. 下面的代码为何能编译经过?明明f的参数类型错了。 
  c++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
   
  using namespace std;
   
  class Blah {
      public :
          Blah( int x) : _x(x) {}
          void speak() {
              cout<< "x=" <<_x<<endl;
          }
      private :
          int _x;
  };
   
  void f(Blah b) {
      b.speak();
  }
   
  int main() {
      f(42);
      return 0;
  }


  
14. 为何类的私有成员变量要写在头文件里?那还叫“私有”吗?类的结构变了,即便只改变私有成员变量,全部依赖于这个类的代码都要从新编译。旧的代码若是不从新编译,就不兼容新的。 
  
若是都是个人代码还好,万一我提供的是一个很流行的很经常使用的库,岂不是个人类的结构都不敢改了?一改,全部的用户都要从新编译? 
  
解法:请使用私有实现模式(private implementation,简称pimpl模式) 
  
15. 深拷贝。你能够把对象赋给另外一个变量,能够按值传入一个函数,也能够按值从一个函数返回。可是其中要经历大量的拷贝。 
  
=== 异常处理 === 
  
16. 怎样写一段代码,使得即便出现异常的时候也会被执行?好比,若是打开了一个文件,如何在异常出现的状况下,先把文件关掉,再把异常抛到块或者函数外面? 
  
答案是把这样的代码写在一个对象的析构函数中,并在栈上分配这个对象。C++中只有try-catch,却没有finally语句。惟一可以在异常抛出的时候执行的代码就是析构函数了。 
  
可是,就算是析构函数,也不必定会执行。C++中,异常一旦抛出,运行环境就会从栈顶到栈底,一个帧一个帧地寻找一个能抓住这个异常的catch语句。惋惜,若是这样的catch语句不存在,那么结果是程序当即终止。任何析构函数都不会执行。 
  
17. 编译器处理C和C++混合的代码很是痛苦,由于C的函数根本不知道“异常”这个概念,可是C++编译器必需要处理“一个C++函数调用了一个C函数,这个C函数又调用了一个C++函数……”这样的状况,同时C编译又会容许“一个C函数调用了一个C++函数(用extern "C"提供了接口),这个C++函数又调用了一个C函数……”这样的状况,而C++也必须考虑到这种状况。 
  
=== 编译 === 
  
18. 编译一个C++程序很是慢,一般花上半个小时编译一个不大的程序都是常有的事。我据说过有一个软件须要没日没夜地编译5天才能编译好。 
  
若是想体验一下的话,就开始学习wxWidget吧。即便是只有一个对话框HelloWorld程序,也要用半分钟编译。 
  
其缘由通常是头文件会互相引用,可是由于使用了大量的模板等,每次都要从新处理一堆头文件。并且构建的时候,make程序有时候须要用半分钟的时间才仅仅能知道“哪些文件变了,哪些文件须要从新编译”,更不用说真的编译的时间了。 
  
19. 若是想提升速度,却是能够考虑“预编译头文件”。VC6.0就支持这个功能。GCC也有。就是把一个头文件做为“预编译头”,先用编译器预处理。编译器会缓存预处理的结果。它对你的代码的要求,就是全部的.cpp文件都必须首先引用这个头文件(不然头文件之间会互相影响)。 
  
可是预编译头文件会生成一个几十MB的缓存。固然,磁盘空间多了,也不用担忧它。可是一不当心把它连代码拷贝走了就很差了。就怕你不知道VisualC++生成的xxxxxx.pch是干什么用的。(PCH=Pre-Compiled Header) 
  
这也是为何不推荐一开始就使用IDE的缘由。若是你一开始就用VC,你大概不知道这个stdafx.h为何这么特殊,没了它编译就不经过;把别的include预编译语句放到#include "stdafx.h"以前,也编译通不过,也不知道它究竟是干什么用的,也不知道如何把它从工程里去掉。另外一个没有它的工程编译得慢,你殊不知道怎么把它加到工程里。估计通常教材不会提它。其实它就是那个“预编译头文件”。 
  
=== 二进制接口 === 
  
20. C++容许你进行函数名重载。你能够写几十个函数,都叫同一个名字,好比都叫foo。 
  
但是,当你把这几十个foo编译起来,放到一个动态连接库bar.so(或者bar.dll)里,你用另外一个程序打开bar.so,想从里面取出一个foo函数来调用。可是……糟糕!这么多函数都叫foo!!!怎么知道我要哪一个foo呢?? 
  
实际上C++会把名字进行“混淆”(英文叫mangling),也就是把函数改个名字,把参数的类型以及返回值的类型编入函数中。好比GCC会把上述那个void f(Blah)函数命名为“_Z1f4Blah”。 
  
糟糕的是,不一样的编译器,混淆的方法不太同样。因此,一个程序用编译器A编译,另外一个程序用编译器B编译,那么它们就不能互相调用对方的函数。 
  
想一想为何Qt库的Windows版要分mingw和MSVC(Microsoft Visual C++)两个版本? 
  
21. 不一样的编译器对于“异常处理”不太一致。 
  
22. 刚才说过了类的“私有成员”问题,类一改,就都不兼容了,都要从新编译 
  
=== 一些杂事 === 
  
23. 凡是涉及“模板”的代码,程序编译出错之后,输出的信息都很是难以看懂。 
  
=== 缺失的特性 === 
  
有些东西原本应该写入编程语言中,由于其实现高度依赖编译器,但这些却不是C++的一部分,好比: 
  
- coroutine:相似很轻很轻的线程。它们不能同时执行。一个coroutine必须暂停本身,把控制权交给另外一个coroutine,另外一个适当的时候跳回来。颇有用的结构,适合于“生产者-消费者”模型,也适合于大规模的并行处理,也能够简化不少算法(好比二叉树遍历)。惋惜能实现这个的只是一篇论文介绍的技术,今年(2013)才发表:http://ulir.ul.ie/handle/10344/2927 
  
- 垃圾回收:正式的名字是“自动内存管理”。用于防止“无用单元”(分配出去了内存,但全部指向它的指针都不见了,再也没法访问它)和“悬垂引用”(一个指针,本来指向一个有效的对象,但这个对象的内存被回收了,这个指针变成了一个无效的指针)。Boost库中有“智能指针”(smart pointer),但那是一个极其朴素的引用计数实现,一旦产生循环引用就完蛋了。并且,Boost里有好几种不一样的“智能指针”,很难判断到底应该用哪种指针。此外还有基于tracing(非引用技术)的Boehm GC,但那是一个保守的垃圾回收,它不能识别全部的指针(由于内存里都是数字,不知道哪一个变量是值,哪一个是指针),因此也不能回收全部的垃圾。另外,程序员都假设对象一旦分配了是不会移动的,因此也不能用“移动式的垃圾回收”,难以免内存碎片的产生。(lighttpd啊,你死得好惨啊!!!!你说不是你的错,是malloc的错,说malloc不整理内存碎片,明明有不少空闲内存,就是分配不出来啊!有木有!!可你怎么不说你是用C写的啊!!!C程序员有责任管理内存啊!!!!有木有!!!有木有啊!!!!内存木有了你找谁去喊冤啊!!!!!!!) 
  
  
--git

后面的回复:程序员

1.语言是对机器的抽象,类型就是抽象方法的一种,抽象的目的就是为了隐藏下层细节,后面好几个point都是这个问题。你真的须要知道bool的内存表示么。拿python来讲,你不须要一个数字背后的内存表示是32位整型仍是一个高精度数,这对你是透明的。你能在java里获得一个变量的内存地址么?不过,若是你想写编译器,那么ISO C++ 98标准4.7节明确的写了"If the source type is bool, the value false is converted to zero and the value true is converted to one."。 
  
2.const是一个接口约定,你说本身是const,那么就表明任何状况下都不会修改引用目标。c++ const的问题几乎和java的checked exception同样,必定程度上保证了程序的安全性和正确性,可是带来了接口的复杂。一种手段解决了某些问题,同时确定会带来新的问题。 
  
3.这个问题和问题1同样,须要补充的是,在一些加密或者网络传输的长期下,须要用到指定长度的整型类型以优化性能,c99加入了Standard Integer Types,提供了int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t。 
  
4.c++的bool类型是一种整型,任何整型能够隐式转换成bool,这固然不必定是好的feature,这么作主要是为了保持对c的源代码兼容性。 
  
5.messageformat这条彻底没喷到点子上,java的设计和sprintf几乎同样,就c++算没有等价面向对象的实现,在类库层面实现一个也不困难。java真正的优点在于标准字符串是unicode编码,而c/c++是ansi。 
  
6~9彻底是扯犊子了 
  
7. 从我毕业那年开始我从没见过这样的笔试题。若是真有相似的问题,他想要的绝对不是一个编译器实现决定的准确数值,而是题目里的两个基础知识点:符号贪婪匹配和表达式求值顺序。我把题目改为int n=0; n=n++;你能回答上来么。 
  
  
后面关于头文件、二进制兼容性的几点是说到点子上了。c++不少设计上缺陷的根源都来自一点——保持对c的代码兼容性。这个特性语言发展的初期,吸引了大量的开发者,可是这个沉重的历史包袱随着语言的发展愈来愈凸显。 
  
  
  
语言是对机器的抽象,使得某些复杂的机器特性对使用者透明;另外一方面也是对问题的抽象,将现实世界的问题抽象成类型系统、控制流。 另一点是,透明不是绝对的,这取决于你关注的方面。 
  
好比gc,不管是用引用计数仍是根搜索实现,本质上是对内存模型的抽象,从物理硬件的线性地址模型抽象到彻底对使用者透明。在大部分业务开发的代码里,gc以必定的机器性能换取了人的开发效率。因此说白了gc是一种程序时间和人月的trade off,在不一样的场景下,根据资源约束条件的不一样来取舍。百度、Google的搜索前台server,QQ的通讯server,绝对只可能会用c/c++,由于海量请求、低时延的要求下,性能是很是critical的,同理mysql、memcached、redis、mongodb等等关注单机性能的存储组件无一不是用c/c++,他们不会用任何gc。而对于处理业务逻辑的应用服务器,如今已经基本看不到c和c++的影子了。 
  
  
--
github

 

- coroutine:相似很轻很轻的线程。它们不能同时执行。一个coroutine必须暂停本身,把控制权交给另外一个coroutine,另外一个适当的时候跳回来。颇有用的结构,适合于“生产者-消费者”模型,也适合于大规模的并行处理,也能够简化不少算法(好比二叉树遍历)。惋惜能实现这个的只是一篇论文介绍的技术,今年(2013)才发表:http://ulir.ul.ie/handle/10344/2927  
--协程boost有,并且分为stackless(参考boost::asio)和stackfull(目前比较流行的golang也是这种),看你想怎么用了 
  
- 垃圾回收:正式的名字是“自动内存管理”。用于防止“无用单元”(分配出去了内存,但全部指向它的指针都不见了,再也没法访问它)和“悬垂引用”(一个指针,本来指向一个有效的对象,但这个对象的内存被回收了,这个指针变成了一个无效的指针)。Boost库中有“智能指针”(smart pointer),但那是一个极其朴素的引用计数实现,一旦产生循环引用就完蛋了。并且,Boost里有好几种不一样的“智能指针”,很难判断到底应该用哪种指针。此外还有基于tracing(非引用技术)的Boehm GC,但那是一个保守的垃圾回收,它不能识别全部的指针(由于内存里都是数字,不知道哪一个变量是值,哪一个是指针),因此也不能回收全部的垃圾。另外,程序员都假设对象一旦分配了是不会移动的,因此也不能用“移动式的垃圾回收”,难以免内存碎片的产生。(lighttpd啊,你死得好惨啊!!!!你说不是你的错,是malloc的错,说malloc不整理内存碎片,明明有不少空闲内存,就是分配不出来啊!有木有!!可你怎么不说你是用C写的啊!!!C程序员有责任管理内存啊!!!!有木有!!!有木有啊!!!!内存木有了你找谁去喊冤啊!!!!!!!)  
--个人理解GC仅仅是内存回收的一种捷径,c++里面包括不少资源类型,好比文件描述符,锁之类的,并且java貌似只是从必定程度上解决了内存问题,可是引用计数这种辅助的东西,对于资源管理仍是不能不用。 
http://microcai.org/2013/07/27/gc-is-wrong-way-of-doing-memory-managment.html,我以为这个比较中肯 
  golang

——

http://microcai.org/2013/07/27/gc-is-wrong-way-of-doing-memory-managment.html,我以为这个比较中肯 
  
RAII确实是C++的风格。是处理资源的合理方法。 
  
不过,除了内存之外的其它资源的管理,如文件、网络链接、锁等,确实不该该靠垃圾回收来管理。垃圾回收也不是为管理这些资源设计的。(注意“引用计数”只是一种垃圾回收的方法,更多的灵活的方法是“跟踪”:从“根对象”扫描整个堆,并扔掉没有触及的对象) 
  
对于Java来讲,等效于RAII的是try-finally,而不是垃圾回收。资源在try中获取,而finally保证try执行以后必定会执行(无论是正确仍是错误的状况)。finally和C++中栈上对象的析构函数是相似的,但即便没有任何catch能抓住异常,它也会执行。 
  
Python 2.5中增长了with语句。在with块结束的时候,它绑定的资源必定会被处理。如: 
  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 旧写法(危险,但很流行)。依赖于引用计数。
  # 若是Python的实现不用引用计数(Jython、PyPy等)就会形成资源泄漏。
  txt = open ( "somefile.txt" ).read()
   
  # 旧写法(安全),保证f会关掉
  f = open ( "somefile.txt" )
  try :
      txt = f.read()
  finally :
      f.close()
   
  # 新写法:保证f.__exit__()必定会执行(它会调用f.close())。
  with open ( "somefile.txt" ) as f:
      txt = f.read()


  
Java 1.7中增长了try-with-resource结构,更增强化了这种“将资源绑定在静态的做用域上”的概念(C++也是用栈上对象的做用域与资源绑定,这一点是相通的)。 
  

1
2
3
4
5
6
7
8
9
10
11
12
// 旧写法
  BufferedReader br = new BufferedReader( new FileReader(path));
  try {
      return br.readLine();
  } finally {
      if (br != null ) br.close();
  }
   
  // 新写法
  try (BufferedReader br = new BufferedReader( new FileReader(path))) {
      return br.readLine();
  }


  
Ruby自己对块支持得很好。通常这种资源能够用函数将其限制在传入的回调块中。 

1
2
3
File : :open ( "somefile.txt" ) do |f|
      puts f.read
  end


  
甚至对锁也能够这样作。Ruby文档里的例子: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'thread'
  semaphore = Mutex. new
   
  a = Thread . new {
    semaphore.synchronize {
      # access shared resource
    }
  }
   
  b = Thread . new {
    semaphore.synchronize {
      # access shared resource
    }
  }


  
Scala有scala-arm库作自动资源管理。实际上只是用Ruby的风格封装了一下Java的文件等资源。https://github.com/jsuereth/scala-arm 
  
  
但不得不说,老Python风格的用垃圾回收来自动关闭文件,是不正确的。(我猜测microcai反对的也正是这种用法,而不是垃圾回收自己)起码这种作法将语言限定在“非延迟的引用计数”这种垃圾回收策略上,而这种策略的性能不好。PHP也是由于不少程序依赖这种策略,以致于使用了其它垃圾回收方法就会致使程序不正确。Facebook的HipHop虚拟机(高性能的PHP虚拟机)实现起来很吃力,也是由于这个。 
  
  
固然,能够将内存也做为“资源”的一种,用一样的方法,经过将动态的资源绑定到静态的资源上,以保证安全地分配和回收。可是,我我的认为内存具备本身的特殊性,因此适合使用垃圾回收,而不是手动地管理。 
  
- 内存的“回收”并无时效性。若是文件不及时关闭,磁盘上的文件多是不完整的,其余进程可能看不到已经写入的数据。锁不及时释放,会阻塞其它线程,甚至形成死锁。网络链接若是不及时关闭,对端会认为你没有传输完。可是内存若是不及时回收,最严重的后果不过是一个进程暂时占用了多于实际须要的内存,垃圾回收事后,内存就会恢复合理的占用量。实际上,“回收”这个动做自己是要花时间的。若是一旦内存不用就必须当即回收,还要挤占程序执行的时间。 
  
- 在一个对象被复杂地共享的环境中,很难肯定对象生存期由哪一个对象维护。若是一个对象产生之后,传递给了其它模块,并且是多个模块,那么任何单个模块都没法决定这个对象是否还要保留。 
  
- 将内存管理的负担交给程序员,轻则加剧程序员的负担,重则增长受打击面,容易引起更多的问题。在构造复杂的数据结构的状况下,若是内存管理必须由程序员显式地进行,那么数据结构也会变得复杂。 
  
例如:如在用引用计数和智能指针实现环形链表的时候,必须让正向引用使用强引用,而反向引用使用弱引用,以免环形引用使得内存没法释放。这样,程序员不但要考虑链表的结构正确性,还要考虑引用类型的正确性。并且必须考虑若是去掉其中一个环节就会致使下一个环节的强引用计数变为0,而后致使以后一系列的对象要自动析构。尽管增长了复杂度,但是弱引用却并非为这种情形设计的:弱引用的代价比强引用更大。 
  
  
总结一下,垃圾回收(正如它正式的名字叫“自动内存管理”同样)是适合内存管理的机制,但并不适合管理全部的资源。不少支持垃圾回收的语言都有专门的机制(try-finally或者with结构)来实现更好的资源管理。 
--

这是传说中的丧心病狂嘛~ 
  
首先C++的原始设计方案就对于底层开发来讲就是糟糕的~这也就致使了Bjarne Stroustrup等人在后续的更新设计上变得愈来愈保守和没有自信。 
  
从根本性上来讲,C++ 0x的gc设计仍是语法糖。须要回收内存时,以扫描大面积内存的时间开销代价来保证内存管理的尽量有效! 
  
这种设计至少对于底层开发者来讲是不能妥协的!而若是用于应用层开发,那为何不选择一种垃圾回收更为优美、更容易书写的语言呢?(好比Java、C#...虽然它们的问题也是一大坨) 
  
这就是一个Check&Balance的过程了,也是C++标准制定者们头疼到死的课题。

 

语言好坏的讨论,老是停留在语言特性的层面上,剩下的看客抛出一句「各有各的用途」,实在是太没养分了。 
  
初学者刚一门语言的时候,每每注意力都集中在语法特性上,由于目标是写出能运行的代码。实际上细节的语法特性只是语言设计哲学的体现。现实世界里,选择编程语言时语法特性是一个很是次要的因素。 
  
  
你们喜闻乐见的cpp和java,它们最主要的区别是什么,是GC?多重继承?bool类型?标准库?编译?异常处理?根本都不在点子上,他们最核心的区别是,Java跑在JVM这个虚拟机上,而cpp只依赖一个运行时,JVM这一层抽象是两者各类差异的关键。另外一个例子是perl和python,python的哲学是"There should be one -- and preferably only one --- obvious way to do it", 而perl的则是"There's more than one way to do it."。换个角度,尝试从语言的设计思路和哲学上来比较分析不一样的语言,不要总纠结在语言特性上。 
  
社区、历史、演进和外围工具也是其余几个能够关注的方面。 
  
--

: 有几条确实是比较无厘头,我认为你是认真,因此我解释下。 
: 1.语言是对机器的抽象,类型就是抽象方法的一种,抽象的目的就是为了隐藏下层细节,后面好几个point都是这个问题。你真的须要知道bool的内存表示么。拿python来讲,你不须要一个数字背后的内存表示是32位整型仍是一个高精度数,这对你是透明的。你能在java里获得一个变量的内存地址么?不过,若是你想写编译器,那么ISO C++ 98标准4.7节明确的写了"If the source type is bool, the value false is converted to zero and the value true is converted to one."。 
  
嗯 
  
: 2.const是一个接口约定,你说本身是const,那么就表明任何状况下都不会修改引用目标。c++ const的问题几乎和java的checked exception同样,必定程度上保证了程序的安全性和正确性,可是带来了接口的复杂。一种手段解决了某些问题,同时确定会带来新的问题。   
  
说的在理。确实以为和Checked Exception有一样的问题。 
  
: 3.这个问题和问题1同样,须要补充的是,在一些加密或者网络传输的长期下,须要用到指定长度的整型类型以优化性能,c99加入了Standard Integer Types,提供了int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t。 
  
关键的是“常量”,也就是42, 42L, 42LL之间的区别。这些类型虽然规定了定长的数据类型,可是没有定长数据类型的常量。有可能这位同窗的这个用途比较特殊,常量的类型也会决定程序的正确性。并且这个bug他调试了一天多才发现是使用了L而不是LL,使得在32位机上是32位,64位机上是64位。 
  
: 5.messageformat这条彻底没喷到点子上,java的设计和sprintf几乎同样,就c++算没有等价面向对象的实现,在类库层面实现一个也不困难。java真正的优点在于标准字符串是unicode编码,而c/c++是ansi。 
  
这个吐槽的是iostream库,并非C++自己。 
  
Qt中的QString是支持按位置替换的。QString("There is a %1 in %0").arg("Windows").arg("IE"); 
  
: 6~9彻底是扯犊子了 
  
6~7是纯粹黑,8和9是认真的。 
  
8是关于模版。模版确实会形成代码爆炸。 
  
9若是是字符串,还比较轻松;若是是高精度整数,就要当心了。GNU-MP的C++绑定专门为C++优化了,使得相似a=b+c这样的表达式能够用一个加法运算完成。 
  
一旦涉及对象按值传入函数和按值返回,就和第15条同样了。 
    
: int n=0; n=n++;你能回答上来么。 
  
也是未定义行为(一个表达式里两个部分有反作用)。用gcc4.8编译是1,用clang3.3编译是0。 
  
: 后面关于头文件、二进制兼容性的几点是说到点子上了。 
  
嗯。这几个是认真的,尤为是二进制兼容性。 
  
: 百度、Google的搜索前台server,QQ的通讯server,绝对只可能会用c/c++,由于海量请求、低时延的要求下,性能是很是critical的…………他们不会用任何gc。 
  
根本的决定因素仍是性能。我认为之因此没有选择垃圾回收的语言,根本缘由是目前非垃圾回收的语言能够比垃圾回收的语言跑得更快。可是若是在虚拟机上跑,Type Inference和JIT-Compiling能够提供Ahead-of-time Compiling语言(如Fortran/Ada/C/C++/Rust/Go等)没法触及的优化机遇。固然这两个是将来的技术(虽然上个世纪90年代早就有人研究过),也许暂时应用还比较少。 
  
: 同理mysql、memcached、redis、mongodb等等关注单机性能的存储组件无一不是用c/c++,他们不会用任何gc。 
  
举一个反例吧:CouchDB,也是以高性能著称的数据库,和mongodb相似。使用Erlang语言实现的,而Erlang是在BEAM虚拟机上跑的函数式语言,而函数式语言必须使用GC。 
  
  
--

相关文章
相关标签/搜索