C++随记(2)

很简单概念的复习,对构造函数、拷贝构造函数和赋值函数:

  1 #include <stdio.h>

  2                           

  3 class Bar

  4 {

  5   public:

  6     Bar() {printf("ctor\n");}

  7     Bar(Bar& b) {printf("copy ctor\n");}

  8     Bar& operator = (const Bar & b) {printf("assignment =\n"); return *this;}

  9 };

 10 

 

 11 void foo(Bar b)

 12 {

 13 }

 14 

 15 int main()

 16 {   

 17     printf("Ctoring bar1\n");

 18     Bar bar1;

 19     printf("Ctoring bar2\n");

 20     Bar bar2 = bar1;

 21     printf("Ctoring bar3\n");

 22     Bar bar3(bar1);

 23     printf("Assigning bar3\n");

 24     bar3 = bar2;

 25     printf("Pass by value bar3\n");

 26     foo(bar3);

 27     try

 28     {

 29         try 

 30         {   

 31             printf("Throwing bar3\n");

 32             throw bar3;

 33         }   

 34         catch (Bar& bar)

 35         {   

 36             printf("Rethrow bar3\n");

 37             throw;

 38         }

 39     }

 40     catch (…)

 41     {

 42     }

 43 }

       

 
输出结果为,特别注意拷贝构造函数的几种调用方式,特别是最后的异常处理,强制pass by value,即便声明了形参为引用传递。
Ctoring bar1
ctor
Ctoring bar2
copy ctor
Ctoring bar3
copy ctor
Assigning bar3
assignment =
Pass by value bar3
copy ctor
Throwing bar3
copy ctor
Rethrow bar3
 
p.s. OSX终端的拷贝功能居然包括控制台背景和颜色。

魔都地铁的新名字

前不久看到网上流传一个上海地铁线路图日文版(题图),除了专业的标示以外(比如急行),还给一些线路起了文字名称。

 

大家都知道,大部分国内的地铁的线路都是按照数字进行编号。少数的例外是深圳,不过现在又都加上了数字号,另外就是北京的一些郊区线路,比如房山线、昌平线、八通线等等。数字编号本身没什么特殊意义,难以记忆和联想,对于不熟悉线路的乘客较不方便。

 

反观海外的一些城市,线路大部分以文字命名,或者是文字+数字的方式,比如东京、香港、伦敦和纽约。计划经济的老大哥——莫斯科地铁也是使用文字命名。

 

一般来说,线路的文字命名有以下几个规律(以东京、香港为例):

  • 连接主城区和郊区的线路,以郊区命名,乘客对于线路走向一目了然,如荃湾线、东涌线

  • 穿过市区知名地区(车站)的线路,以该知名地命名,如有乐町线、银座线

  • 以线路走向命名,如东西线、南北线

  • 以线路走行的区域命名,如港岛线

 

不才斗胆按照一些惯例给上海的地铁线路取新名字:

 

1号线——南北线:纵贯市区南北,基本沿着浦西的中轴线,北段和南北高架完全重合;

 

2号线——东西线、机场线:串起两大机场,基本上完美地把市区划成南北两块;

 

3号线——明珠线、宝山线:3号线本名就是明珠线,另外其直通宝山城区

 

4号线——内环线、环线:很直白

 

5号线——闵行线:只在闵行腹地穿行

 

6号线——浦东线:只行驶在浦东地界

 

7号线——美兰湖线、静安寺线

 

8号线——杨浦线:穿过杨浦核心地区

 

9号线——松江线:连接松江与主城区

 

10号线——虹桥线:虹桥枢纽重要线路,连接机场2个航站楼和火车站

 

11号线——嘉定线:连接嘉定与主城区

 

12号线——龙华线、复兴岛线:其实这两个似乎都不太响亮,如果叫提篮桥线那绝对霸气侧漏

 

13号线——世博线、普陀线:当年世博园内区唯一的地铁线路

 

16号线——南汇线、滴水湖线

 

22号线——金山线

 

当然,现在数字命名的地铁线路已经深入人心,不可能再更改,所以也只是YY。但是如果能借鉴北京的做法,把郊区线路单独用文字命名,不失为一个两头兼顾的好办法。

 

===========================

 

小白小黑开了个微信公众号,公众号 – “黑白杂谈”, 微信号 talking_ted,或者扫描下面的二维码,欢迎大家踊跃关注。

C++随记

在析构函数中抛出异常,如果这时候已经有一个异常的话,程序会崩溃。

参考:http://www.cnblogs.com/KevinSong/p/3323372.html  ,以及《Effective C++》第8条。


  1. 构造函数中抛出异常,不会自动调用析构函数。但是已经分配的内存会自动释放,另外已经自动构造的成员变量会调用析构函数。
  2. 例如,在new Bar()中,Bar的构造函数抛出一个异常,不会自动调用~Bar(),成员变量的析构也不会调用
  3. 如果是成员变量的构造抛出异常,情况类似,不会调用成员的析构,即使是已经构造过的变量new结果返回NULL,因为对应的内存已经被释放。
  4. 结果是很有可能造成内存的泄露和资源未释放
  5. 抛出异常时,在退出当前堆栈之前,会自动析构当前作用域的本地变量。这也是C++异常处理不需要finally的原因。如果是动态构造出来的还需要手动处理,或者使用类似smart pointer机制。

参考《深度探索C++对象模型》7.2节。

就上面两条短评一下C++的异常处理机制。相对与Java当中的广泛应用,C++中的异常处理似乎受到人们的冷遇。我想有以下几个原因

  1. C++很多情况下被当做面向对象的C使用,一般局限在封装、继承和多态,高级功能使用得不多。Java没有类似的包袱,异常在各个类库里都是重要的组成部分。
  2. Java的finally机制更容易理解。stack unwinding的想法固然好,但是使用起来却容易顾此失彼。
  3. 缺乏checked exception机制。一个函数如果可能会抛出异常,那就需要在编译期解决这个问题。调用一个第三方类库,却不知道什么时候会抛出异常,用的人也是步步惊心。

在父类构造函数中调用虚函数,由于子类还没构造完成,虚函数表未建立,调用的还是父类的实现。如果调用的是纯虚函数,则程序会直接退出。事实上gcc会给出编译警告。

把子类变量赋值给一个父类变量,即使传值拷贝,也不会拷贝虚函数本身。即父类变量(不是引用不是指针)无论如何不会调用子类的虚函数。