由于编译器会尽可能的为所有的警告和错误做出解释。但也因此导致了部分情况下的过度解析。
书中给的例子是编译器由于过度解析,使用了类型转换运算符的解析代码,导致隐藏了真正的错误。
cin << intval;
int temp = cin.operator int();
temp << intval;
分析一下:
要注意由于隐式转换可能造成的不良后果。
隐式类型转换虽然会"暗地里"做一些转换操作,但这种机制的好处也是显而易见的。而且C++为了让这个"暗地里"的隐式操作能够被程序员察觉显形,提供了一个修饰符"explicit",类型转换构造函数和类型转换函数声明前加上explicit关键字将阻止编译器隐式类型转换操作。任何尝试隐式转换的操作都会报错。
如下:
#include<iostream>
using namespace std;
class A
{
public:
//explicit
A(int a):m_a(a)
{
cout << "construct A from int" << endl;
}
//explicit
operator int()
{
cout <<"convert A to int " << end;
return m_a;
}
public:
int m_a;
};
int main(void)
{
A a(5);//显式类型转换构造
A b = 5;//隐式类型转换构造
int i = a;//隐式类型转换函数
return 0;
}
讨论trivial和notirvial其实就是讨论构造函数存在的必要性。构造本质上要为对象的生成做一些辅助操作。
但如果对象生成的需求仅仅是分配空间就够了,那么构造函数其实也没有什么意义。
构造函数确实不是必须的,甚至于某些情况下,编译器连系统默认构造函数都不会提供
。
比如有一个类就像C中结构体的一样,struct A* pa = new (sizeof(struct A));就足够,即不需要初始化成员变量,也不需要负责成员变量的构造,更不需要初始化虚函数表,或者虚基类表。那么编译器也没有必要提供一个默认构造多此一举了。
综合来看,作者想要说明的是:在基类或者类成员没有自定义构造函数时默认构造函数的作用,在以下几种条件下会体现即:
看作者总结的四种情况(没有自定义构造函数的情况下):
public class B
{
B(){}
};
class A
{
class B b;
};
public class B
{
B(){}
};
class A:public class B
{
};
class A
{
virtual fun(){}
};
class A : virtual public class B
{
};
类需要执行拷贝构造的三种情形
注意:拷贝构造的本质是还是构造,本质操作是初始化操作,而非拷贝操作。
拷贝构造同样被分成了trivial和notirvial,是trivial还是notrivial和默认构造解释差不多。先理解下几个名词。
bitwise copy是编译器默认提供的位拷贝,即memcpy系列,以bit为单位。加个semantics(语意)引申意义后面讲。
memberwise init即基本类型成员的赋值,以成员为单位。
bitwise copy semantics:位拷贝语意,即一个类的拷贝构造过程中的初始化操作应该是固定且连续的memwise init,不能被安插子对象或者类类型的成员变量的拷贝构造(尽管子对象或者类类型的成员变量内部也是递归mmwise init的),另外也不能由于存在虚函数或者虚基类增加拷贝构造操作而在该类的拷贝构造中额外增加虚表指针或者虚基类表指针的重定位操作。
换句话说:bitwise copy semantics上只能有 POD数据类型C++ POD(Plain Old Data)类型
某些情况下对象不能有bitwise copy semantics,否则拷贝构造会出现问题。即上述的几个不能。
以下四种情况不应该表现出bitwise copy semantics,默认构造必须是notrivial的
前两种,基类子对象或者类类型成员的拷贝构造必须被当前对象的拷贝构造调用。默认拷贝构造必须是notrivial。
类存在虚函数表vtbl时或者继承自虚基类时,需要重定虚表指针,默认拷贝构造必须是notirvial的。
也就是默认拷贝构造必须要有才行。
最终要回归trivial和notrivial,不要陷在bitwise copy semanstics里。
原文:https://www.cnblogs.com/kuikuitage/p/12296711.html