第十四章 C++中的代码重用

包含对象成员的类

将类的对象作为新类的成员。称为has-a关系。使用公有继承的时候,类可以继承接口,可能还有实现(纯虚函数不提供实现,只提供接口)。使用包含时,可以获得实现,但是不能获得接口。

explicit关键字的用法:

防止单参数构造函数的隐式转换,例如定义了如下的构造函数:

Student::Student(const string &s, int n);

Student::Student(int n);

使用如下的定义:

Student st("Tom", 3);

st=5;     //这样调用会调用Student::Student(int n)

//生成一个Student对象,将原来的对象覆盖

使用explicit关键字后,上面的调用会报错,使用方法如下:

explicit Student::Student(int n);

构造函数在构造继承对象之前,先构建继承对象与所有成员对象。

类构造函数中的成员初始化列表的初始化顺序按照类声明中变量定义的顺序,而不是初始化列表的顺序。

私有继承

私有继承是C++另一种实现has-a关系的途径。使用私有继承,基类的公共成员和保护成员都将成为派生类的私有成员。

对于继承类的构造函数,成员初始化列表中使用类名而不是成员名来标识构造函数,如:

Student::Student(const char * str, const double *pd, int n)

:std::string(str),valarray<double>(pd,n)    { }

使用作用域解析符来调用基类的方法,如:

double Student::Average() const

{

    if(valarray<double>::size()>0)

        return valarray<double>::sum()/valarray<double>::size();

    else

        return 0;

}

访问基类对象的方法:使用强制类型转换

const string &Student::Name() const

{

    return (const string &) *this;

}

访问基类的友元函数:

os<<(const string &)stu<<std::endl;

多重继承

class SingingWaiter: public Waiter, public Singer {…};

如果不指定继承方式为public,默认为private。

如果多重继承的类含有一个共同基类,则会产生问题。即加入Waiter和Singer均继承自Worker类,则SingingWaiter将会产生两个Worker的副本,在使用多态性时将会产生二义性问题。可以使用强制类型转换解决一部分问题,如:

SingingWaiter ed;

Worker * pw1=(Waiter *)&ed;

Worker * pw2=(Singer *)&ed;

我们可以使用虚基类技术解决这个问题

虚基类

虚基类使得从多个类(基类相同)派生出来的对象只继承一个基类对象。在类声明中使用关键字virtual 即可。

如:

class Singer: virtual public Worker{};

class Waiter: public virtual Worker{};

定义SingingWaiter如下:

class SingingWaiter: public Singer, public Waiter {…};

SingingWaiter对象中只含有Worker对象的一个副本。

C++在基类是虚的时候,禁止信息通过中间类自动传递给基类。即不能通过调用Waiter构造函数来调用Worker类的构造函数构造Worker,故SingingWaiter类的构造函数如下:

SingingWaiter(const Worker & wk, int p=0, int v=Singer::other)

                :Worker(wk), Waiter(wk,p), Singer(wk,v) {}

通过直接调用Worker类的构造函数来构造Worker对象。

若想调用基类中相同的函数,使用作用域解析符

void SingingWaiter::Show()

{

        Singer::Show();

}

类模板

C++的类模板为生成通用的类声明提供了一种更好的办法,模板提供参数化类型,即能够将类型名作为参数传递给接收方来建立类或者函数。

C++提供了很多模板类,如valarray,vector,array和STL(标准模板库)。

由于模板不是函数,他们不能单独编译,所以模板必须与特定的模板实例化请求一起使用。为此,最简单的方法就是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该头文件。

定义模板类

表 0-1 模板类stacktp.h

//stacktp.h – a stack template

#ifndef STACKTP_H_

#define STACKTP_H_

template <class Type>

class Stack

{

private:

enum{MAX=10}; //

Type items[MAX];

int top; //栈顶索引

public:

Stack();

bool isempty();

bool push(const Type & item); //压栈

bool pop(Type & item); //出栈

}

 

template<class Type>

Stack<Type>::Stack()

{

top=0;

}

 

template<class Type>

bool Stack<Type>::isempty()

{

return top==0;

}

 

template<class Type>

bool Stack<Type>::isfull()

{

return top==MAX;

}

 

template<class Type>

bool Stack<Type>::push(const Type & item)

{

if(top<MAX)

{

items[top++]=item;

return true;

}

else

return false;

}

 

template<class Type>

bool Stack<Type>::pop(const Type & item)

{

if(top>0)

{

item=items[--top];

return true;

}

else

return false;

}

#endif

非类型参数

template<class T, int n>

int n指定特殊的类型而不是用作泛型名称为非类型或表达式参数。

表达式参数有一些限制,可以为整型、枚举、引用或者指针。double m不合法,但是double * pm和double& rm合法。模板代码不能修改表达式的值,也不能使用使用参数的地址。实例化模板时,用作表达式参数的值必须是常量表达式。

模板的具体化

隐式实例化:

stacktp<int> stp;    //声明一个类并实例化一个对象

或者

stacktp<int> *pst;//仅声明一个指针

pst=new stacktp<int>;    //创建一个对象

显式实例化

template class stacktp<int>;

显式具体化

template <> class Classname<specialized-type-name> {…};

如:template <> class stacktp<const char *>{};//重定义stacktp

部分具体化:

//通用模板

template <class T1, class T2> class Pair{…};

template <class T1> class Pair<T1,int> {…};

template<> class Pair<int, int> {…};

编译器将根据具体化程度最高的模板

Pair<double,double> p1;//通用模板

Pair<double,int> p2;//Pair<T1,int>模板

Pair<int,int> p3;//Pair<int,int>模板

template<class T> class Feeb{…};

template<class T*> class Feeb{…};

Feeb<char> fb1;//调用第一个

Feeb<char *> fb2;//调用第二个

成员模板

模板可以作为结构、类或者模板类的成员

表 0-2 tempmemb.h

//tempmemb.h – template as member of template class

#include<iostream.h>

using std::cout;

using std::endl;

template <typename T>

class beta

{

private:

template <typename V>

class hold

{

private:

V val;

public:

hold(V v=0):val(v){}

void show() const {cout<<val<<endl;}

V Value() const {return val;}

};

hold<T> q;

hold<int> n;

public:

beta(T t,int i):q(t),n(i){}

template<typename U> //函数模板

U blab(U u,T t){return (n.Value()+q.Value)*u/t;}

void show() const{q.show();n.show();}

} ;

将模板用作参数

template <template <typename T> class Thing>

class Crab

Thing必须是一个模板类

Crab<Stack>

模板类和友元

模板类的非模板友元函数

template<class T>

class HasFriend

{

friend void report(HasFriend<T> &);

};

模板类的约束模板友元函数

模板类的约束模板友元函数

先声明模板函数

template <typename T> void counts();

声明模板类

template <typename TT>

class HasFriendT

{

friend void counts<TT>();

friend void report<>(HasFriend<TT> &);

};

模板类的非约束模板友元函数

template <typename T>

class ManyFriend

{

    template <typename C, typename D> friend void show2(C &,D &);

};

C++ Primer Plus学习:第十四章的更多相关文章

  1. C++ Primer Plus学习:第四章

    C++入门第四章:复合类型 1 数组 数组(array)是一种数据格式,能够存储多个同类型的值. 使用数组前,首先要声明.声明包括三个方面: 存储每个元素中值的类型 数组名 数组中的元素个数 声明的通 ...

  2. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段 代码工程地址: https://github. ...

  3. 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记

    第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...

  4. 【C++】《C++ Primer 》第十四章

    第十四章 重载运算与类型转换 一.基本概念 重载运算符是具有特殊名字的函数:由关键字operator和其后要定义的运算符号共同组成.也包含返回类型.参数列表以及函数体. 当一个重载的运算符是成员函数时 ...

  5. 第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)

    本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks) ...

  6. “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. “全栈2019”Java多线程第二十四章:等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. “全栈2019”Java多线程第十四章:线程与堆栈详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. “全栈2019”Java异常第十四章:将异常输出到文本文件中

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

随机推荐

  1. 关于sparkStreaming(spark on yarn)的一个坑!

    前些天我维护的一个streaming实时报表挂了,情况:数据无法实时更新增长,然后查看了报表所依赖的五张sqlserver的表,发现,只有1张的数据是正常写入的,还一张数据非正常写入,还有3张完全没有 ...

  2. python列表学习

    #创建列表,通过[]来创建列表my_list=[] #创建了一个空列表#print(my_list,type(my_list)) #列表追存储的数据,我们称为元素#一个列表中可以存储多个元素,也可以在 ...

  3. Linux命令—tar

    day 7:tar压缩.解压缩.打包.解包命令 区分打包和压缩:打包是指将一大堆文件或目录变成一个总的文件:压缩则是将一个大的文件通过一些压缩算法变成一个小文件. 为什么要区分这两个概念呢?这源于Li ...

  4. C++ —— 非类中使用const定义常量的初始化,以及#define和typedef的区别

    总结一下在非类中使用const关键字定义常量时的初始化问题,亲测VS2015.顺便记录#define宏和typedef的区别. 1 首先对const声明的常量的初始化做简单小结: , w2 = , w ...

  5. 20155234 《Java程序设计》实验四 (Android程序设计)实验报告

    实验内容 基于Android Studio开发简单的Android应用并部署测试; 了解Android.组件.布局管理器的使用: 掌握Android中事件处理机制. 实验步骤 (一)Android S ...

  6. 20155310 2016-2017-2 《Java程序设计》第十周学习总结

    20155310 2016-2017-2 <Java程序设计>第十周学习总结 教材学习内容总结 网络编程 •网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就 ...

  7. java课堂实践(5月17日)20155317 王新玮

    对P145 MathTool.java 代码托管 在IDEA中,使用JUnit进行单元测试,测试用例不少于三个,要包含正常情况,边界情况.提交测试代码和运行结果截图,加上学号水印,提交码云代码链接. ...

  8. 20155321 《Java程序设计》实验五 网络编程与安全

    实验内容 两人一组结对编程: 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA 结对实现中缀表达式转后缀表达式的功能 MyBC.java 结对 ...

  9. Qt-QML-电子罗盘

    使用QML中的Canvas实现电子罗盘绘制,效果图如下 一个简单的电子罗盘,红色N极.其中中间飞机表示当前的指向, 还是比较简单的,直接上代码吧 /* 作者:张建伟 时间:2018年4月27日 简述: ...

  10. 令自己的本地ip可以被外网访问

    https://www.ngrok.cc/_book/general/open.html