C++自动提供的成员函数有:默认构造函数,复制构造函数,默认析构函数,赋值操作符,地址操作符即this指针,这五种函数如果用户没有定义,则系统会自动创建一个。
复制构造函数:用一个对象复制一个新的对象时被调用,声明为:类名(类名&对象名);
下面这段话很清楚的说明了很多概念,需要细细体会,故粘了下来:
把参数传递给函数有三种方法,一种是值传递,一种是传地址,还有一种是传引用。前者与后两者不同的地方在于:当使用值传递的时候,会在函数里面生成传递参数的一个副本,这个副本的内容是按位从原始参数那里复制过来的,两者的内容是相同的。当原始参数是一个类的对象时,它也会产生一个对象的副本,不过在这里要注意。一般对象产生时都会触发构造函数的执行,但是在产生对象的副本时却不会这样,这时执行的是对象的复制构造函数。为什么会这样?嗯,一般的构造函数都是会完成一些成员属性初始化的工作,在对象传递给某一函数之前,对象的一些属性可能已经被改变了,如果在产生对象副本的时候再执行对象的构造函数,那么这个对象的属性又再恢复到原始状态,这并不是我们想要的。所以在产生对象副本的时候,构造函数不会被执行,被执行的是一个默认的构造函数(复制构造函数)。当函数执行完毕要返回的时候,对象副本会执行析构函数,如果你的析构函数是空的话,就不会发生什么问题,但一般的析构函数都是要完成一些清理工作,如释放指针所指向的内存空间。这时候问题就可能要出现了。假如你在构造函数里面为一个指针变量分配了内存,在析构函数里面释放分配给这个指针所指向的内存空间,那么在把对象传递给函数至函数结束返回这一过程会发生什么事情呢?首先有一个对象的副本产生了,这个副本也有一个指针,它和原始对象的指针是指向同块内存空间的。函数返回时,对象的析构函数被执行了,即释放了对象副本里面指针所指向的内存空间,但是这个内存空间对原始对象还是有用的啊,就程序本身而言,这是一个严重的错误。然而错误还没结束,当原始对象也被销毁的时候,析构函数再次执行,对同一块系统动态分配的内存空间释放两次是一个未知的操作,将会产生严重的错误。
上面说的就是我们会遇到的问题。解决问题的方法是什么呢?首先我们想到的是不要以传值的方式来传递参数,我们可以用传地址或传引用。没错,这样的确可以避免上面的情况,而且在允许的情况下,传地址或传引用是最好的方法,但这并不适合所有的情况,有时我们不希望在函数里面的一些操作会影响到函数外部的变量。那要怎么办呢?可以利用复制构造函数来解决这一问题。复制构造函数就是在产生对象副本的时候执行的,我们可以定义自己的复制构造函数。在复制构造函数里面我们申请一个新的内存空间来保存构造函数里面的那个指针所指向的内容。这样在执行对象副本的析构函数时,释放的就是复制构造函数里面所申请的那个内存空间。
在C++中,下面三种对象需要拷贝的情况。因此,拷贝构造函数将会被调用。
1). 一个对象以值传递的方式传入函数体
2). 一个对象以值传递的方式从函数返回
3). 一个对象需要通过另外一个对象进行初始化
以上的情况需要拷贝构造函数的调用。如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作赋共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。
除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。换句话说,你从函数返回得到的只是对象的一份拷贝。但是同样的,拷贝构造函数被正确的调用了,你不必担心。
如果在类中没有显式的声明一个拷贝构造函数,那么,编译器会私下里为你制定一个函数来进行对象之间的位拷贝(bitwise copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。显式声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数除非另外一个构造函数在类初始化或者在构造列表的时候被调用。
{
public:
String(); //构造函数
String(const String &s); //复制构造函数
~String(); //析构函数
// 接口函数
void set(char const *data);
char const *get(void);
private:
char *str; //数据成员ptr指向分配的字符串
};
{
str = new char[strlen(s.str) + 1];
strcpy(str, s.str);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
#include
<iostream> #include
<string.h> using namespace std; class withCC { public : withCC(){} withCC( const withCC&aa) { aa.print(); cout<< "withCC(withCC&)" <<endl; } void print( const char *
msg=0) const { cout<< "adsg" <<endl; } }; class woCC { enum {bsz=100}; char buf[bsz]; public : woCC( const char *
msg=0) { memset (buf,0,bsz); if (msg) strncpy (buf,msg,bsz); } void print( const char *
msg=0) const { if (msg) cout<<msg<< ":" ; cout<<buf<<endl; } }; //类composite既含有withCC类的成员对象又含有woCC类的成员对象, //它使用无参的构造函数创建withCC类的对象WITHCC(注意内嵌的对象WOCC的初始化方法) class composite { withCC
WITHCC; woCC
WOCC; public : composite():WOCC( "composite()" ){} void print( const char *
msg=0) { WOCC.print(msg); } }; void main() { composite
c; c.print( "contents
of c" ); cout<< "calling
composite copy-constructor" <<endl; composite
c2=c; //通过对象C初始化对象c2,缺省的复制构造函数被调用 c2.print( "constents
of c2" ); } |
(2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
class CStr { public : CStr();
//默认构造函数 CStr( const char *
psz); //一种广义拷贝构造函数,不过也有人不认同我的看法 CStr( const CStr&
str); //拷贝构造函数 const CStr&
operator=( const CStr&
str); //赋值构造函数 size_t GetSize()
const ;
//这里的const是什么意思?它必须吗? operator
const char *()
const {
return m_pdata;
} protected : const CStr*
_Copy( const CStr&
str); private : char *
m_pdata; size_t m_size;<br>}; <br>CStr::CStr() { m_pdata
= NULL; m_size
= 0; } size_t CStr::GetSize()
const { return m_size; } const CStr*
CStr::_Copy( const CStr&
str) { if ( this !=
&str) { if (m_pdata)<br>
{ delete []
m_pdata; } m_size
= str.GetSize(); m_pdata
= new char [m_size
+ 1]; assert (m_pdata); strcpy (m_pdata,
str); } return this ;<br>} CStr::CStr( const char *
psz) : m_pdata(NULL), m_size(0) { assert (psz); if (m_pdata
!= psz) { if (m_pdata)<br>
{ delete []
m_pdata; } m_size
= strlen (psz); m_pdata
= new char [m_size
+ 1]; assert (m_pdata); strcpy (m_pdata,
psz); } } CStr::CStr( const CStr&
str): m_pdata(NULL), m_size(0) { _Copy(str); } const CStr&
CStr::operator=( const CStr&
str) { return *(_Copy(str)); } int main() { const char *
psz = "test" ; const char *
psz1 = "me" ; CStr
str(psz); //拷贝构造函数,此处调用的是CStr(const
char* psz)。 #1 CStr
str2(str); //
拷贝构造函数,此处调用的是 CStr(const CStr& str) #2 CStr
str1 = psz1; //
拷贝构造,str1此前并不存在,现在要先构造它。 #3 str
= str1; //
真正的赋值构造函数 #4 return 0; } |
对于#3,我想着重说明的是:赋值构造的实际用意是修改一个已有的对象,而现在str1还没有被定义,所以此处还是必须要调用拷贝构造函数先产生一个CStr对象。