?
?
//a
0
3
//b
0
3
我们设计的类在内存中也是连续的,使用这样的拷贝方法会得到一个一模一样的同类型实例。而且编译器我们处理了这一件事(C++的编译器真好,它能解决的事,就不用麻烦我们了),也就是说即使我们没有定义拷贝构造函数,编译器也会在需要使用的时候,自己产生一个拷贝构造函数,使用的方法就是位拷贝。但是这样好吗,使用这种方法产生的新类可以安全的工作吗,应该有不少朋友已经产生了疑问。
什么时候可以让编译器自己处理拷贝构造函数。
#include <iostream>
using namespace std;
class A{
private:
int x;
int y;
int z;
public:
A():x(0),y(0),z(0){ }
A( int _x = 0 , int _y = 0 , int _z = 0 ):x(_x),y(_y),z(_z){ }
friend ostream& operator <<( ostream& , A const& );
};
ostream& operator <<( ostream& out , A const& arg )
{
out << "This is a Instance of A" << endl;
out << "Member Data x is : " << arg.x << endl;
out << "Member Data y is : " << arg.y << endl;
out << "Member Data z is : " << arg.z << endl;
return out;
}
void main()
{
A a( 1 , 12 ,123 );
A b(a);
cout << "This is a!" << endl;
cout << a << endl;
cout << "b is a copy of a!" << endl;
cout << b;
}
结果是:
This is a!
This is a Instance of A
Member Data x is : 1
Member Data y is : 12
Member Data z is : 123
b is a copy of a!
This is a Instance of A
Member Data x is : 1
Member Data y is : 12
Member Data z is : 123
可以看出,位拷贝得出的结果是正确的。
上面的例子中成员变量都是在编译期间决定的,在内存中的位置也相对固定,如果成员变量的内容是在运行期间决定的呢,比如字符串成员变量,他需要在堆中动态分配内存。还能正常工作吗,继续看例子。
#include <iostream>
#include <string.h>
#include <mem.h>
using namespace std;
class A{
private:
char * data;
public:
A():data(NULL){ }
A( char * _data ):data(NULL)
{
if( !_data )
return;
int length = strlen(_data) +1;
data = new char[length];
memcpy( data , _data , length );
}
~A()
{
if( data )
delete data;
}
void Clear( void )
{
if( data )
{
memset( data , 0 , strlen( data ) );
delete data;
}
data = NULL;
}
friend ostream& operator <<( ostream& , A const& );
};
ostream& operator <<( ostream& out , A const& arg )
{
out << "This is a Instance of A" << endl;
if( arg.data && *arg.data )
out << "Member Data data is : " << arg.data << endl;
else
out << "Member Data data is : NULL" << endl;
return out;
}
void main()
{
A a( "abcdefg" );
A b(a);
cout << "This is a!" << endl;
cout << a << endl;
cout << "b is a copy of a!" << endl;
cout << b << endl;
a.Clear();
cout << "Where a's mem clear!" << endl;
cout << a;
cout << "God! b's mem clear!" << endl;
cout << b << endl;
}
结果是:
This is a!
This is a Instance of A
Member Data data is : abcdefg
b is a copy of a!
This is a Instance of A
Member Data data is : abcdefg
Where a's mem clear!
This is a Instance of A
Member Data data is : NULL
God! b's mem clear!
This is a Instance of A
Member Data data is : NULL//不!a中释放了内存连带着b的一起释放掉了。
这是当然的由于位拷贝,b中的data只是将a中的data复制过来了而已,并没有分配内存,拷贝字符串的内容。显而易见,使用位拷贝不能满足我们的要求,原来只需要简单的将成员变量的值简单的复制,这种我们称之为:浅拷贝。现在我们需要处理对应成员变量,用其他方法来得到我们需要的结果,这种我们称之为:深拷贝。
这样我们就需要自己写拷贝构造函数来实现深拷贝了。
#include <iostream.h>
#include <string.h>
#include <mem.h>
class A{
private:
char * data;
public:
A():data(NULL){ }
A( char * _data ):data(NULL)
{
if( !_data )
return;
int length = strlen(_data) +1;
data = new char[length];
memcpy( data , _data , length );
}
A( A const& arg )
{
if( !arg.data )
return;
int length = strlen(arg.data) +1;
data = new char[length];
memcpy( data , arg.data , length );
}
~A()
{
if( data )
delete data;
}
void Clear( void )
{
if( data )
{
memset( data , 0 , strlen( data ) );
delete data;
}
data = NULL;
}
friend ostream& operator <<( ostream& , A const& );
};
ostream& operator <<( ostream& out , A const& arg )
{
out << "This is a Instance of A" << endl;
if( arg.data && *arg.data )
out << "Member Data data is : " << arg.data << endl;
else
out << "Member Data data is : NULL" << endl;
return out;
}
void main()
{
A a( "abcdefg" );
A b(a);
cout << "This is a!" << endl;
cout << a << endl;
cout << "b is a copy of a!" << endl;
cout << b << endl;
a.Clear();
cout << "Where a's mem clear!" << endl;
cout << a;
cout << "Good! b's mem not clear!" << endl;
cout << b << endl;
}
结果是:
This is a!
This is a Instance of A
Member Data data is : abcdefg
b is a copy of a!
This is a Instance of A
Member Data data is : abcdefg
Where a's mem clear!
This is a Instance of A
Member Data data is : NULL
Good! b's mem not clear!
This is a Instance of A
Member Data data is : abcdefg //哈哈,这正是我想得到的结果。
如果能使用位拷贝,尽量让编译器自己用位拷贝的方式处理,这样会提高效率。但是一定要谨慎,不然会产生不可预料的结果,如果你的类中有一个成员变量也是类,它使用了深拷贝,那么你也一定要使用深拷贝。
另外,我在《白马非马----继承》中说到,一个类型的的派生类是该类型的一种。那么。
class A;
class B: public A{
};
B b;
A a(b);
这样的形式是正确的。事实上,b先切片退化成一个临时变量tempb,类型是class A,有关A的部分原封不动的保留下来,然后使用A a(tempb)这样的方式成功的调用了。
拷贝构造函数并非可有可无!不能用其他函数来替代
看这样的例子
void function( A a);
在函数调用的时候按值传递参数,那么将在栈里产生一个class A的临时变量,如果没有拷贝构造函数,这个过程就无法自动完成,如果没用设计好浅拷贝或深拷贝,那么可能得不到正确结果。如果拷贝构造函数正确,那么我们可以轻松的获得我们想要的结果----按值传递的参数在函数执行后不受影响。
classA a = a1;//拷贝构造函数
事实上就是这样的形式。
ClassA a(a1);//可以改成这种形式
……