二十六、何时使用成员函数模板
关于成员函数模板见/Master_Cui/article/details/111824152
成员函数模板主要用来兼容不同类型的数据,正如链接中的mystack类模板一样,为了使int和double类型的数据能够彼此拷贝和赋值,需要对拷贝构造函数和operator=写成模板的形式
如果一个拷贝构造函数或者operator=被声明为成员函数模板,如果设计者没有声明非成员函数模板版本的拷贝构造函数或者operator=,编译器会为你暗自生成一个。也就是说,在类内声明成员函数模板版本的拷贝构造函数并不会阻止编译器生成非成员模板版本的拷贝构造函数,所以,如果想要自定义拷贝构造函数,必须同时声明成员函数模板版本的构造函数和正常的拷贝构造函数。相同规则也适用于operator=
二十七、模板类的实参推断与类型转换
下面的代码自定义了一个模板类fraction的operator*操作,并且operator*也是个函数模板
template <typename T>class fraction{public:fraction(const T &numerator=0, const T &denominator=1);~fraction();const T getnumerator() const {return numerator_;} //这两个get函数必须是const,因为要被operator*中的a,b调用const T getdenominator() const {return denominator_;}private:T numerator_;T denominator_;};template <typename T>fraction<T>::fraction(const T &numerator, const T &denominator):numerator_(numerator),denominator_(denominator){}template <typename T>fraction<T>::~fraction() {}template <typename T>const fraction<T> operator*(const fraction<T> &a, const fraction<T> &b){return fraction<T>(a.getnumerator()*b.getnumerator(), a.getdenominator()*b.getdenominator());}int main(int argc, char const *argv[]){fraction<int> onehalf(1,2);fraction<int> res=onehalf*2;return 0;}
上述代码之所以出错,是因为调用operator*时,需要模板参数推断,但是模板参数的推断过程不包括隐式类型转换,所以,编译器不能将2转化为fraction<int>(2, 1),然后在推断出T是int,而是直接显示const fraction<T> 与int类型不匹配
解决办法:将operator*设置为fraction的友元并在类内实现
template <typename T>class fraction{friend const fraction<T> operator*(const fraction<T> &a, const fraction<T> &b){return fraction<T>(a.getnumerator()*b.getnumerator(), a.getdenominator()*b.getdenominator());}public:fraction(const T &numerator=0, const T &denominator=1);~fraction();const T getnumerator() const {return numerator_;}const T getdenominator() const {return denominator_;}private:T numerator_;T denominator_;};
所以,如果在模板类中存在类型转换操作,需要将类型转换操作设置为友元并在类内实现
二十八、继承与数组
不要使用基类的指针处理子类的数组,示例代码如下
class base{friend ostream &operator<<(ostream &s, const base &d);public:base():ib(10){}~base(){}private:int ib;};class derive:public base{public:derive():id(20){}~derive(){}private:int id;};ostream &operator<<(ostream &s, const base &b){s<<b.ib;return s;}void print(ostream &s, const base arrayb[], int num){for (int i=0;i<num;++i) {s<<arrayb[i]<<endl;}}int main(int argc, char const *argv[]){derive arrayd[5];cout<<sizeof(base)<<sizeof(derive)<<endl;print(cout, arrayd, sizeof(arrayd)/sizeof(arrayd[0]));return 0;}
上述代码之所以没有输出5次10,而交替输出10,20。是因为编译时,编译器认为指针arrayb指向数组中,每个数组元素的大小是sizeof(base),所以。循环遍历时,只能访问到数组的前20个字节(5*sizeof(base))。但此时传入print的参数是derive的数组,这种情况下编译器仍假设数组中每一元素的大小是base对象的大小,但其实每一元素的大小是derive的大小。所以,也只能访问到derive的数组的前20个字节。所以就会输出上面的结果。所以,不要使用基类的指针处理子类的数组
参考
《Effective C++》
《More Effective C++》
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出