赋值运算符
1, 禁止一个类对象指定给另一个类对象的方法:copy 赋值运算符声明为private,并且不提供定义。
2, 一个class对于默认的copy 赋值运算符,以下情况不会表现出位拷贝语意:
1】 类内含成员对象,其类有一个copy 赋值运算符;
2】 类内基类有一个copy 赋值运算符;
3】 类声明了任何虚函数
4】 类继承自一个虚基类
注:上述4种情况类似拷贝构造,构造函数。
例子:编译器合成的copy 赋值运算内部调用基类/成员对象的copy 赋值运算
建议:尽可能避免虚基类的拷贝操作(因为虚基类会有多份拷贝实体),甚至,不要在虚基类中声明数据。
————————————————————————————————————————————————————————————————-析构函数
什么时候合成?
1, class没有定义析构函数,
2, 只有在class内带的member object(或class自己的基类)拥有析构函数时,编译器才合成。
否则,析构函数被认为不需要,就不需合成(更不需要被调用)
例子:不会合成(即使有虚函数)
注意:只有类中的基类或数据成员不含析构函数,该类就不会合成一个析构函数
即使类中有虚函数,或继承自虚基类
一个class的析构函数被扩展的方式如下:
1,本身的destructor
2,如果class有member class objects,后者有destructors,那么它们以其声明顺序的相反顺序被调用;
1, 如果object内有vptr,则被重新设定,指向适当的base class的virtual table;
如果有任何非虚基类有析构函数,它们以其声明顺序的相反顺序被调用;
2, 如果有nonvirtual base class拥有destructor,而当前讨论的这个class 是最尾端的class,那么它们以其原来的构造顺序的相反顺序被调用。
注意:析构函数和构造函数的调用次序相反
一个最底层对象归还起内存空间前,依次变成各个基类(和析构函数的顺序有关)。——————————————————————————————————————————————————————————
构造函数
默认构造函数什么时候合成?
即编译器需要时:
1> 带有默认构造函数的成员对象
2> 带有默认构造函数的基类
3> 带有虚函数的类,默认构造函数作用:此时,设置vptr
4> 带有虚基类的类,安排虚基类在每一个派生类对象的位置,用于执行期
注意:合成的构造函数,仅仅满足编译器的需要
最好使用成员初始化列表的情况:
1,初始化一个reference member;
2,当初始化一个const member时;
3,调用一个base class 的constructor,而它拥有一组参数时;
4,调用一个member class 的constructor,而它拥有一组参数时;
在这4种情况下,不使用成员初始化列表可以执行,但效率不彰,例如:
class word{
string name;
int cnt;
public:
word()
{
name=0;//编译器内部会产生临时对象string,最后再摧毁临时对象
cnt=0;
}
};
较佳的写法:
word::word:name(0)
{
cnt=0;
}
或者坚持此写代码风格:
word::word:cnt(0),name(0) //name的初始化早于cnt,因为其声明早于cnt
{
}
注意:list中的项目次序由class中的members声明次序决定,不是由initialization list中的排列次序决定。
例子:
有陷阱的写法:i的值不确定
修改后的写法:j在i之前被赋值
————————————————————————————————————————————————————————————
拷贝构造函数
被调用的3种情况?
1》 赋值
class X{};
X x;
X xx=x;
2》参数
3》返回值
1,当编译器不合成默认拷贝构造函数时,使用位逐次拷贝。
什么时候类不表现出位逐次拷贝?
1,类内含成员对象,其类有一个copyconstructor (无论是编译器合成还是编写的);
2,类内基类有一个copy constructor(无论是编译器合成还是编写的);
3,类声明了任何virtual function
4,类派生自一个继承链,其中有virtual base class
前2种情况:编译器将member/baseclass的copy constructors 安插到被合成的copy constructor;
注:以上四个条件类似默认构造函数的合成!!!!!!!!!!!!
第三种情况---内含虚函数的情况:重新设置虚表指针
重新设置虚表指针-例子
第四中情况----虚基类子对象:让派生类对象中的虚基类子对象位置准备妥当
在编译器合成的copy constructor 中插入一些代码,设定 virtualbase class pointer/offset的初值。
针对拷贝构造函数的优化
使用者的优化:优化后:直接返回对象,去除临时对象
编译器的进一步优化,去除临时对象:
拷贝构造函数的问题:
拷贝构造函数中使用memcpy()时,如果类函数vptr,那么vptr会被清零。
例子1:效率更高
例子2:但当类函数虚函数或虚基类时,上述函数会导致虚指针被改写:memset把vptr清零