java 走向 C++

来源:互联网 发布:室内装饰效果图软件 编辑:程序博客网 时间:2024/06/05 14:15
 本文不涉及一些微妙蛋疼的语法比较, 关注的是宏观方面, 当然后期逐步更新如果, 如果觉得必要, 可能会加上.


议题之一: 初始化的比较

1. CPP基类的任何类构造函数会默认调用父类的不带参数的构造函数, 这点Java也是同样处理的. 子类调用父类的构造函数方式会不同, C++中这样做:

Son::Son(int id):Father(id) {

    //Father(10); //注意,这样虽然编译通过,但事实上在构造一个Father类,完了( 出了作用域)之后就立即销毁. 危害非常之大.

    cout<<"in Sonconstructor Son(int id) function!"<<endl;

}

 

2. Son son 这样的方式初始化, 会将一个对象初始化在栈上, 当超出作用域, 自动消除. Son *son这样的方式初始化, 会将一个对象放在堆上, 需要手动Delete, 否则内存泄漏. Son* pSon这样的方式, 没有构造任何对象.

3. 上面也意味着, 除基本类型外, Java的对象都是在堆中分配的.

4. 构造和析构的基本顺序是《看源代码》:

view plaincopy to clipboardprint?
  1. $ ./app.exe  
  2. In Main Function!  
  3. in FatherItem constructor FatherItem()  function!  
  4. in father constructor Father(int id) function!  
  5. in SonItem constructor SonItem()  function!  
  6. in Son constructor Son(int id) function!  
  7. in Son destructor ~Son() function!  
  8. in SonItem destructor ~SonItem() function!  
  9. in Father destructor ~Father() function!  
  10. in FatherItem destructor ~FatherItem() function!  

       上面是子类调用父类带参数函数的例子, 基本说明的情况是:

a.      父类的成员变量初始化

b.      父类的构造函数初始化(看子类调用那个父类的构造函数,默认情况是不带参数的)

c.      子类的.h文件中声明的成员变量初始化

d.      子类的构造函数调用

析构的顺序正好相反.

这跟Java实质上是一致的, 微弱的区别在于语法表达不同:注意C++引起成员变量的调用, 要么是Son son, 要么是Son* pSon = new Son(); 类似于Son* pSon是没有做初始化的. 不会引起构造函数的调用;而Java中持有的都是引用. 这点需要特别关注, Java中的Son son = new Son()类似于Son* pSon = new Son();(Java中的Son son类似于C++中的Son*pson, 没有做初始化工作). 参见2.

5. 关于静态成员变量.

a.      Java的静态成员变量在类加载的时候就完成. 类加载是指, 类的首次使用,比如类被构造, 类的其他静态函数或者成员被使用. 因此他会先于所有的成员被初始化. 子类的static成员初始化之前,会先初始化父类的static成员变量, 当静态成员初始化完成之后, 然后参考4的顺序.

b.      C++的静态成员变量.

在C++中, 声明静态成员函数是放在.h文件中:

view plaincopy to clipboardprint?
  1. public:  
  2.     static void static_function();//静态成员函数   
  3. private:  
  4.         FatherItem itemOne;  
  5. FatherItem static static_fatherWithPara;  

将成员变量初始化是放在cpp文件中, 注意跟类定义是类似的:

void Father::static_function() {

    // TODO Auto-generated destructor stub

    cout<<"in Fatherstatic_function function!"<<endl;

}

 

FatherItem Father::static_fatherWithPara(10);

当然,结论跟Java是一致的,也即, 类首次加载的时候,完成静态变量的初始化, 加载子类前,首先父类务必先加载. 当类加载成功之后, 在考虑普通变量的初始化,以及构造函数的调用.

 

6. C++的普通成员初始化.

.h文件中的声明的普通对象被初始化, 比如下面这个:

private:

FatherItem fatherIt;

声明的时候, 先初始化fatherIt, 然后构造函数被调用。跟4的解释一致。但这里就会引申出另外一个问题, 带参数的变量声明,会发生什么, 比如:

private:

FatherItem father(1);

这里要务必注意,这是一个语法错误. 源于声明中存在的二义性问题, 因为我们声明函数也是这么声明的:

 

private:

    ReturnValue FunctionName(Parameter name);

 

    这样编译器看到这个就懵了. 为了清晰起见,我们把这样的声明,还是留给了函数.我们只在头文件中声明变量, 然后在构造函数的外部去定义/初始化这个变量:

在头文件中:

view plaincopy to clipboardprint?
  1. private:  
  2.     FatherItem fatherWithPara;  

在.cpp文件的构造函数中:

view plaincopy to clipboardprint?
  1. Father::Father():fatherWithPara(10) {  
  2.     cout<<"in father constructor Father() function! begin"<<endl;  
  3.     cout<<"in father constructor Father() function! end"<<endl;  
  4.    
  5. }  

这样会带来一个弱点,会发生我们不愿意看到的情况,就是.h中声明的变量,实质上已经被它的默认(即不带参数的)构造函数初始化了, 然后我们在构造函数外部,有对这个变量初始化一次.

    ——很幸运,这个推断是错误的,结论是: 构造函数外初始化的变量, 在.h中不会被初始化. 也就是说,仅仅初始化一次. 先看我们的测试,马上就可以论证这个结论.

做一个如下的测试,彻底看看对象的初始化过程:

声明两个变量,一个带参数,一个不带参数,这个在头文件中:

 

view plaincopy to clipboardprint?
  1. private:  
  2.     FatherItem itemOne;  
  3.     FatherItem fatherWithPara;   
然后在构造函数中初始化fatherWithPara, 但不初始化itemOne, .cpp的代码如下:

view plaincopy to clipboardprint?
  1. Father::Father():fatherWithPara(10) {  
  2. cout<<"in father constructor Father() function! begin"<<endl;  
  3. cout<<"in father constructor Father() function! end"<<endl;  

运行代码可以发现,顺序如下:

$ ./app.exe

In Main Function!

in FatherItem constructor FatherItem()  function! //头文件的声明产生的调用

in Constructor FatherItem(int  i) function! 10     //构造函数外调用

in father constructor Father() function! Begin     //构造函数调用

in father constructor Father() function! End       

in Father destructor ~Father() function!

in FatherItem destructor ~FatherItem()function!

in FatherItem destructor ~FatherItem()function!

    进一步搞定这个问题的方案是: 即使我们.h文件中的变量声明过了, 我们依然在构造函数的外部, 进行再次初始化:

不难发现, .h文件中声明的变量,如果在构造函数外部被初始化了, 则声明中不会再次初始化.

7. 有必要总结一下整个初始化,流程,结合4,5和6. 为描述方便, 假如Son son出现在Main函数中, 即son对象需要被初始化:

A.     初始化静态变量, 按照先父(Father类)后子(Son类)的顺序:

1.        加载静态变量所在的类

2.        加载类同时, 立即初始化该类的所有静态成员变量.

B.     按照先父(Father类)后子(Son类)的顺序, 完成以下操作, 注意下面是对一个类进行的操作.

1.        初始化类的.h文件中声明的变量(并满足在构造函数外没有被初始化). 调用的是默认构造函数(即不带参数的). 如果在构造函数外出现了成员变量的, .h文件中的变量无需初始化.

2.        调用构造函数外所列出的所有成员变量名称涉及的构造方法.

3.        调用类的构造函数.

 

8. 有必要约定的规则:

a.      .h中永远只做声明, 声明了任何变量, 务必在构造函数的外部进行初始化, 即使是不带参数的变量.

b.      .cpp中永远只做定义.

c.      摒弃一切奇技淫巧. 比如今天看到的.h中的声明, 变成定义这样的问题.

 

附上源码:

      源码地址

源码说明:

1. 包含Java和CPP的源代码. 理论上两者都是跨平台的. CPP的是用GCC编译器, 当然用VS肯定是没有问题

2. 主要成员说明: Father类, 持有成员变量FatherItem. 

3  Son类, 持有成员变量SonItem

4  Father 和 son是继承关系

5. SonItem和FatherItem没有任何关系.


 

议题之二: 数据管理

我的个人偏见是,内存管理,实质上是数据管理. 后期展开该话题.

 

函数参数传递, 全局变量的管理模式, 成员变量的设计思想.

 

 

议题之三: 第三方代码的引入或者导出

 

源码的引入, 比如java会采用import, C++会采用include, using namespace的方式

 

二进制代码模块的引入, java会引入jar包. C++会引入静态库, 共享库/动态库等.

http://blog.csdn.net/ostrichmyself/article/details/6035538