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(重要 ...
随机推荐
- 遗传算法在JobShop中的应用研究(part4:变异)
下面,我们以车间调度为例来谈谈遗传算法中的另一个重要操作变异.变异操作通常发生在交叉操作之后,它的操作对象是交叉得到的新染色体.在本文中我们通过随机交换染色体的两个位置上的值来得到变异后的染色体,变异 ...
- CAS单点登录和spring securtiy集成
说明:本文章主要建立在spring-security早已集成在系统中的前提下: 1.需要创建一个spring-security.xml文件并关联在applicationContext.xml文件中:& ...
- git中忽略UserInterfaceState.xcuserstate的方法
在commit 时候一直会提示userinterfacestate.xcuserstate文件尚未commit. 你可以用命令行 git rm --cached [YourProjectName].x ...
- Javascript中的集合
集合是由一组无序且唯一(即不能重复)的项组成 function Set() { var items={}; this.has=function(value){ //return value in it ...
- spring 注解重复(防重复请求)
1.配置拦截器 spring-mvc.xml <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/ ...
- windows PHONE 开发-入门程序构筑
1:页面的扩展名为:.xaml文件类似于ASPX一样可以编写客户端显示内容和后台处理内容 一般的前台页面的形式为: <Page x:Class="MyFirstApp.MainPage ...
- 零配置文件搭建SpringMVC实践纪录
本篇记录使用纯java代码搭建SpringMVC工程的实践,只是一个demo.再开始之前先热身下,给出SpringMVC调用流程图,讲解的是一个http request请求到达SpringMVC框架后 ...
- C#中对IDisposable接口的理解
http://blog.sina.com.cn/s/blog_8abeac5b01019u19.html C#中对IDisposable接口的理解 本人最近接触一个项目,在这个项目里面看到很多类实现了 ...
- OneSQL安装
下载 www.onexsoft.com -> 下载 -> OneSQL 安装 yum install onesql-<version>.rpm 配置 进入安装目录 cd /us ...
- medoo–高效的轻量级PHP数据库操作类
网址:http://medoo.lvtao.net/ /*medoo.php*/ class medoo { protected $database_type = ‘mysql’; protected ...