1、
多态和指针算法不能混合在一起来用,所以数组与多态也不能用在一起。
Display(Base bs[]) {}
Derived dr[];
Display(dr);
在函数内部的array[i]是指针算法*(array+i)的缩写。我们知道bs是一个指向数组起始地址的指针,但是bs中各元素内存地址与数组的起始地址的间隔究竟有多大呢?它们的间隔是i*sizeof(一个在数组里的对象),因为在bs数组[0]到[i]间有i个对象。编译器为了建立正确遍历数组的执行代码,它必须能够确定数组中对象的大小,这对编译器来说是很容易做到的。参数bs被声明为Base类型,所以array数组中每一个元素都是Base类型, 因此每个元素与数组起始地址的间隔是i*sizeof(Base)。很明显,如果采用多态传递一个派生类对象的数组(dr),而派生类对象一般要比基类对象大,对于数组元素间隔的计算肯定就出现了错误。
2、 虚基类时最好还是要提供缺省构造函数。因为不提供缺省构造函数的虚基类,很难与其进行合作。因为几乎所有的派生类在实例化时都必须给虚基类构造函数提供参数。这就要求所有由没有缺省构造函数的虚基类继承下来的派生类(无论有多远)都必须知道并理解提供给虚基类构造函数的参数的含义。派生类的作者是不会企盼和喜欢这种规定的。
3、 关于c++的用户定义的转换
越有经验的C++程序员就越喜欢避开类型转换运算符(如operater int())。例如在C++标准库(参见Effective C++条款49和M35)委员会工作的人员是在此领域最有经验的,他们加在库函数中的string类型没有包括隐式地从string转换成C风格的char*的功能,而是定义了一个成员函数c_str用来完成这个转换。
通过单参数构造函数进行隐式类型转换更难消除。而且在很多情况下这些函数所导致的问题要甚于隐式类型转换运算符。对于单参的构造函数最好声明为explicit。注意在一次类型转换过程中只能调用一次用户定义的转换,所以很多时候也可以采用嵌套类(proxy classes)的作法,将内置数据类型转换为嵌套类类型作为该类的构造函数参数。
4、 自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
为了重载前缀和后缀形式,C++规定后缀形式有一个int类型参数(且最好省略该参数名称,以免编译器发出警告信息),当函数被调用时,编译器传递一个0做为int参数的值给该函数,我们可以看一下函数实现版本:
// 前缀形式:增加然后取回值
UPInt& UPInt::operator++()
{
*this += 1; // 增加
return *this; // 取回值
}
// postfix form: fetch and increment
const UPInt UPInt::operator++(int)
{
UPInt oldValue = *this; // 取回值
++(*this); // 增加
return oldValue; // 返回被取回的值
}
几点分析:
A、注意前缀与后缀形式返回值类型是不同的。 前缀形式返回一个引用,后缀形式返回一个const类型。之所以后缀要返回一个const类型,主要原因是在于对i++++之类的操作,一个是标准的operator++不支持,还有就是即使支持返回一个非const类型,那么得到的也不是用户想要的最初值,而是经过一次自增之后的值;
B、很清楚可以看到后缀形式有临时变量的生成,所以从代码效率考虑,应该 尽可能的使用前缀形式;
C、从前缀和后缀代码的维护考虑,后缀increment和decrement应该根据它们的前缀形式来实现(后缀形式代码中调用了前缀形式的函数)。你仅仅需要维护前缀版本,因为后缀形式自动与前缀形式的行为一致。
Display(Base bs[]) {}
Derived dr[];
Display(dr);
在函数内部的array[i]是指针算法*(array+i)的缩写。我们知道bs是一个指向数组起始地址的指针,但是bs中各元素内存地址与数组的起始地址的间隔究竟有多大呢?它们的间隔是i*sizeof(一个在数组里的对象),因为在bs数组[0]到[i]间有i个对象。编译器为了建立正确遍历数组的执行代码,它必须能够确定数组中对象的大小,这对编译器来说是很容易做到的。参数bs被声明为Base类型,所以array数组中每一个元素都是Base类型, 因此每个元素与数组起始地址的间隔是i*sizeof(Base)。很明显,如果采用多态传递一个派生类对象的数组(dr),而派生类对象一般要比基类对象大,对于数组元素间隔的计算肯定就出现了错误。
2、 虚基类时最好还是要提供缺省构造函数。因为不提供缺省构造函数的虚基类,很难与其进行合作。因为几乎所有的派生类在实例化时都必须给虚基类构造函数提供参数。这就要求所有由没有缺省构造函数的虚基类继承下来的派生类(无论有多远)都必须知道并理解提供给虚基类构造函数的参数的含义。派生类的作者是不会企盼和喜欢这种规定的。
3、 关于c++的用户定义的转换
越有经验的C++程序员就越喜欢避开类型转换运算符(如operater int())。例如在C++标准库(参见Effective C++条款49和M35)委员会工作的人员是在此领域最有经验的,他们加在库函数中的string类型没有包括隐式地从string转换成C风格的char*的功能,而是定义了一个成员函数c_str用来完成这个转换。
通过单参数构造函数进行隐式类型转换更难消除。而且在很多情况下这些函数所导致的问题要甚于隐式类型转换运算符。对于单参的构造函数最好声明为explicit。注意在一次类型转换过程中只能调用一次用户定义的转换,所以很多时候也可以采用嵌套类(proxy classes)的作法,将内置数据类型转换为嵌套类类型作为该类的构造函数参数。
4、 自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
为了重载前缀和后缀形式,C++规定后缀形式有一个int类型参数(且最好省略该参数名称,以免编译器发出警告信息),当函数被调用时,编译器传递一个0做为int参数的值给该函数,我们可以看一下函数实现版本:
// 前缀形式:增加然后取回值
UPInt& UPInt::operator++()
{
*this += 1; // 增加
return *this; // 取回值
}
// postfix form: fetch and increment
const UPInt UPInt::operator++(int)
{
UPInt oldValue = *this; // 取回值
++(*this); // 增加
return oldValue; // 返回被取回的值
}
几点分析:
A、注意前缀与后缀形式返回值类型是不同的。 前缀形式返回一个引用,后缀形式返回一个const类型。之所以后缀要返回一个const类型,主要原因是在于对i++++之类的操作,一个是标准的operator++不支持,还有就是即使支持返回一个非const类型,那么得到的也不是用户想要的最初值,而是经过一次自增之后的值;
B、很清楚可以看到后缀形式有临时变量的生成,所以从代码效率考虑,应该 尽可能的使用前缀形式;
C、从前缀和后缀代码的维护考虑,后缀increment和decrement应该根据它们的前缀形式来实现(后缀形式代码中调用了前缀形式的函数)。你仅仅需要维护前缀版本,因为后缀形式自动与前缀形式的行为一致。