code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类
0 C++中多态的概念
多态是指通过基类的指针或者引用,利用虚函数机制,在运行时确定对象的类型,并且确定程序的编程策略,这是OOP思想的核心之一。多态使得一个对象具有多个对象的属性。class Core作为就算成绩的基类、class Grad为Core的子类,添加了论文(thesis)成绩,Grad最终成绩为论文成绩和基类计算方法得到的成绩中的较小值。这是一个知识点:继承的适用场合就是,子类和基类的功能是相同或者相似,但是子类多了一些扩展。
//filename:Core.h
#ifndef GUARD_Core_h
#define GUARD_Core_h #include <iostream>
#include <stdexcept>
#include <string>
#include <vector> class Core {
public:
Core(): midterm(), final() { }
Core(std::istream& is) { read(is); } std::string name() const; virtual std::istream& read(std::istream&);
virtual double grade() const; virtual ~Core() { }
protected:
std::istream& read_common(std::istream&);
double midterm, final;
std::vector<double> homework; virtual Core* clone() const { return new Core(*this); }
private:
std::string n;
friend class Student_info;
}; class Grad: public Core {
public:
Grad(): thesis() { }
Grad(std::istream& is) { read(is); } double grade() const;
std::istream& read(std::istream&);
private:
double thesis; Grad* clone() const { return new Grad(*this); }
};
1 使用句柄类的必要性推导及句柄类实现及示例
接下来讨论如何使用这两个类,因为这两个类是不同的类型,各自的read,grade函数都有不同的定义,如何简明地使用两个类就成了一个要解决的问题。
方案1是对于每个学生,先判断下是什么类型,然后声明这种类型的对象,调用类中定义的grade函数完成成绩统计工作,使用Core的方法如下,Grad的使用将以下程序的Core替换为Grad。
//main_1.cpp use Core& Grad
vector<Core> students;
Core record;
string::size_type maxlen = ; // read and store the data
while (record.read(cin)) {
maxlen = max(maxlen, record.name().size());
students.push_back(record);
} // alphabetize the student records
sort(students.begin(), students.end(), compare); // write the names and grades
for (vector<Core>::size_type i = ; i != students.size(); ++i) {
cout << students[i].name()
<< string(maxlen + - students[i].name().size(), ' ');
try {
double final_grade = students[i].grade(); // `Core::grade'
streamsize prec = cout.precision();
cout << setprecision() << final_grade
<< setprecision(prec) << endl;
} catch (domain_error e) {
cout << e.what() << endl;
}
}
方案2:对于方案1,为了使用Core、Grad的使用,代码极大的重复,不是好的编程实践。方案2的改进是声明一个Core*类型的对象,通过Core*类型调用虚函数,以多态的方式完成程序。
//main_2.cpp
int main()
{
vector<Core*> students;
Core* record;
char ch;
string::size_type maxlen = ; while (cin >> ch) {
if (ch == 'U')
record = new Core;
else
record = new Grad;
record->read(cin);
maxlen = max(maxlen, record->name().size());
students.push_back(record);
}
sort(students.begin(), students.end(), compare_Core_ptrs); for (std::vector<Core*>::size_type i = ; i != students.size(); ++i) {
cout << students[i]->name()
<< string(maxlen + - students[i]->name().size(), ' ');
try {
double final_grade = students[i]->grade();
streamsize prec = cout.precision();
cout << setprecision() << final_grade
<< setprecision(prec) << endl; } catch (domain_error e) {
cout << e.what() << endl;
}
delete students[i];
}
return ;
}
3 句柄类接口与实现
方案2精简了代码,并且保证了调用不同方案的灵活性,但这样的方案有个缺点:编程中需要随时记得为不同的对象分配空间,在对象使用后要记得销毁Core*分配的对象,回收空间,增加了程序出错的可能性。句柄类的优势就是将类似工作交给专门的句柄类完成,程序员使用句柄类完成统计成绩的工作。
//student_info.h
#ifndef GUARD_Student_info_h
#define GUARD_Student_info_h #include <iostream>
#include <stdexcept>
#include <string>
#include <vector> #include "Core.h" class Student_info {
public:
// constructors and copy control
Student_info(): cp() { }
Student_info(std::istream& is): cp() { read(is); }
Student_info(const Student_info&);
Student_info& operator=(const Student_info&);
~Student_info() { delete cp; } // operations
std::istream& read(std::istream&); std::string name() const {
if (cp) return cp->name();
else throw std::runtime_error("uninitialized Student");
}
double grade() const {
if (cp) return cp->grade();
else throw std::runtime_error("uninitialized Student");
} static bool compare(const Student_info& s1,
const Student_info& s2) {
return s1.name() < s2.name();
} private:
Core* cp;
}; #endif
//student_info.cpp #include <iostream> #include "Core.h"
#include "Student_info.h" using std::istream; istream& Student_info::read(istream& is)
{
delete cp;
char ch;
is >> ch;
if (ch == 'U') {
cp = new Core(is);
} else {
cp = new Grad(is);
} return is;
} Student_info::Student_info(const Student_info& s): cp()
{
if (s.cp) cp = s.cp->clone();
} Student_info& Student_info::operator=(const Student_info& s)
{
if (&s != this) {
delete cp;
if (s.cp)
cp = s.cp->clone();
else
cp = ;
}
return *this;
}
代码说明:student_info类中封装了基类指针,调用Core或者Grad中定义的方法,所以Student_info中要有和Core类相同的接口,该类的拷贝构造函数要得到指针指向的类的信息,得到原来值得副本,这个副本由clone函数得到,所以在Core和Grad中定义不同的clone函数获得当前值得副本:
virtual Core* clone() const { return new Core (*this); }
virtual Grad* clone() const { return new Grad (*this); }
因为Core和Grad都有各自的拷贝构造函数,所以将clone函数声明放在protected中,virtual是可以继承的,一般而言子类中继承virtual函数的参数和返回类型应该与基类相同,但是通过基类指针和基类引用调用的虚函数重新定义的时候,返回类型可以是子类指针或者子类引用类型,Grad* clone( )为protected或者private标签下都可以。
4 使用句柄类
//main_3.cpp #include "Student_info.h" using std::cin; using std::cout; using std::domain_error; using std::endl; using std::setprecision; using std::setw; using std::sort; using std::streamsize; using std::string; using std::vector; #ifdef _MSC_VER #include "../minmax.h" #else using std::max; #endif int main() { vector<Student_info> students; Student_info record; string::size_type maxlen = ; while (record.read(cin)) { maxlen = max(maxlen, record.name().size()); students.push_back(record); } sort(students.begin(), students.end(), Student_info::compare); for (std::vector<Student_info>::size_type i = ; i != students.size(); ++i) { cout << students[i].name() << string(maxlen + - students[i].name().size(), ' '); try { double final_grade = students[i].grade(); streamsize prec = cout.precision(); cout << setprecision() << final_grade << setprecision(prec) << endl; } catch (domain_error e) { cout << e.what() << endl; } } return ;
}
自己实现句柄类机制
5 实现代码
A——基类,B——A的子类,Handle——句柄类,检测拷贝构造函数、operator=是否正确,代码如下:
#include <iostream>
using namespace std; class A {
protected:
int len;
virtual A* clone() {return new A(*this);}
private:
int HandleA;
friend class Handle;
public:
A():len() { }
A(int a) { len = a;}
A(istream& is) {read(is);}; virtual istream& read(istream& is); virtual int sum() {return len + ;}
virtual ~A(){}; }; istream& A::read(istream& is) {
is >> len;
return is;
} class B : public A {
public:
B():HandleB () { }
B(int b) { HandleB = b; }
B(istream& is) {read(is);} istream& read(istream& is); int sum() { return HandleB * ;}
protected:
B* clone() {return new B(*this);} //private & protected both OK!
private: int HandleB;
}; istream& B::read(istream& is) {
is >> HandleB;
return is;
} class Handle {
public:
Handle():pa(){}
istream& read(istream& is); Handle(Handle& f) { if(f.pa) pa = f.pa->clone(); else pa = ;}
Handle& operator= (Handle& f) {
if( &f != this){
delete pa;
if(f.pa) pa = f.pa->clone();
else pa = ;
}
return *this;
}
int sum () {
if(pa) return pa->sum();
else {
pa = ;
return ;}
}
~Handle() {delete pa ;} private:
A* pa;
};
istream& Handle::read(istream& is) {
delete pa;
char ch;
is >> ch;
if (ch =='a' || ch == 'A'){
pa = new A (is);
}
else if(ch =='b' || ch == 'B') {
pa = new B(is);
}
else {
pa = ;
}
return is;
} void main ()
{
Handle f;
A a(); f.read(cin);
Handle g(f);
Handle h = f; cout << f.sum() << endl;
cout << g.sum() << endl;
cout << h.sum() << endl;
} // 第一个计算结果由句柄对象f调用sum函数得到,第二个计算结果拷贝f初始化g,调用g的sum得到,第三个计算机结果先用f赋值给h,调用h的sum计算得到,3个计算结果应该一致。
code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类的更多相关文章
- Google Chrome 源码下载地址 (Google Chrome Source Code Download)
1. Google Chrome 源码 SVN 地址:http://src.chromium.org/svn.包含有 Chrome.Gears.Webkit.GCC 等源码以及编译依赖工具.Chrom ...
- google code 上源码的下载方法
SVN全称是Subversion,是Apache的一个子项目 ,具体能够到SVN中文站(http://www.subversion.org.cn/)去了解下.Google Code是Google的一个 ...
- Nginx code 常用状态码学习小结
最近了解下Nginx的Code状态码,在此简单总结下.一个http请求处理流程: 一个普通的http请求处理流程,如上图所示:A -> client端发起请求给nginxB -> ngin ...
- git android.google 源码:Unknown SSL protocol error in connection to code.google.com:443
想要提取android的源码.就必须要使用git.下面是本人安装的过程发生的问题: 1.1安装git.win的命令行的客户端(相当与svn的乌龟那样使用).http://git-scm.com/dow ...
- Asp.Net MVC4+EF6 Code First 权限管理系统 源码下载
这个权限管理系统是基于在@TZHSWEET 的权限管理系统之上做的修改.@TZHSWEET 那个是DB first. 这个是Code First.源码下载:http://download.csdn.n ...
- LIRE教程之源码分析 | LIRE Tutorial of Analysis of the Source Code
LIRE教程之源码分析 |LIRE Tutorial of Analysis of the Source Code 最近在做地理图像识别和检索的研究,发现了一个很好用的框架LIRE,遂研究了一通.网上 ...
- 编译Code::Blocks源码 with MinGW on Win
Build Code::Blocks源码 ---By 狂徒归来 CodeBlocks是一款非常优秀的IDE !可惜的是没有64位的版本,而且本来是轻量级别的IDE就应该够轻,能够像记事本工具一样,迅速 ...
- tinymce原装插件源码分析(三)-code
code: 用于显示源码.主要包含一个弹框.设置显示内容以及内容的更新. function showDialog() { var win = editor.windowManager.open({ t ...
- Visual Studio Code - 在 JS 源码(TS、压缩前代码)上使用断点
步骤: 在构建工具(webpack.gulp 等)的配置中开启生成 source map 将 VSCode 配置中的debug.allowBreakpointsEverywhere设置为true(重要 ...
随机推荐
- RabbitMQ 入门 Helloworld
1.介绍 RabbitMQ 是信息传输的中间者.本质上,他从生产者(producers)接收消息,转发这些消息给消费者(consumers).换句话说,他能够按根据你指定的规则进行消息转发.缓冲.和持 ...
- sql-按周输出每月的周日期范围
--日期参数,此处可以建立存储过程,接收月份,计算月开始结束时间或者直接接受开始与结束时间 declare @begDate datetime = '2014-06-01' declare @endD ...
- sublimetext3备份
http://files.cnblogs.com/files/hwd13/Data.zip http://files.cnblogs.com/files/hwd13/sublime3.zip
- hihoCoder 1426 : What a Ridiculous Election(总统夶选)
hihoCoder #1426 : What a Ridiculous Election(总统夶选) 时间限制:1000ms 单点时限:1000ms 内存限制:256MB Description - ...
- LayoutInflater的使用
在实际工作中,事先写好的布局文件往往不能满足我们的需求,有时会根据情况在代码中自定义控件,这就需要用到LayoutInflater.LayoutInflater在Android中是“扩展”的意思,作用 ...
- iOS - Regex 正则表达式
1.Regex 定义 正则表达式又称正规表示法.常规表示法(英语:Regular Expression,在代码中常简写为 regex.regexp 或 RE),计算机科学的一个概念.正则表达式使用单个 ...
- 扒一扒自从买了kindle后看的书
一.<性别战争> 讲述的是动物界各类动物的xxx(你懂的),以动物拟人化的口吻来进行问答,十分的生动形象,虽说和自己的生活没有多大联系,但是偶尔了解一下,也是意外的不错的感觉. 二.< ...
- hdu4087ALetter to Programmers(三维旋转矩阵)
参考 三维旋转矩阵 + 矩阵加速 这个还要用到仿射变换. 平移 translate tx ty tz 1 0 0 tx 0 1 0 ty 0 0 1 tz 0 0 0 1 缩放 scale kx ky ...
- HTML基本标签
h1-h6:标题标签.(从大到小) p:段落标签. img:图片标签:属性src:图片的相对路径:alt:图片加载失败的提示语言. a:超链接标签:属性:href:地址链接:target:网页打开的默 ...
- 严重:Error listenerStart
转自: http://1985wanggang.blog.163.com/blog/static/7763833200942611050436/ 近日浏览论坛,发现好多人提问,都说在运行web程序时, ...