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

关于成员函数模板见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++》

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

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

  1. c++ 纯虚成员函数+抽象基类

    公共接口是指一系列成员函数的集合,支持该接口的类必须以合适的方式重新定义这些成员函数,否则就无法创建对象.C++ 中可以通过抽象基类来实现公共接口,为了介绍抽象基类,我们需要先来了解一下纯虚成员函数. ...

  2. 基类成员函数和派生类成员函数不构成重载

    基类成员和派生类成员的名字一样时会造成遮蔽,这句话对于成员变量很好理解,对于成员函数要引起注意,不管函数的参数如何,只要名字一样就会造成遮蔽.换句话说,基类成员函数和派生类成员函数不会构成重载,如果派 ...

  3. C++中模板类中的成员函数以及模板函数在类外定义

    在C++中,类中的成员函数可以在类外完成定义,从而显得类中的成员函数看起来简洁明了.但是模板类里的成员函数和模板函数与普通的成员函数在类外定义不同. 先定义一个模板类以及成员函数和模板函数: 接下我们 ...

  4. C++编程进阶5(内联函数、如何降低编译成本、处理继承体系中同名不同参的成员函数、私有虚函数)

    十七.内联函数 在https://blog.csdn.net/Master_Cui/article/details/106391552中,已经简单的说过内联函数的作用. 函数体较小的内联函数经过编译后 ...

  5. c/c++教程 - 2.4.3 this指针作用,链式编程思想,空指针访问成员函数,const修饰成员函数,常函数,常对象

    目录 4.3 C++对象模型和this指针 4.3.1 成员变量和成员函数分开存储 4.3.2 this指针概念(非常重要) 4.3.3 空指针访问成员函数 4.3.4 const修饰成员函数 相关教 ...

  6. 在类内定义成员函数、在类外定义成员函数、计算长方体的体积【C++面向对象编程类的使用经典案例】

    文章目录 一.在类内定义成员函数 二.在类外定义成员函数(使用符号::) 三.计算3个长方体的体积(class成员函数) 一.在类内定义成员函数 #include <iostream> u ...

  7. 10.2.1 关于vc++不支持把类的成员函数定义为类的友元函数的处理

    •说明:有的C++编译系统(如Visual C++ 6.0)没有完全实现C++标准,它所提供不带后缀.h的头文件不支持把成员函数重载为友元函数.但是VisualC++所提供的老形式的带后缀.h的头文件 ...

  8. const成员函数、const类对象、mutable数据成员

    1. const成员函数 只是告诉编译器,表明不修改类对象. 但是并不能阻止程序员可能做到的所有修改动作,比如对指针的修改,编译器可能无法检测到 2. 类体外定义的const成员函数,在定义和声明处都 ...

  9. 3-9:C++默认成员函数练习-日期类实现

    文章目录 一:前言 二:准备 (1)实现文件 (2)基本布局 三:具体实现 (1)默认成员函数实现 A:全缺省构造函数 B:拷贝构造 C:析构函数 (2)赋值运算符重载 A:日期+=天数 B:日期+天 ...

最新文章

  1. C#中删除目录以及目录下文件的方法
  2. win10你的电脑设备需要修复_windows10系统崩溃怎么办,一键修复windows10
  3. oracle json入参调用ws服务返回请求失败_Spring 5.2.2技术集成 —Spring HTTP调用程序和JAXWS...
  4. 如何在数据表中存取图片 - 回复 三足乌 的问题
  5. sql 把特定数据排在最前面
  6. 2021吉林高考26日几点可以查询成绩,2021吉林高考成绩查分时间及入口
  7. 定了!网易CEO丁磊6月11日快手直播带货
  8. 美团延长旅行订单免费取消保障政策至2月29日
  9. oracle esb 灾备,两地三中心双活系统灾备切换场景和数据补录问题?
  10. es6-3.webpack应用
  11. c语言 圆周率10000位,计算圆周率 Pi (π)值, 精确到小数点后 10000 位(C语言)
  12. 2021最全数学建模比赛时间、含金量、获奖率等数据一览!
  13. sass-------sass的基本介绍、node.js的sass工具
  14. LinuxC学习日记
  15. 2018 Android开发面试经历
  16. GoF设计模式——状态模式(C++实现)
  17. is are am 三者的区别
  18. 动能方案|智能门锁超低功耗读卡方案
  19. 如何将USB接口打印机转换成RJ45网口并安装到打印服务器上共享使用?
  20. 立体声音频消音_如何更换立体声连接器并抢救音频电缆和耳机

热门文章

  1. 什么叫编译时和运行时
  2. !--处理:借款冲销不自动冲减预算--
  3. SecureCRT自动登录
  4. anndroid ndk使用
  5. c#读写XML文件 (转)
  6. Protocol Buffer序列化协议及应用
  7. NHibernate学习手记(3) - NH的配置信息
  8. 71《SQL学习指南(第二版)》mysql 的数据类型和范围
  9. SpringMVC:学习笔记(11)——依赖注入与@Autowired
  10. hive常用参数配置设置