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(重要 ...
随机推荐
- Boost学习笔记(三) progress_timer
progress_timer也是一个计时器,它继承自timer,会在析构时自动输出时间,省去timer手动调用elapsed()的工作,是一个用于自动计时相当方便的小工具. #include < ...
- AFN设置请求超时时间
进入AFURLRequestSerialization.m 找到 - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLSt ...
- web前端之HTML中元素的区分
作为前端人员,我们就是要与各种超文本标记打交道,用到各种不同的标签元素.在使用的时候不知道有没有注意到他们的分类归属?现在就来说一说博主的见解: 目前博主总结了三种分类方法:一是按封闭来划分,一是按显 ...
- 遗传算法在JobShop中的应用研究(part4:变异)
下面,我们以车间调度为例来谈谈遗传算法中的另一个重要操作变异.变异操作通常发生在交叉操作之后,它的操作对象是交叉得到的新染色体.在本文中我们通过随机交换染色体的两个位置上的值来得到变异后的染色体,变异 ...
- 安装软件时出现error 1337 【添加权限】
Error 1317 An error occurred while attempting to create the directory Drive Name:\Folder Name https: ...
- 关于String str =new String("abc")和 String str = "abc"的比较
String是一个非常常用的类,应该深入的去了解String 如: String str =new String("abc") String str1 = "abc&qu ...
- VBA中方法传参
将变量做为参数传递给方法 Sub Test() Dim a As Integer a = Add a Debug.Print a '引用传递,a的值发生了变化,输出101 End Sub Functi ...
- PHP---Mysql常用语法(增删改查)
1.数据库操作: 创建数据库:create databas ...
- web项目总结——通过jsp+servlet实现对oracle的增删改查功能
1.DAO模式 分包:依次建立 entity:实体包,放的是跟oracle数据库中表结构相对应的对象的属性,也就是这个对象有什么 dao:增删改查接口,实现增删改查的具体方法 service:同dao ...
- Linux系统下配置JDK环境变量
刚申请了阿里云,平时很少接触Linux,特此记录一下Linux系统下安装JDK的步骤. 1.进入usr:cd /usr: 2.创建java文件夹:mkdir java: 3.将下载好的文件拷贝至jav ...