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(重要 ...
随机推荐
- dom节点的操作
dom节点的操作 -- jQuery 内部插入 1.(结尾)append 方法 . appendto方法(为了方便链式操作) (开头)prepend方法 $('#div1').ap ...
- java回调初步学习
转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273),请尊重他人的辛勤劳动成果,谢谢以前不理解什 ...
- scrapy基础教程
1. 安装Scrapy包 pip install scrapy, 安装教程 Mac下可能会出现:OSError: [Errno 13] Permission denied: '/Library/Pyt ...
- 剑指offer七:两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点. import java.util.*; public class Solution { public ListNode FindFirstCommonNode ...
- nginx缓冲区优化
关于缓冲, 主要是合理设置缓冲区大小, 尽量避免缓冲到硬盘时的情况 proxy_buffering proxy_buffering这个参数用来控制是否打开后端响应内容的缓冲区,如果这个设置为off,那 ...
- js : json和 cookie 的简单操作
使用 cookie,可以记录用户的最近的浏览历史 <!DOCTYPE HTML> <html lang="zh-cn"> <head> < ...
- eclipse maven web环境搭建
选择创建new project 勾选跳过创建类型选择(让eclipse创建标准maven项目) 填写组织ID,唯一ID,注意:如果选择打包类型为war包时,会生成web类型的maven工程 修改JRE ...
- javascript中的事件冒泡、事件捕获和事件执行顺序
谈起JavaScript的 事件,事件冒泡.事件捕获.阻止默认事件这三个话题,无论是面试还是在平时的工作中,都很难避免. DOM事件标准定义了两种事件流,这两种事件流有着显著的不同并且可能对你的应用有 ...
- Connection broken for id 62, my id = 70, error =
启动费zokeeper失败,报错如下:Connection broken for id 62, my id = 70, error = 原因是因为zoo.cfg中server.id不正确. serve ...
- ios cordova报gap://ready 弹出框,一直弹
The iOS app was not working because there was a plugin missing: https://github.com/apache/cordova-pl ...