Chinaunix首页 | 论坛 | 博客
  • 博客访问: 465584
  • 博文数量: 79
  • 博客积分: 848
  • 博客等级: 军士长
  • 技术积分: 1800
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-26 19:30
文章分类

全部博文(79)

文章存档

2015年(4)

2013年(39)

2012年(36)

分类: C/C++

2015-04-03 14:06:07

虽然现在一直做得是java的开发工作,但是对于c++一直念念不忘,于是乎得空就拿起了深入理解c++面向对象模型一书读了起来。两厢印证,颇有几分乐趣。从一个c++程序员转做java开发,并不困难,想要做一个优秀的java程序员却也并不容易。感觉JAVA开发确实方便、安全、敏捷,想要用好java要学习的东西不少,网络编程,jvm等等,但是对于一个开发层面的人来讲,可能并不需要深入的了解jvm的工作机制,就可以写出工作良好的代码,因为很多第三方的jar包完成了工作中需要解决的大部分问题。c++则不同,始终觉得要写好c++代码并不容易,因为c++很多的敏感地方,太容易让你接触到。说白了就是内存操作比较随意,因此很多的bug源于这种自由。内存操作的自由是把双刃剑,对于高端的c++程序员来讲是一件神兵利器,很多业务场景需要通过特殊的内存控制来提高运行效率;但是对于初级程序来讲,无意识的内存操作却也很容易的造成意想不到的严重后果。java、c++孰优孰劣的讨论被各大论坛津津乐道,个人以为这种谈论颇为滑稽。“存在即合理”两种语言都是在相应的应用背景下产生的,各有所长。这里不讨论语言的优劣,仅仅是对于规则的总结。
说到拷贝构造函数(copy constructor),使用c++的人都不陌生。概念这里不再重述,它的调用发生在当声明一个对象时用另外一个对象对这个对象进行初始化操作的时候。这句话表述的很拗口,但是条件缺一不可。
顾名思义,拷贝构造函数。首先应该是一个构造函数,所谓构造函数,是指用来构造一个对象的函数,所以只有在构造对象的时候才会调用(听起来好像是一句废话)。来看下面的例子:

点击(此处)折叠或打开

  1. class A{
  2. };

  3. A a;//发生了构造行为调用构造函数
  4. A aa;//发生了构造行为调用构造函数

  5. A aaa = a;//发生了构造行为,一般情况下会调用拷贝构造函数
  6. A aa = a;//未发生构造行为,调用重载的“=”运算符

另外的两类情形,一般情况下回调用拷贝构造函数:

点击(此处)折叠或打开

  1. void test(A a);//当对象最为一个函数的形参的时候
  2. A test();//当对象作为一个函数的返回值的时候
假如class A显示的声明了一个A(const A &a)拷贝构造函数,上面的提到的几种情形一定会调用拷贝构造函数,但是假如class A中并未声明拷贝构造函数。编译器是否会调用一个默认的拷贝构造函数呢?这是不确定的!原因就是,我们在没有看到A的具体声明之前,是不能确定A是否符合Bitwise Copy semantics(位逐次拷贝)的。当我刚看到这个名词的时候,也是非常的疑惑,似乎从来没有听过这个概念。
具体来解释一下就是,当发生对象拷贝的时候,如果该对象内部并没有声明一个拷贝构造函数,一个良好的编译器可以对大部分的对象进行bitwise copies。简单来说将是讲一个对象内存空间的内容按位拷贝到另外一个对象的内存空间之中,而不会调用拷贝构造函数。
举个例子来说明下:

点击(此处)折叠或打开

  1. class A
  2. {
  3. public:
  4.     A(const char* s)
  5. {
  6.         name = new char[strlen(s) + 1];
  7.         strcpy(name, s);
  8.         age = 10;
  9. }
  10.     ~A()
  11.     {
  12.         delete name;
  13.     }
  14.     char * name;
  15.     int age;
  16. };

  17. int main()
  18. {

  19.     A a("ddx");
  20.     A b = a;
  21.     cout << a.name << endl;
  22.     cout << b.name << endl;
  23.     cout << sizeof(a) << endl;
  24.     cout << "a和b的内存位置" << endl;
  25.     cout << (unsigned long)&a << endl;
  26.     cout << (unsigned long)&b << endl;
  27.     cout <<"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"<<endl;

  28.     cout << "a的内存布局" << endl;
  29.     cout << (unsigned long)(a.name) << endl;//3413920
  30.     cout << (unsigned long)&(a.name) << endl;
  31.     cout <<"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"<<endl;

  32.     cout << "b的内存布局" << endl;
  33.     cout << (unsigned long)(b.name) << endl;//3413920
  34.     cout << (unsigned long)&(b.name) << endl;
  35.     return 0;
  36. }
从运行结果可以看出,a 和 b两个对象中的指针,指向了同一块内存,a和b两个对象中成员变量的值是完全一样的,在对象b初始化的时候,编译器做得操作是为b开辟一块内存(该内存的大小是根据class A的声明决定,在class A中有一个指针占四个字节和一个int型的数值也占了4个字节因此,类A的所有实例在内存中占有8个字节的空间),然后将对象a的内容,逐个字节的拷贝到b的内存空间之中,这就是所谓的bitwise copy semantics。那么如何来判断一个类是否符合bitwise copy sematics呢?
c++面向对象模型之中又如何表述,当存在下列情况时类不符合bitwise copy semantics。
1、当类含有对象类型的成员变量,而该成员变量的类声明中含有拷贝构造函数的时候(该拷贝构造函数包括编译器自动生成的)。
2、当该类的父类存在拷贝构造函数的时候。
3、该类声明了一个或者多个虚函数。
4、该类派生自一个继承串链,其中存在虚基类的情况。

除了上述的四种情况,其他的类如果未声明拷贝构造函数的情况下,将不生成默认的拷贝构造函数,而直接进行bitwise copy。

参考:深度探索c++对象模型


阅读(2509) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~