[温故而知新] 《深度探索c++对象模型》——构造函数

来源:互联网 发布:访客网络需要开启吗 编辑:程序博客网 时间:2024/05/16 05:04

前言:
由于平时工作时候大部分时间写Java,虽然之前花了不少时间学习C++,很长时间不写,回头要写,又发现很多东西又手生了,这里做个总结备忘,顺便把C++的一些东西和java比较。

c++相对于c,编译器干了好多事,而理解c++关键的点,就是理解编译器帮我们做了什么事,以及为什么做这些事。

1.默认构造函数 default constructor
首先理解什么是默认构造函数?
我目前的理解,设计默认构造函数的目的,就是在我们定义一个对象时(给它分配内存空间),让编译器能自动帮我们做一些事情。
比如我们有个Point类如下:

classs Point{public:    int x;    int y;    Point();}

如果我们希望每次定义一个Point的时候,编译器帮我们自动把x和y赋值为0,这样我就可以少敲很多代码,这时候就可以这么做:

Point::Point(){    x = 0;    y = 0;}

好了,这是我们和编译器的一个小约定,“每当定义一个Point对象,给它分配内存空间的时候,编译器得确保我们的默认构造函数得到执行。”

那我们定义一个Point对象,给它分配空间,这会在哪些情况下发生呢?

class Line{public:    Point start;    Point end;}//1.直接new 一个PointPoint *p = new Point;//2.Point作为Line的成员Line *l = new Line;

上面的例子,Point的默认构造函数都得到执行,这是我们跟编译器之间的约定,即使Line没有自己的构造函数,但是编译器也得保证我们的约定有效,它帮我们合成了一个。

那如果Line有自己的构造函数呢?比如下面这样:

class Line{public:    int length;    Point start;    Point end;    Line(int len);}

如果我们是编译器的设计者,该怎么办?太easy了,就在Line的构造函数前面插入一些代码,保证Point的那些构造函数得到执行,不就o了?实际上编译器就是这么干的。

OK,小结一下上面的例子,要说明的就是:我们希望当为一个对象分配空间的时候,编译器自动执行一个动作,默认构造函数。实际上,编译器也会有跟我们类似的需求,那就是 c++ 对 virtual 的实现。c++在实现virtual function 和 virtual class的时候,需要在对象安插一个vptr指针,指向对应的vtable,因此也必须在初始化对象(给对象分配内存空间)的时候,正确的设置vptr的值。

2.拷贝构造函数 copy constructor
拷贝构造函数的声明方式:

class Line{public:    Point start;    Point end;    Line(const Line&);  //拷贝构造函数}

下列三种操作属于copy构造:

//1.Line line1;Line line2 = line1;  // copy 构造//2.作为函数参数void printLine(Line line){    ...}//3.函数返回前,会执行 copy 构造Line getLine(){    Line line;    ...    return line;}

上面的第3个例子,让我想到一个点,学过汇编的同学应该知道,函数的返回值是不可能返回任意大小的对象的,一般是把返回值放在EAX寄存器中返回,那么返回一个大对象是怎么实现的呢?
有两种实现方案,不过都是把返回值改造为函数的参数,一种用引用传递,另一种用值传递,
下面例子是改为值传递的例子(伪代码):

Line tmpInStack;getLine(tmpInStack){   //其实跟上面的第2个例子类似    Line line;    ...    tmpInStack.Line(line);    ...}

上面的伪代码只说明一种思路,实际上编译器还会做一些优化。

好了,现在想一下什么是拷贝构造以及为什么需要它?

其实跟上面的默认构造非常类似的思路:
以上面的 Line 类为例子
1.我们希望,Line类的对象发生拷贝构造的时候,编译器自动调用拷贝构造函数做一些事情。
2.与编译器实现virtual function、virtual class 相关。如果一个类有virtual function 或者 继承自 某个virtual base class,那么这个类的对象在拷贝的时候,对象的vptr应该被正确地设置好,使其正确指向相应的vtable。这个就是编译器自己的需求了。


构造的时候一些注意点:
1.在构造函数里使用memset和memcopy的方式进行构造,对于那些有vptr的对象,将导致错误。
2.class member的初始化在member initialization list中完成在一些情况可以减少中间object的产生。
3.member initialization list中的初始化,不应该依赖于顺序。
4.member initialization list可以调用class的member function,但是不知道member function 对于自身class的依赖程度,有可能踩坑。
其它:
纯虚函数的声明语法:
class Widget{
public:
virtual void flip() =0;(与Java的abstract类似)
}

0 0
原创粉丝点击