第十四章 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. PHP Ajax跨域问题解决办法

    在项目开发中,经常会遇到跨域访问资源,上传图片等,那么这些都怎么解决呢,下面简单介绍一下ajax请求时,解决跨域问题. 原文地址:小时刻个人博客 > http://small.aiweimeng ...

  2. stm32串口通信实验,一点笔记

    第一次深入学习stm32,花了好长时间才看懂代码(主要是C语言学习不够深入),又花了段时间自己敲了一遍,然后比对教程,了解了利用中断来串口通信的设置方法. 板子是探索版f407,本实验工程把正点原子库 ...

  3. Jquery 批量操作标签属性

     $("[id*='Custom']").removeAttr("disabled")

  4. sqli-labs(less-11-16)

    POST登入 首先试试 uname=admin'# & passwd=1 登入成功 如果不知道用户名 ,注释符被过滤,可以从password入手 一般第一个登陆字段(一般是用户名)就用注释,第 ...

  5. jenkins + ansible + docker 代码集成发布

    一.环境搭建 1. 安装Java 配java_home, /etc/profile 2.安装Jenkins 下载war包,用 Java -jar  Jenkins.war或者  把war包放tomca ...

  6. BurpSuite系列(一)----Proxy模块(代理模块)

    一.简介 Proxy代理模块作为BurpSuite的核心功能,拦截HTTP/S的代理服务器,作为一个在浏览器和目标应用程序之间的中间人,允许你拦截,查看,修改在两个方向上的原始数据流. Burp 代理 ...

  7. Hive中Join的类型和用法

    关键字:Hive Join.Hive LEFT|RIGTH|FULL OUTER JOIN.Hive LEFT SEMI JOIN.Hive Cross Join Hive中除了支持和传统数据库中一样 ...

  8. 逆向某停车app(原创)

    最近一直在做python开发的事情,信息安全方面做得很少,也是"蛋蛋"的忧伤呀.今天有朋友请我帮忙,将一个app里的文字和图标替换一下,花了一下午和一晚上的时间搞了一下,主要是图标 ...

  9. 拼多多商品id怎么查看 拼多多店铺ID怎样看

    网上开店平台有很多编号.id等可以区分商品和店铺的标志,拼多多有店铺id也有商品id,这是两个不同的概念,店铺id进入到拼多多店铺即可查询,拼多多商品id怎么查看 拼多多店铺ID怎样看,那么拼多多商品 ...

  10. ubuntu dpkg出现语法错误:安装软件提示无效组件

    当安装软件是提示组件错误而导致不能安装,特别是以前卸载软件之后,再安装新版本的软件,其实就是之前卸载的时候没有按照正确的方法卸载引起的 解决方法如下: 使用sudo授权 1.  列出sudo dpkg ...