分类: C/C++
2011-12-04 22:46:33
It seems like this:
1. Bitwise copy means copy an whole object directly. Where it will be used? I don't know, maybe it is an concept in implementation detail, and memcpy() could be used to achieve it. The term "bitwise copy", as I know it, means copying objects with memcpy(), or in some other way that treats object storage as an array of bytes and produces a copy that's byte-for-byte identical.
2. Memberwise copy means copy one member of an object at a time.
3. The compiler will give us a default copy constructor if needed when we do not provide it. And this constructor is memberwise copy.
4. Shallow copy and deep copy are concepts that describe the result of a copy action. Note that these are colloquial terms, not formally defined in any normative document, and really only meaningful in context.
------------------------------------------------------------------------
//hi.baidu.com/hpagent/blog/item/3dd4f4ff8545ba57d6887d11.html
大家都知道,C++中类的默认的拷贝构造函数是按位拷贝的,也就是直接将一个对象的成员值拷贝过来;
比如一个类A,我们不显示提供拷贝构造函数的话,如下:
class{
int a;
char arr[10];
char *p;
};
A a1;
A a2=a1;
这个时候,a2和a1的成员 int a和arr[ ]是相同的值,成员是独立的,修改某一个的成员不会影响另外一个,但是这个时候要注意的是指针p;
虽然我们输出的结果一样,但是不能草率下决定。因为这个时候a2和a1的p值是一样的,也就是说两个指针指向同一个地方;
所以这样的拷贝会经常出现问题,比如两次释放指针的内存等。解决办法大家都知道是自己构造一个按内容拷贝的拷贝构造函数,这里我也就不多说了。
我想讨论的问题是如下的问题:
C++编译器在什么情况下不会按照默认的Bitwise copy来做呢?
首先有两种情况:
1:就是含有的成员对象本身提供了拷贝构造函数(不管是默认的还是自己提供的),这个时候当拷贝这个对象的时候调用的是对象的类提供的拷贝构造函数。
2:继承的基类有拷贝构造函数,这个时候编译器会插入基类的拷贝构造函数,而不是编译器自己来提供。这两种都不会按照默认的拷贝语意来做。
这两种情况我不想说,我想讨论下面两种:
3:含有虚函数的类;
考虑下面的代码:
class Foo
{
public:
int a;
Foo *next;
virtual void Func(void){cout<<"call Foo::func()"<
class D:public Foo
{
public:
int c;
virtual void Func(void){cout<<"call D::func()"<
int main(void)
{
D d;
Foo f=d;
d.Func();
f.Func();
system("pause");
return 0;
}
输出的结果是什么呢?运行看看:结果完全按照我们想要的要求运行:call D::func() call Foo::func()
所以这个就是说,当含有虚函数的时候,我们的类会有虚函数表,每个对象会有vptr。如果我们的基类只是简单的将子类的vptr值拷贝过来,显然是不符合要求的,于是这个时候编译器会给我们自动的停用bitwise拷贝,而用reset将基类的vptr指向基类的虚函数表。所以这个时候是达到了我们的要求的。我想说的是,其他值会按照默认的按位拷贝,只是vptr不会,还有我们下一个将讨论的也不会。
4:虚继承
与第三种情况类似,但是有区别,这里有vbtl,然而vbtl是采用的重新初始化,而不是reset;
让我们看看例子:
class Raccoon : public virtual ZooAnimal
{
public: Raccoon() { /* private data initialization */ }
Raccoon( int val ) { /* private data initialization */ }
// ... private: // all necessary data
};
class RedPanda : public Raccoon
{
public: RedPanda() { /* private data initialization */ }
RedPanda( int val ) { /* private data initialization */ }
// ... private: // all necessary data
};
这里如果是两个一样的类的对象间进行拷贝,简单的按位拷贝就会解决问题,而我们的问题在于父类与子类之间的拷贝;
如:RedPanda little_red;
Raccoon little_critter = little_red;
这个时候,编译器会在默认的拷贝构造函数中插入初始化指向虚基类的指针,而不是reset。这个与类的内部存储结构有关,建议看看之后便一目了然了。