深入探索C++对象模型笔记之四 —— 构造函数语意学 (Copy Constructor的建构操作)
2011-05-26 19:14
441 查看
有三种情况,会以一个object的内容作为另一个class object的初值。
1.当对一个object做明确的初始化操作时
2.当object被当做参数交给某个函数时
3.当函数传回一个class object时
以上三种情况都会导致copy constructor的调用。
Default Memberwise Initialization
如果class没有提供一个explicit copy constructor,编译器会怎么做呢?
当class object以“相同的class的另一个object”作为初值时,其内部是以所谓的default memberwise initialization手法完成的,也就是把每一个内建的或派生的data member的值,从某个object拷贝一份到另一个object身上,不过它并不会拷贝其中的member class object,而是以递归的方式(也就是继续调用member class object的copy constructor函数)施行memberwise initialization。
这样的操作实际上如何完成?ARM告诉我们:
“从概念上而言,对于一个class X,这个操作是被一个copy constructor实现出来...”
其中主要的字眼是“概念上”,这个注释又紧跟着一些解释:
“一个良好的编译器可以为大部分class objects 产生bitwise copies,因为它们有bitwise copy semantics...”
也就是说,“如果一个class未定义出copy constructor,编译器自动为它产生出一个”这句话不对,而是应该像ARM所说:
“Default constructor和copy constructor在必要的时候才由编译器产生出来.”
这个句子中的“必要”意指当class不展现bitwise copy semantics时。
C++ Standard 说:
“一个class object可以从两种方式复制得到,一种是被初始化,一种是被指定(assignment),从概念上讲,这两个操作分别是以copy constructor和copy assignment operator 完成的。”
Bitwise Copy Semantics(位逐次拷贝)
#include "Word.h"
Word noun("book");
void foo()
{
Word verb = noun;
...
}
很明显verb是根据noun来初始化。但是在尚未看过class Word的声明之前,我们不可能预测这个初始化操作的程序行为。如果class Word的设计者定义了一个copy constructor,verb的初始化操作会调用它。如果该class没有定义explicit copy constructor,那么是否会有一个编译器合成的实体被调用呢 ?这就得视该class是否展现“bitwise copy semantics”而定。
例如:
class Word
{
public:
Word(const char*);
~Word(delete [] str);
private:
int cnt;
char* str;
}
这种情况下并不需要合成出一个default copy constructor,因为上述声明展现了“default copy semantics”,而verb的初始化操作也不需要以一个函数调用收场。(但是这里会有明显的浅拷贝错误)然而,如果class Word是这样声明:
class Word
{
public:
Word(const String&);
~Word();
private:
int cnt;
String str;
};
其中String声明了一个explicit copy constructor:
class String
{
public:
String(const char*);
String(const String&);
~String();
};
在这个情况下,编译器必须合成出一个copy constructor以便调用member class String object 的copy constructor:
inline Word::Word(const Word& wd)
{
str.String::String(wd.str);
cnt = wd.cnt;
}
有一点很值得注意:在这被合成出来的copy constructor中,如整数、指针、数组等等的nonclass members也都会被复制。
四种情况下一个class不展现出“bitwise copy semantics”:
1.当class内含一个member object而后者的class声明有一个copy constructor时。(不论是明确声明还是由编译器合成而来)
2.当class继承自一个base class而后者存在一个copy constructor时。(不论是明确 声明还是由编译器合成而来)
3.当class声明了一个或多个virtual function时。
4.当class派生自一个继承串链,其中有一个或多个virtual base classes 时。
下面说明第三条和第四条。
第三条:如果class声明了一个或多个virtual function时,编译器会将一个指向virtual function table的指针(vptr)安插在每一个class object内。很显然,如果编译器对于每一个新产生的class object的vptr不能成功而正确地设好其初值,将导致可怕的后果。因此,当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics了。现在,编译器需要合成出一个copy constructor,以求将vptr适当地初始化。
第四条:virtual base class的存在需要特别处理,一个class object如果以另一个object作为初值,而后者有一个virtual base class subobject,那么也会使“bitwise copy semantics”失效。
1.当对一个object做明确的初始化操作时
2.当object被当做参数交给某个函数时
3.当函数传回一个class object时
以上三种情况都会导致copy constructor的调用。
Default Memberwise Initialization
如果class没有提供一个explicit copy constructor,编译器会怎么做呢?
当class object以“相同的class的另一个object”作为初值时,其内部是以所谓的default memberwise initialization手法完成的,也就是把每一个内建的或派生的data member的值,从某个object拷贝一份到另一个object身上,不过它并不会拷贝其中的member class object,而是以递归的方式(也就是继续调用member class object的copy constructor函数)施行memberwise initialization。
这样的操作实际上如何完成?ARM告诉我们:
“从概念上而言,对于一个class X,这个操作是被一个copy constructor实现出来...”
其中主要的字眼是“概念上”,这个注释又紧跟着一些解释:
“一个良好的编译器可以为大部分class objects 产生bitwise copies,因为它们有bitwise copy semantics...”
也就是说,“如果一个class未定义出copy constructor,编译器自动为它产生出一个”这句话不对,而是应该像ARM所说:
“Default constructor和copy constructor在必要的时候才由编译器产生出来.”
这个句子中的“必要”意指当class不展现bitwise copy semantics时。
C++ Standard 说:
“一个class object可以从两种方式复制得到,一种是被初始化,一种是被指定(assignment),从概念上讲,这两个操作分别是以copy constructor和copy assignment operator 完成的。”
Bitwise Copy Semantics(位逐次拷贝)
#include "Word.h"
Word noun("book");
void foo()
{
Word verb = noun;
...
}
很明显verb是根据noun来初始化。但是在尚未看过class Word的声明之前,我们不可能预测这个初始化操作的程序行为。如果class Word的设计者定义了一个copy constructor,verb的初始化操作会调用它。如果该class没有定义explicit copy constructor,那么是否会有一个编译器合成的实体被调用呢 ?这就得视该class是否展现“bitwise copy semantics”而定。
例如:
class Word
{
public:
Word(const char*);
~Word(delete [] str);
private:
int cnt;
char* str;
}
这种情况下并不需要合成出一个default copy constructor,因为上述声明展现了“default copy semantics”,而verb的初始化操作也不需要以一个函数调用收场。(但是这里会有明显的浅拷贝错误)然而,如果class Word是这样声明:
class Word
{
public:
Word(const String&);
~Word();
private:
int cnt;
String str;
};
其中String声明了一个explicit copy constructor:
class String
{
public:
String(const char*);
String(const String&);
~String();
};
在这个情况下,编译器必须合成出一个copy constructor以便调用member class String object 的copy constructor:
inline Word::Word(const Word& wd)
{
str.String::String(wd.str);
cnt = wd.cnt;
}
有一点很值得注意:在这被合成出来的copy constructor中,如整数、指针、数组等等的nonclass members也都会被复制。
四种情况下一个class不展现出“bitwise copy semantics”:
1.当class内含一个member object而后者的class声明有一个copy constructor时。(不论是明确声明还是由编译器合成而来)
2.当class继承自一个base class而后者存在一个copy constructor时。(不论是明确 声明还是由编译器合成而来)
3.当class声明了一个或多个virtual function时。
4.当class派生自一个继承串链,其中有一个或多个virtual base classes 时。
下面说明第三条和第四条。
第三条:如果class声明了一个或多个virtual function时,编译器会将一个指向virtual function table的指针(vptr)安插在每一个class object内。很显然,如果编译器对于每一个新产生的class object的vptr不能成功而正确地设好其初值,将导致可怕的后果。因此,当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics了。现在,编译器需要合成出一个copy constructor,以求将vptr适当地初始化。
第四条:virtual base class的存在需要特别处理,一个class object如果以另一个object作为初值,而后者有一个virtual base class subobject,那么也会使“bitwise copy semantics”失效。
相关文章推荐
- 深入探索C++对象模型笔记之三 —— 构造函数语意学 (Default Constructor的建构操作)
- 深入探索C++对象模型笔记之五 —— 构造函数语意学 (成员们的初始化队伍 Member Initialization List)
- 深入探索C++对象模型 第二章 构造函数语意学
- 深入探索C++对象模型:第二章构造函数语意学
- 《深入探索C++对象模型》第二章:构造函数语意学(下)
- 深入探索C++对象模型之二 --- 构造函数语意学
- 深入探索C++对象模型笔记之六 —— Data语意学
- 《深入探索C++对象模型》第三章:Data语意学
- 深度探索C++对象模型 第二章构造函数语意学
- 深度探索C++对象模型 Function语意学笔记
- 深度探索C++对象模型学习 之 C++构造函数语意学(一)
- 《深入探索C++对象模型》第六章 执行语意学
- 深入探索C++对象模型:第三章 DATA语意学
- 深入探索C++对象模型 之 执行期语意学
- 深入探索C++对象模型笔记
- 《深入探索C++对象模型》笔记之第一章关于对象
- 深度探索C++对象模型学习笔记——Function语意学
- 深入探索C++对象模型--C++构造函数
- 《深入探索C++对象模型》第二章:构造函数语意学(上)
- 【深入探索c++对象模型】c++中构造函数调用虚函数的讨论