第十四章 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. c#开发微信公众号——关于c#对象与xml的转换

    在成为微信公众号开发者以后,整个交互流程:用户->微信服务器->自己的服务器->返回微信服务器->用户: 举个例子:用户在微信公众号里面发了个“您好!”,微信服务器会以特定的x ...

  2. KKT原理以及SVM数学的理论推导分析

    一直很好奇机器学习实战中的SVM优化部分的数学运算式是如何得出的,如何转化成了含有内积的运算式,今天上了一节课有了让我很深的启发,也明白了数学表达式推导的全过程. 对于一个SVM问题,优化的关键在于 ...

  3. python 数据类型、进制转换

    数据类型 存储单位 最小单位是bit,表示二进制的0或1,一般写作b 最小的存储单位是字节,用byte表示,1B = 8b 1024B = 1KB 1024KB = 1MB 1024MB = 1GB ...

  4. 20155220 2016-2017-2《Java程序设计》课程总结

    20155220 2016-2017-2<Java程序设计>课程总结 每周作业链接汇总 预备作业1:师生关系 预备作业2:优秀技能经验 预备作业3:虚拟机linux初接触 第一周学习总结: ...

  5. 20155331 实验四 Android开发基础

    20155331丹增旦达实验四报告 实验四 Android程序设计-1 Android Stuidio的安装测试: 参考<Java和Android开发学习指南(第二版)(EPUBIT,Java ...

  6. 【转载】C/C++杂记:深入理解数据成员指针、函数成员指针

    原文:C/C++杂记:深入理解数据成员指针.函数成员指针 1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针.而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始 ...

  7. 【CF833E】Caramel Clouds

    [CF833E]Caramel Clouds 题面 洛谷 题目大意: 天上有\(n\)朵云,每朵云\(i\)会在时间\([li,ri]\)出现,你有\(C\)个糖果,你可以花费\(c_i\)个糖果让云 ...

  8. 转:后台管理UI的选择

    注:文中缺少了ZUI和LAYUI两个. 目录 一.EasyUI 二.DWZ JUI 三.HUI 四.BUI 五.Ace Admin 六.Metronic 七.H+ UI 八.Admin LTE 九.I ...

  9. Jmeter接口测试(一) Jmeter简介

    一.Jmeter介绍 (一)Jmeter简介 Apache JMeter 是 Apache 组织的开放源代码项目,是一个纯 Java 桌面应用,用于压力测试和性能测试.它最初被设计用于 Web 应用测 ...

  10. UTF-8编码下'\u7528\u6237'转换为中文汉字'用户'

    UTF-8编码下'\u7528\u6237'转换为中文'用户' 一.前言 有过多次,在开发项目中遇见设置文件编码格式为UTF-8,但是打开该文件出现类似\u7528这样的数据,看也看不懂,也不是平常见 ...