第十四章 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. SQL基于时间的盲注过程

    0x00 前言 由于要使用到基于时间的盲注,但是我觉得基于时间的盲注其实就是基于布尔的盲注的升级版,所以我想顺便把基于布尔的盲注分析总结了: 首先我觉得基于时间的盲注和基于布尔的盲注的最直观的差别就是 ...

  2. Gblocks命令行

    使用默认的设置: $ Gblocks proteins.fasta -t=p 必须是 fasta 文件在前,参数在后.若没有参数,则进入交互式界面. Gblocks cds.fasta −t=c −b ...

  3. CentOS 7.2重启网络报错 Failed to start LSB: Bring up/down

    CentOS 7.2重启网络报错 Failed to start LSB: Bring up/down 我的虚拟机原本有两块网卡,一块叫eno16777736,另一块叫eno5033674.本来是正常 ...

  4. python学习之路_字符编码

    字符编码及python中的转码问题,这篇博客讲的比较清楚,python 之路,致那些年,我们依然没搞明白的编码

  5. 20155220 吴思其 2016-2017《java程序设计》第一周总结

    对第一章和第二章的学习 通过了前两章的学习,我了解到了java的由来以及JVM/JRE/JDK三大平台 JDK JDK 是 Java 语言的软件开发工具包. JDK是整个JAVA的核心,包括了Java ...

  6. sort与qsort的异同

    主要内容: 1.qsort的用法 2.sort的用法 3.qsort和sort的区别 qsort的用法: 原 型: void qsort(void *base, int nelem, int widt ...

  7. [cogs2314][HZOI 2015] Persistable Editor - 可持久化平衡树

    [cogs2314][HZOI 2015]Persistable Editor - 可持久化平衡树 题目链接 首先吐槽扯淡几句 [题目描述] 维护一种可持久化的文本编辑器,支持下列操作: 1 p st ...

  8. angular promise $q 异步调用

    Angular异步调用 Promise和$q的用法 背景 首先说明一下promise异步调用出现的背景: javascript语言是一种单线程模式,就是说一次只能够执行一个任务,如果有多个任务的话就必 ...

  9. ubuntu 16.04 安装php 5 6等版本

    //加入ppa $ sudo add-apt-repository ppa:ondrej/php $ sudo apt-get update //安装5.6 $ sudo apt- //安装7 $ s ...

  10. 微服务介绍及Asp.net Core实战项目系列之微服务介绍

    0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.微服务选型 在做微服务架构的技术选型的时候,我们以“无侵入”和“社区活跃”为主要的考量点,将来升级为原子服务架构.量子服务架构 ...