首页 > 代码库 > 模式识别 - 处理特征数据 及 代码
模式识别 - 处理特征数据 及 代码
和默认构造函数一样,当用户未显式定义复制构造函数时,编译器只有在某些条件下才会合成一个nontrivial的复制构造函数。所以,如果一个类未定义复制构造函数,编译器就自动为它产生出一个,这句话是错误的。下面主要讨论在哪些情况下,编译器才会自动合成一个复制构造函数。
如果有一个如下所示的类:
class Foo { public: int x, y; };
那么编译器不会自动生成一个复制构造函数的,因为对于这些内置类型,已经能够实现逐位拷贝了。但如果是下面这样的类:
class Foo { public: int x, y; string str; };
在这种情况下,编译器必须合成一个复制构造函数,函数内部调用str对象的复制构造函数,其它内置类型依旧逐位拷贝。
当没有显式定义复制构造函数,编译器在四种情况下会合成复制构造函数。
1、类中有一个成员对象,并且该对象定义(显式定义或编译器合成)了复制构造函数
2、类继承自一个基类,而基类存在一个(显式定义或编译器合成)复制构造函数
上面两种情况比较好理解。成员对象或基类中有复制构造函数,所有编译器需要插入一些代码调用这些复制构造函数,这些代码被安插在合成的复制构造函数中。
测试代码如下:
#include <iostream> using namespace std; class Foo { public: Foo() {} // 此构造函数必须定义 Foo(const Foo &f) { cout << "Foo‘s copy construct!" << endl; } }; class Bar: public Foo { public: // 未定义复制构造函数 int x, y; Foo foo; }; int main() { Bar ba; Bar bb = ba; return 0; }
运行结果:
上述代码同时满足情况1、2,所以编译器会在合成的复制构造函数中调用了两次Foo类的复制构造函数。
3、当类中声明了虚函数
一说到虚函数,就会联想到virtual function table(vtbl)和vptr。在对象间进行复制时,对vptr的复制操作非常重要。虽说vptr是一个指针,可以按照逐位拷贝原则进行复制,但有时会发生很严重的错误:vptr都指向了同一个virtual function table。所以当编译器导入一个vptr后,为了正确初始化vptr,编译器需要合成复制构造函数进行相关操作。具体来说:
- 当同类对象间进行复制初始化时,采用的是逐位拷贝,vptr指向相同的虚函数表。
- 当用派生类初始化基类时,不能采用逐位拷贝,不同类的vptr指向各自的虚函数表,这就是为什么发生切割后无法实现多态性质的原因。
测试代码:
#include <iostream> using namespace std; class Foo { public: virtual void func() { cout << "virtual function in Foo!" << endl; } }; class Bar: public Foo { public: void func() { cout << "virtual function in Bar!" << endl; } }; int main() { Bar b1; Bar b2 = b1; // vptr直接复制,指向相同的virtual function table b2.func(); Foo foo = b1; // 发生切割,vptr不直接复制,指向不同的virtual function table foo.func(); return 0; }
运行结果:
结果正如上面所说。
4、有虚拟基类的情况
虚基类在一个继承体系中只有一个实例存在,所以编译器必须保证程序在执行期确定虚基类的地址。所以,当对象进行复制初始化时,编译器必须执行某些操作来完成这种保证,以使不同对象的虚基类彼此分离。
可以看到,编译器对复制构造函数的隐式操作和默认构造函数是相似的。编译器在需要某些特别的初始化操作时,才会合成复制构造函数。否则,对象之间直接进行逐位拷贝,编译器不会合成复制构造函数。
环境:
Win7 + Code::Blocks
参考:
《深度探索C++对象模型》 P48-P60.
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。