【C++ Primer】第十四章 C++中的代码重用
序:C++的一个主要目标是促进代码重用,其中包含公有继承、包含、使用私有或保护继承
一,包含对象成员的类
1)valarray类简介 #include <valarray>
作用:处理数值,支持数值中所有元素的值相加,找最大值,最小值
用法:vallarray <int> a; //数组 a size=0
vallarray <double> b(10); //数组 b size=10
vallarray <double> c(10,8); //数组 c size=8 每个元素设置为 10
int s={2,3,4,5,6}; vallarray <double> d(s,3); //数组d 取s的前三个元素
2)student 类设计
#include <iostream> #include <string> #include <valarray> using namespace std; class student { private: typedef valarray<double> ArrayDb;//定义了一个double数组 的别名 string name; ArrayDb scores;//数组 ostream & arr_out(ostream &os)const; public: /*已经初始化的类*/ student():name("NULL Student"),scores(){}; student(const string &s):name(s),scores(){}; explicit student(int n):name("NULLy"),scores(n){}; //关闭隐式转换 因为n为数组个数 student(const string &s,int n):name(s),scores(n){}; student(const string &s,ArrayDb a):name(s),scores(a){};//分数为数组 student(const char *str,const double *pd,int n):name(str),scores(pd,n){}; ~student(){cout<<"\nstudent is over";}; double Average()const; const string &Name()const; double &operator[](int i); double operator[](int i)const; /*友元函数*/ friend istream &operator>>(istream &is,student &stu); /*如果是成员函数,而不是友元函数的话,student>>cin 这样调用*/ friend istream &getline(istream &is,student &stu); friend ostream &operator<<(ostream &os,const student &stu); }; double student::Average()const //求student 平均分 { if(scores.size()>0) return scores.sum()/scores.size(); else return 0; } const string &student::Name()const { return name; } double &student::operator[](int i) { return scores[i]; } double student::operator[](int i)const { return scores[i]; } ostream & student::arr_out(ostream &os)const //将所有结果输出 { int i; int lim=scores.size(); if(lim>0) { for(i=0;i<lim;i++) { os<<scores[i]<<" "; if(i%5==4) os<<endl; } if(i%5!=0) os<<endl; } else os<<"empty array"; return os; } istream &operator>>(istream &is,student &stu) { is>>stu.name; return is; } istream &getline(istream &is,student &stu) { getline(is,stu.name); return is; } ostream &operator<<(ostream &os,const student &stu) { os<<"scores for "<<stu.name<<":\n"; stu.arr_out(os); return os; } void set(student &s,int n) { cout<<"please enter the student's name:"; getline(cin,s); cout<<"please enter "<<n<<"quiz scores:\n"; for(int i=0;i<n;i++) cin>>s[i]; while(cin.get()!='\n') continue; } int main() { student ada[3]={student(5),student(5),student(5)}; //初始化学生数组 int i; for(i=0;i<3;++i) set(ada[i],5); //初始化学生 cout<<"\nStudent List"; for(i=0;i<3;++i) //学生名单 cout<<ada[i].Name()<<endl; cout<<"\nResult"; for(i=0;i<3;++i) //学生平均分 { cout<<endl<<ada[i]; cout<<"avaverage: "<<ada[i].Average()<<endl; } cout<<"\nDone.\n"; return 0; }
1)typedef:用来声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。
typedef double double_a; //为已知类型 double起别名 double_a
typedef struct node{
char *name;
int age;
} *Node;
typedef valarray<double> ArrayDb;//定义了一个double数组 的别名
2)explicit : 关闭构造函数的隐式转换
explicit student(int n):name("NULLy"),scores(n){};
如果 student dod("xiaotian",10);//定义对象 名字为“xiaotian" 数组有5个元素
dod=5;//重新定义dod对象 名字为空,数组有5个元素
如果没有explicit 则调用student(5); 将5转化为一个临时student 对象
二,私有继承:has-a关系
1)class student : private string, private valarray<double>
不同种类的继承
特征 | 公有继承 | 保护继承 | 私有继承 |
共有成员变成 | 派生类的公有成员 | 派生类的保护成员 | 派生类的私有成员 |
保护成员变成 | 派生类的保护成员 | 派生类的保护成员 | 派生类的私有成员 |
私有成员变成 | 派生类的私有成员 | 派生类的私有成员 | 派生类的私有成员 |
能否隐式向上转换 | 是 | 是 | 是 |
使用using 重新定义访问权限,让私有继承中的方法也可以调用
class student :private string,private valarray<double>
{
public:
using std::valarray::mix; //using 声明只使用 成员名,没有圆括号
using std::valarray::min;
}
三,多重继承
#include <string> #include <iostream> using namespace std; class worker { private: string fullname; long ID; public: worker():fullname("no one"),ID(0L){}; worker(const string &s, long n):fullname(s),ID(n){}; virtual ~worker()=0;// 虚函数 virtual void set(); virtual void show() const; } ; class waiter:public worker //服务员 { private: int panache; public: waiter():worker(),panache(0){}; waiter(const string &s,long n,int p=0):worker(s,n),panache(p){}; waiter(const worker &wk,int p=0):worker(wk),panache(0){}; void set(); void show()const; }; class singer:public worker { protected: enum{ others,alto,constralto,soprano,bass,baritone,tenor }; enum{Vtypes = 7}; private: static char *pv[Vtypes]; int voice; public: singer():worker(),voice(others){}; singer(const string &s,long n,int v=others):worker(s,n),voice(others){}; singer(const worker &wk,int v=others):worker(wk),voice(others){}; void set(); void show()const; }; worker::~worker(){ }//虚函数必须实现 void worker::set() { cout<<"Enter worker's name:"; getline(cin,fullname); cout<<"Enter worker's ID: "; cin>>ID; while(cin.get()!='\n')//记住这里是字符而不是字符串 continue; } void worker::show()const { cout<<"name:"<<fullname<<endl; cout<<"employee id"<<ID<<endl; } //waiter method void waiter::set() { worker::set(); cout<<"Enter waiter's panache rating :"; cin>>panache; while(cin.get()!='\n')//记住这里是字符而不是字符串 continue; } void waiter::show()const { cout<<"category:waiter\n"; worker::show(); cout<<"panache rating :"<<panache<<"\n"; } //singer method char *singer::pv[]={ "other","alto","constralto","soprano","bass","baritone","tenor" }; void singer::set() { worker::set(); int i; for(i=0;i<Vtypes;++i) { cout<<i<<":"<<pv[i]<<" "; if(i%4== 3) cout<<endl; } if(i%4==0) cout<<endl; cin>>voice; while(cin.get()!='\n') continue; } void singer::show()const { cout<<"category:singer\n"; worker::show(); cout<<"vocal range :"<<pv[voice]<<endl; } const int LIM=4; int main() { waiter bob("bob apple",314L,5); singer bev("beverly hills",522L,3); waiter w_temp; singer s_temp; worker *pw[LIM]={&bob,&bev,&w_temp,&s_temp}; int i; for(i=2;i<LIM;++i) pw[i]->set(); for(i=0;i<LIM;i++) { pw[i]->show(); cout<<endl; } return 0; }
1)singer类和waiter类都继承了一个worker组件,如果新建一个类singerwaiter,
class singerwaiter :public singer,public waiter{};
则singerwaiter包含两个worker组件,这将引起问题。例如,通常可以将派生类的对象的地址赋给基类指针但是现在回出现二义性。
singerwaiter ed;
worker *pw = &ed;//这 会出现二义性
解决办法:
1>worker *pw=(waiter *)&ed;
worker *pw=(singer *)&ed;
2>虚基类
class singer:virtual public worker{};
class waiter:public virtual worker{};
这时 class singerwaiter:public singer,public worker;
2)C++在基类是虚拟的时候,禁止信息通过中间类自动传递给基类
3)singerwaiter ss; ss.show();//引起二义性
ss.singer::show();//解决问题
四,类模板
1)template <class Type> //template 告诉编译器,将要定义一个模板。class 是类型名,Type 是该变量名
2)简单的模板使用
#include <iostream> #include <string> #include <cctype> using namespace std; template <class Type> class stack { private: enum{MAX=10}; Type items[MAX]; int top; public: stack(); bool isempty(); bool isfull(); bool push(const Type &item);//add item to stack 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(Type &item) { if(top>0) { item=items[--top]; return true; } else { return false; } } int main() { stack<string> st; //???????????? char ch; string po; cout<<"Please enter A to add a purchase order\n"<<"P to process a PO,or Q to quit.\n"; while(cin>>ch&&std::toupper(ch)!='Q') { while(cin.get()!='\n') continue; if(!std::isalpha(ch)) { cout<<'\a'; continue; } switch(ch) { case 'A': case 'a':cout<<"Enter a po number to add:"; cin>>po; if(st.isfull()) cout<<"stack is already full"<<endl; else st.push(po); break; case 'p': case 'P':if(st.isempty()) cout<<"stack already empty"<<endl; else { st.pop(po); cout<<"po#"<<po<<"popped\n"; break; } } cout<<"Please enter A to add a purchase order\n"<<"P to process a PO,or Q to quit.\n"; } cout<<"BYE\n"; return 0; }
说明:1)模板代码不能修改参数值,不能使用参数地址。所以不能使用n++和&n这种表达式
2)递归使用模板:Array<Array<int , 5>,10> a;//包含10个元素的数组,每个元素是包含5个元素的数组
int a[10][5]; //含有十行,每行包含5个元素
3)使用多个参数:template <class T1, class T2> //声明
class pa
{
T1 a;
T2 b;
};
pa<string ,int>("tianshuai",1);
复习题:虚基类与非虚基类之间的区别
如果两个继承路线有相同的祖先,则类中包含两个祖先的拷贝,而将基类设置成虚的则可以避免这种情况。
转载于:https://www.cnblogs.com/JPAORM/archive/2012/03/26/2510020.html
【C++ Primer】第十四章 C++中的代码重用相关推荐
- 《C++ Primer Plus(第六版)》(30)(第十四章 C++中的代码重用 编程题答案)
14.7 编程练习 1.Wine类有一个string类对象成员(参见第4章)和一个Pair对象(参见本章):其中前者用来存储葡萄酒的名称,而后者有2个valarry<int>对象(参见本章 ...
- 《操作系统真象还原》第十四章 ---- 实现文件系统 任务繁多 饭得一口口吃路得一步步走啊(总结篇)
文章目录 专栏博客链接 闲聊时刻 第十四章代码总览 编写完的fs.c(fs/fs.c) 编写完的fs.h(fs/fs.h) 编写完的dir.c(fs/dir.c) 编写完的dir.h(fs/dir.h ...
- 可视化工具D3.js教程 入门 (第十四章)—— 中国地图
可视化工具D3.js教程 入门 (第十四章)-- 中国地图 代码 <!DOCTYPE html> <html lang="en"><head>& ...
- 【c++ primer】第五版第十四章习题答案
第十四章 重载运算与类型转换 练习14.1 在什么情况下重载的运算符与内置运算符有所区别?在什么情况下重载的运算符又与内置运算符一样? 解: 不同点: 重载操作符不保证操作数的求值顺序,例如对& ...
- C++ Primer Plus习题及答案-第十四章
习题选自:C++ Primer Plus(第六版) 内容仅供参考,如有错误,欢迎指正 ! 第十四章- 代码重用笔记 复习题 1. 以A栏的类为基类时,B栏的类采用公有派生还是私有派生更合适 A B c ...
- 【《Real-Time Rendering 3rd》 提炼总结】(十一) 第十四章 : 游戏开发中的渲染加速算法总结
本文由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/78884513 导读 这是一篇1万3千 ...
- 【转载】【《Real-Time Rendering 3rd》 提炼总结】(十一) 第十四章 : 游戏开发中的渲染加速算法总结
本文由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/78884513 导读 这是一篇1万 ...
- 谭浩强《C++程序设计》书后习题 第十三章-第十四章
2019独角兽企业重金招聘Python工程师标准>>> 最近要复习一下C和C++的基础知识,于是计划把之前学过的谭浩强的<C程序设计>和<C++程序设计>习题 ...
- 工程项目管理丁士昭第二版_2021年软考系统集成项目管理工程师知识点预习第十四章第二节...
听说99%的同学都来这里充电吖 为了方便大家尽早投入2021年的软考考试备考中,我们已开始连载<系统集成项目管理工程师>知识点,今天带来的是 第十四章 第二节 编制询价~ 知识点:第十四章 ...
最新文章
- java如何监控cpu耗时_超级干货:3个性能监控和优化命令讲解
- 使用wxSqlite3来解决sqlite加密问题zz
- 我感觉ae比较难用,就是做这种画中画的视频,final cut pro真香
- PHP Collection 类
- 毕业设计上线啦!----跳蚤部落与基于Comet的WebIM系统开发
- 第三节:ThreadPool的线程开启、线程等待、线程池的设置、定时功能
- JavaScript实用代码片段
- kubernetes视频教程笔记 (33)-什么是 Helm
- 借助百度识图爬取数据集
- 用数据告诉你:中国最网红的城市在哪里
- Java OOP 思想
- Apache ab测试结果解析
- IWAM账号密码不一致引起IIS无法处理ASP文件
- 压力越大责任越大,数字基础设施还要“稳”字当头
- 正则(数字或者以英文逗号隔开的数字字符串)
- saiku安装方法总结
- 河南省 第十一届 ACM 省赛 试题
- 11篇推荐系统入门必读经典论文(附下载链接)
- DELL电脑开机自检提示please run setup program
- 新公司(企业)在各银行开基本户所需资料、流程及费用
热门文章
- 由pthread_create引起的段错误
- 第一个DFS,第一个递归 HDU1515
- SpringBoot整合Redis 之 StringRedisTemplate、RedisTemplate 基础
- 一篇文章总结暴力破解方法大全
- ubuntu18安装vnpyv1.9.2之二
- 从业16年,经历了这7个数据仓库的变化,总结出了这份干货
- 向maven中添加Oracle数据库的驱动,有效!
- mysql的设计模式_数据库设计中使用设计模式
- 昭阳k42-80可以装linux么,华硕K420JC 双显卡 本本 能装ubuntu吗
- 年度调薪一般涨多少_宁愿辞职也不敢提加薪,心理专家告诉你加薪提多少才合适...