序言:
在计算机当中, 当一个class object的内容作为另一个class object的初始值的时候, 如果class 设计者没有显示的声明Copy Constructor, 那么编译器将会自动生成Copy Constructor。
那么在这里问题就来了:
1、 在什么情况下会调用Copy Constructor?
2、在初始化过程中, 是采用位逐次拷贝还是采用成员初始化的方式呢?
在什么情况下会调用Copy Constructor
1、一个明确的class object的内容作为另一个class object的初始值。
2、类的对象本身作为函数的参数(非引用和指针的情况)
3、函数返回值是一个类的对象(如果可能在编译器优化的过程中不做明显调用)[在VS下会出现情况, 但是在dev下不会出现此情况]
位逐次拷贝或成员初始化形式的Copy Constructor
位逐次拷贝Copy Constructor:按照类成员的声明次序, 调用memcpy函数进行拷贝
成员初始化Copy Constructor:按照类成员的声明次序,以此调用起构造函数进行初始化
他们之间的区别:
1、如果类成员中包含指针, 通过位逐次拷贝的新对象的成员指针指向的相同的空间。
2、在虚指针的情况下, 如果是父类和基类之间的构造,很有可能会造成虚指针乱指。
例子如下:
class Myclass{
public:
//...
private:
int a;
char *str;
};
如果是位逐次拷贝的情况下复制构造函数可能合成的伪代码
Myclass ::Myclass(const Myclass & _my){
//合成的伪码
this->a = _my.a;
this->str = _my.str;
}
如果是成员初始化的情况下复制构造函数可能合成的伪代码
Myclass ::Myclass(const Myclass & _my){
//合成的伪码
this->a.int::int(_my.a);
this->str = new char[strlen(_my.str) + 1];
memset(this->str, 0, strlen(_my.str) + 1);
memcpy(this->str,_my.str, sizeof(char) * (strlen(_my.str)));
}
如何使C++不展现出位逐次拷贝
1、该类含有member object, 并且该member object的class中声明了复制构造函数(不管是class设计者声明或者是编译器自动生成的)
2、该类继承与一个有复制构造函数的基类(不管是class设计者声明或者是编译器自动生成的)
3、class 声明了一个或者多个virtual function
4、当class派生自一个继承链, 并且其中有一个或者多个virtual base class
一、编译器如何重新设定virtual table的指针
当class声明了一个或者多个virtual function的时候, 在生成复制构造函数将有两个扩张的操作:
(1)、增加一个virtual function table(vtbl), 内含每一个有作用的virtual function的地址
(2)、将一个指向virtual function table的指针(vptr), 安插在每一个class object内
当我们在类中当中加入virtual function的时候, 那么该类就不表现为位逐次拷贝, 那么编译器将会自动合成复制构造函数来对虚表和虚指针进行初始化。
class ZooAnimal{
public:
ZooAnimal(){cout << "ZooAnimal Constructor\n";}
virtual ~ZooAnimal(){cout << "ZooAnimal Destructor\n";}
virtual void animate(){cout << "ZooAnimal animate\n"; }
virtual void draw(){cout << "ZooAnimal draw\n"; }
private:
// member data
};
class Bear:public ZooAnimal{
public:
Bear(){cout << "Bear Constructor\n";}
~Bear(){cout << "Bear Destructor\n";}
void animate(){cout << "Bear animate\n"; }
void draw(){cout << "Bear draw\n"; }
virtual void dance(){cout << "Bear dance\n"; }
private:
// member data
};
当ZooAnimal object 以另一个对象作为初始值, 或者 Bear object以另外一个对象作为初值, 都可以直接考“位逐次拷贝”, 这样是安全的
Bear b1;
Bear b2 = b1;
但是如果执行以下代码,
Bear b1;
ZooAnimal z1 = b1;
编译器生成的复制构造函数并不会直接拷贝Bear object当中的虚表内容, 会发生切割的行为, 导致ZOoAnimal会明确设定对象object的vptr指向ZOoAnimal class的virtual table的内容, 而不是直接拷贝游走
二、总结
1、如果一个class 未定义copy constructor函数, 那么编译器将会自动生成---这个说法是错误的
2、Default constructor和Copy Constructor在必要的时候由编译器自动合成。
3、一个class object有两个方式构成复制构造函数: 一种是初始化的方式另一种是assignment重载。