C++编程进阶7(何时使用成员函数模板,模板类的实参推断与类型转换、继承与数组)

本文讲解了如何在C++中使用成员函数模板处理不同数据类型,以及在模板类中遇到的类型转换问题。通过实例说明了为何避免基类指针处理子类数组,并给出了相关书籍《Effective C++》和《More Effective C++》作为参考。
摘要由CSDN通过智能技术生成

二十六、何时使用成员函数模板

关于成员函数模板见https://blog.csdn.net/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++》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值