【补足基础】C++类中拷贝构造函数详解【转】

来源:互联网 发布:mysql架构图 编辑:程序博客网 时间:2024/04/29 15:40

a. C++标准中提到“The default constructor, copy constructor and copy assignment operator, and destructor are special member functions.[Note: The implementation will implicitly declare these member functions for some class types when the program does not explicitly declare them. The implementation will implicitly define them if they are used.]”。即缺省构造函数、拷贝构造函数、拷贝赋值操作符和析构函数是特殊成员函数。

 

b. “Constructors do not have names. A special declarator syntax using an optional sequence of function- specifiers(inline, virtual and explicit) followed by the constructor’s class name followed by a parameter list is used to declare or define the constructor.” 构造函数没有名称。

 

c. 构造函数不能有返回类型,也不能由virtual, const, static 和 volatile来修饰。但可以由inline来修饰,事实上隐式构造函数就是用inline来修饰的。inline表示编译时展开,通常速度快;virtual表示运行时绑定,通常意味着灵活。

 

d. 类中存在虚函数或者有虚基类的情况下需要显式声明构造函数。拷贝构造函数也是如此。

 

f. 构造函数是一种特殊函数,而拷贝构造函数是一种特殊的构造函数。类X的拷贝构造函数的第一个参数必须为X&,或者const X&;除了第一个参数外,构造函数要么不存在其他参数,如果存在其他参数,其他参数必须有默认值。一个类可以有多个拷贝构造函数。它的形式如下:

X::X(X& x)

X::X(const X& x)

X::X(X& x, int a = 0, int b = 1…)

 

 

 

 

如果将拷贝构造函数中的引用符号去掉 & ,编译将无法通过,出错的信息如下:

非法的复制构造函数 : 第一个参数不应是“ CClass ”

没有可用的复制构造函数或复制构造函数声明为“ explicit ”

 

 

原因:

如果拷贝构造函数中的参数不是一个引用,即形如 CClass(const CClass c_class) ,那么就相当于采用了传值的方式 (pass-by-value) ,而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。因此拷贝构造函数的参数必须是一个引用。

 

需要澄清的是,传指针其实也是传值,如果上面的拷贝构造函数写成 CClass(const CClass* c_class) ,也是不行的。事实上,只有传引用不是传值外,其他所有的传递方式都是传值。

 

 

附带说明,在下面几种情况下会调用拷贝构造函数:

a.       显式或隐式地用同类型的一个对象来初始化另外一个对象。如上例中,用对象 c 初始化 d ;

b.       作为实参 (argument) 传递给一个函数。如 CClass(const CClass c_class) 中,就会调用 CClass 的拷贝构造函数;

c.        在函数体内返回一个对象时,也会调用返回值类型的拷贝构造函数;

d.       初始化序列容器中的元素时。比如 vector<string> svec(5) , string 的缺省构造函数和拷贝构造函数都会被调用;

e.       用列表的方式初始化数组元素时。 string a[] = {string(“hello”), string(“world”)}; 会调用 string 的拷贝构造函数。

 

 

 

运行下面代码以验证之:

 

 

 

运行结果:
copy constructor is called.
copy constructor is called.
copy constructor is called.

运行结果表明,上述结论是正确的。

 

 

 

h. 什么时候必须要显式声明拷贝构造函数?
    拷贝构造函数的作用就是用一个已经实例化了的该类对象,去实例化该类的另外一个对象。


1) 下面的代码并没有显式声明一个构造函数,编译器会自动为类CExample1生成一个缺省的隐式拷贝构造函数:

 

 

 

输出为:

100
90
100
100

 

2) 如果有成员变量以指针形式存在,涉及动态内存分配等情况下,一定要显式声明拷贝构造函数。要注意到,如果需要显式定义拷贝构造函数,那么通常都是需要同时定义析构函数(因为通常涉及了动态内存分配),至于是否必须重载操作符“=”,要视情况而定。

 

 

上面的程序没有显式声明拷贝构造函数,运行结果如下:

 


 

 

可见,ca和cb中的指针成员变量sth指向的是同一个内存地址(Console输出的第5、6行),这就是为什么在cb.setValue(2, 3)后,ca对应的内容也发生了改变(Console输出的第3、4行),而这不是我们所期望的;其次,我们生成了两个对象ca和cb,因此对两次调用析构函数,第一次调用析构函数的时候没有问题,因为此时sth里面有内容,第二次调用析构函数时,sth里面的内容由于在第一次调用析构函数的时候已经被delete了,所以会出现如上的错误提示。


保持其他代码不变,现在我们增加一个拷贝构造函数如下:


 


再运行上面的程序,
运行结果如下:

 

 

 

这次,ca和cb中的指针成员变量sth指向的不是同一个内存地址(Console输出的第5、6行)了,这就是为什么在cb.setValue(2, 3)后,ca对应的内容保持不变,而cb的内容该如愿地改为(2, 3)(Console输出的第3、4行);其次,析构函数也不会报告错误了。


3) 关于拷贝构造函数另外一个完整的例子,其中包含了copy constructor,destructor 和copy assignment operator。

 

 

http://patmusing.blog.163.com/blog/static/1358349602009113061024796/

 

http://blog.csdn.net/wbgeorge/archive/2011/02/21/6198093.aspx

 

 

 

 

 

原创粉丝点击