创建功能更强的类型

抽象的过程

*计算机的工作是建立在抽象的基础上。

-机器语言和汇编语言是对机器硬件的抽象

-高级语言是对汇编语言和机器语言的抽象

*现有抽象的问题:

-要求程序员按计算机的结构去思考,而不是按要解决的问题的结构去思考。

-当程序员要解决一个问题时,必须要在机器模型和实际要解决的问题模型之间建立联系。

-而计算机的结构本质上还是为了支持计算,当要解决一些非计算问题时,这个联系的建立是很困难的

面向对象的程序设计

*为程序员提供了创建工具的功能

*解决一个问题时

-程序员首先考虑的是需要哪些工具

-创建这些工具

-用这些工具解决问题

*工具就是所谓的对象

*现有的高级语言提供的工具都是数值计算的工具

过程化vs面向对象

以计算圆的面积和周长的问题为例

*过程化的设计方法:从功能和过程着手

-输入圆的半径或直径

-利用S=πr2和C=2πr计算面积和周长

-输出计算结果

*面向对象的程序设计方法:

-需要什么工具。如果计算机能提供给我们一个称为圆的工具,它可以以某种方式保存一个圆,告诉我们有关这个圆的一些特性,如它的半径、直径、面积和周长。

-定义一个圆类型的变量,以他提供的方式将一个圆保存在该变量中,然后让这个变量告诉我们这个圆的面积和周长是多少

面向对象的程序设计的特点

*代码重用:圆类型也可以被那些也需要处理圆的其他程序员使用

*实现隐藏:

-类的创建者创造新的工具

-类的使用者则收集已有的工具快速解决所需解决的问题

-这些工具是如何实现的,类的使用者不需要知道

*继承:在已有工具的基础上加以扩展,形成一个功能更强的工具。如在学校管理系统中,可以形成如下的继承关系

*多态性:

-当处理层次结构的类型时,程序员往往想把各个层次的对象都看成是基类成员。

-如需要对教师进行考核,不必管他是什么职称,只要向所有教师发一个考核指令。每位教师自会按照自己的类型作出相应的处理。如高级职称的教师会按高级职称的标准进行考核,初级职称的教师会按初级职称的标准进行考核。

*好处:程序代码就可以不受新增类型的影响。如增加一个院士的类型,它也是教师类的一个子类,整个程序不用修改,但功能得到了扩展。

库和类

*类是更合理的库

*例:设计一个库,提供动态整型数组服务,该数组满足两个要求:

-可以任意指定下标范围;

-下标范围可在运行时确定;

-使用下标变量时会检查下标的越界。

库的设计

*数组的保存

-数组需要一块保存数组元素的空间。这块空间需要在执行时动态分配。

-数组的下标可以由用户指定范围。因此,对每个数组还需要保存下标的上下界。

-保存这个数组的三个部分是一个有机的整体,因此可以用一个结构体把它们封装在一起。

*数组操作

-给数组分配空间

-给数组元素赋值

-取某一个数组元素的值

-由于这个数组的存储空间是动态分配的,因此,还必须有一个函数去释放空间

Array库的头文件

 1 #ifndef _array_h
 2 #define _array_h
 3
 4 //可指定下标范围的数组的存储
 5 struct IntArray
 6 { int low;
 7   int high;
 8   int *storage;
 9 };
10
11 //根据low和high为数组分配空间。分配成功,返回值为true,否则为false
12 bool initialize(IntArray &arr, int low, int high);
13 //设置数组元素的值
14 //返回值为true表示操作正常,返回值为false表示下标越界
15 bool insert(const IntArray &arr, int index, int value);
16 //取数组元素的值
17 //返回值为true表示操作正常,为false表示下标越界
18 bool fatch(const IntArray &arr, int index, int &value);
19 //回收数组空间
20 void cleanup(const IntArray &arr);
21 #endif

Array库的实现文件

 1 #include "array.h"
 2 #include <iostream>
 3 using namespace std;
 4
 5 //根据low和high为数组分配空间。
 6 //分配成功,返回值为true,否则为false
 7 bool initialize(IntArray &arr, int low, int high)
 8 {arr.low = low;
 9  arr.high = high;
10  arr.storage = new int [high - low + 1];
11  if (arr.storage == NULL) return false; else return true;
12 }
13
14 //设置数组元素的值
15 //返回值为true表示操作正常,返回值为false表示下标越界
16 bool insert(const IntArray &arr, int index, int value)
17 {if (index < arr.low || index > arr.high) return false;
18  arr.storage[index - arr.low] = value;
19  return true;
20 }
21 //取数组元素的值
22 //返回值为true表示操作正常,为false表示下标越界
23 bool fatch(const IntArray &arr, int index, int &value)
24 {if (index < arr.low || index > arr.high) return false;
25  value = arr.storage[index - arr.low] ;
26  return true;
27 }
28 //回收数组空间
29 void cleanup(const IntArray &arr)  { delete [ ] arr.storage; } 

Array库的应用

 1 #include <iostream>
 2 using namespace std;
 3 #include "array.h"
 4
 5 int main()
 6 { IntArray array; //IntArray是array库中定义的结构体类型
 7   int value, i;
 8
 9  //初始化数组array,下标范围为20到30
10   if (!initialize(array, 20, 30))
11       { cout << "空间分配失败" ; return 1;}
12 for (i=20; i<=30; ++i) {
13      cout << "请输入第" << i << "个元素:";
14      cin >> value;
15      insert(array, i, value);
16  }
17  while (true) {
18      cout << "请输入要查找的元素序号(0表示结束):";
19      cin >> i;
20      if (i == 0) break;
21      if (fatch(array, i, value)) cout << value << endl;
22          else cout << "下标越界\n";
23  }
24  cleanup(array); //回收array的空间
25  return 0;
26 }

Array库的问题

*这个数组的使用相当笨拙。每次调用和数组有关的函数时,都要传递一个结构体给它。

*我们可能在一个程序中用到很多库,每个库都可能需要做初始化和清除工作。库的设计者都可能觉得initialize和cleanup是比较合适的名字,因而都写了这两个函数。

*系统内置的数组在数组定义时就指定了元素个数,系统自动会根据元素个数为数组准备存储空间。而我们这个数组的下标范围要用initialize函数来指定,比内置数组多了一个操作。

*当数组使用结束后,还需要库的用户显式地归还空间。

*对数组元素的操作不能直接用下标变量的形式表示。

Array库的改进

*将函数放入结构体

*好处:

-函数原型中的第一个参数不再需要。编译器自然知道函数体中涉及到的low,  high和storage是同一结构体变量中的成员

-函数名冲突的问题也得到了解决。现在函数名是从属于某一结构体,从属于不同结构体的同名函数是不会冲突的。

改进后的Array库的头文件

 1 #ifndef _array_h
 2 #define _array_h
 3 struct IntArray
 4 {
 5   int low;
 6   int high;
 7   int *storage;
 8   bool initialize(int lh, int rh);
 9   bool insert(int index, int value);
10   bool fatch(int index, int &value);
11   void cleanup();
12 };
13 #endif

改进后的Array库的实现文件

与原来的实现有一个变化:函数名前要加限定

1 bool IntArray::initialize(int lh, int rh)
2 {low = lh;
3  high = rh;
4  storage = new int [high - low + 1];
5  if (storage == NULL) return false; else return true;
6 } 

改进后的Array库的应用

函数的调用方法不同。就如引用结构体的成员一样,要用点运算符引用这些函数

 1 int main()
 2 {IntArray array;
 3  int value, i;
 4  if (!array.initialize(20, 30)) { cout << "空间分配失败" ; return 1;}
 5  for (i=20; i<=30; ++i) {
 6      cout << "请输入第" << i << "个元素:";     cin >> value;
 7      array.insert(i, value);
 8  }
 9  while (true) {
10      cout << "请输入要查找的元素序号(0表示结束):";
11      cin >> i;
12      if (i == 0) break;
13      if (array.fatch(i, value)) cout << value << endl;
14           else cout << "下标越界\n";
15  }
16  array.cleanup();
17  return 0;
18 }

将函数放入结构体的意义

*将函数放入结构体是从C到C++的根本改变

*在C中,结构体只是将一组相关的数据捆绑了起来,它除了使程序逻辑更加清晰之外,对解决问题没有任何帮助。

*将处理这组数据的函数也加入到结构体中,结构体就有了全新的功能。它既能描述属性,也能描述对属性的操作。事实上,它就成为了和内置类型一样的一种全新的数据类型。

*为了表示这是一种全新的概念,C++用了一个新的名称 — 类来表示。

类的接口和实现分开

*与库设计一样,类的定义写在接口文件中,成员函数的实现写在实现文件中。

*某些简单的成员函数的定义可以直接写在类定义中。

*在类定义中定义的成员函数被认为是内联函数。

this指针

*通常,在写成员函数时可以省略this,编译时会自动加上它们。

*如果在成员函数中要把对象作为整体来访问时,必须显式地使用this指针。这种情况常出现在函数中返回一个对调用函数的对象的引用

动态对象的初始化

*动态变量的初始化是在类型后面用一个圆括号指出它的实际参数表

*如果要为一个动态的IntArray数组指定下标范围为20到30,可用下列语句:

p = new IntArray(20, 30);

*括号中的实际参数要和构造函数的形式参数表相对应。

为什么要使用初始化列表

我们完全可以在函数体内对数据成员赋初值!!!

*事实上,不管构造函数中有没有构造函数初始化列表,在执行构造函数体之前,都要先调用每个数据成员对应的类型的构造函数初始化每个数据成员。

*在构造函数初始化列表中没有提到的数据成员,系统会用该数据成员对应类型的默认构造函数对其初始化。

*显然利用初始化列表可以提高构造函数的效率。在初始化数据成员的同时完成了赋初始值的工作。

必须使用初始化的情况

*数据成员不是普通的内置类型,而是某一个类的对象,可能无法直接用赋值语句在构造函数体中为它赋初值

*类包含了一个常量的数据成员,常量只能在定义时对它初始化,而不能对它赋值。因此也必须放在初始化列表中。

重载构造函数

*构造函数可以重载,导致对象可以有多种方式构造

*试设计一个时间转换器,用户可输入秒、分秒或时分秒输出相应的秒数。

时间转换器的实现

 1 #include <iostream>
 2 using namespace std;
 3 class timer{  int second;
 4   public:
 5     timer(int t)    {second=t;}
 6     timer(int min, int sec)  {second=60*min+sec;}
 7     timer(int h, int min, int sec)  {second=sec+60*min+3600*h;}
 8     int gettime()    {return second;}
 9 }
10 main()
11 {timer a(20),b(1,20),c(1,1,10);
12  cout<<a.gettime()<<endl;
13  cout<<b.gettime()<<endl;
14  cout<<c.gettime()<<endl;
15 }

析构函数不能重载,没有参数,没有返回值

缺省的拷贝构造函数

*如果用户没有定义拷贝构造函数,系统会定义一个缺省的拷贝构造函数。该函数将已存在的对象原式原样地复制给新成员

何时需要自定义拷贝构造函数

*一般情况下,默认的拷贝构造函数足以满足要求。

*但某些情况下可能需要设计自己的拷贝构造函数。

*例如,我们希望对IntArray类增加一个功能,能够定义一个和另一个数组完全一样的数组。但默认的拷贝构造函数却不能胜任。如果正在构造的对象为arr1,作为参数的对象是arr2,调用默认的拷贝构造函数相当于执行下列操作:

arr1.low = arr2.low;

arr1.high = arr2.high;

arr1.storage = arr2.storage;

前两个操作没有问题,第三个操作中,storage是一个指针,第三个操作意味着使arr1的storage指针和arr2的storage指针指向同一块空间。

对象定义时

*拷贝构造函数用于对象构造时有两种用法:直接初始化和拷贝初始化。

*直接初始化将初始值放在圆括号中,直接调用与实参类型相匹配的拷贝构造函数。如

IntArray array2(array1);

*拷贝初始化是用“=”符号。当使用拷贝初始化时,首先会用“=”右边的表达式构造一个临时对象,再调用拷贝构造函数将临时对象复制到正在构造的对象。如

IntArray array =  IntArray(20, 30);

IntArray array1=array2;

自定义拷贝构造函数举例

 1 class point{ int x, y;
 2  public: point(int a, int b){x=a; y=b;}
 3          point(const point &p)    {x=2*p.x; y=2*p.y;}
 4          void print()   {cout<<x<<"  "<<y<<endl;}
 5 };
 6 void main()
 7 {point p1(10, 20), p2(p1), p3 = p1, p4(1, 2);
 8  p1.print();  p2.print();  p3.print();  p4.print();
 9  p4 = p1;  p4.print(); //这个p4=p1并没有调用拷贝构造函数,而是调用的赋值函数
10  }

const对象

*const对象的定义

const MyClass obj(参数表);

*const对象不能被赋值,只能初始化,而且一定要初始化,否则无法设置它的值。

*C++规定:对const对象只能调用const成员函数

const函数

*任何不修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其他非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。

class A {   int x;

public:

A(int i) {x=i;}

int getx() const

{return x;}

};

常量成员

*const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。同一类的不同的对象其const数据成员的值可以不同。

*常量成员的声明

在该成员声明前加上const。如

class  abc {

const  int  x;

};

*不能在类声明中初始化const数据成员。

class A

{

//错误,企图在类声明中初始化const数据成员

const int SIZE = 200;

int array[SIZE];  //错误,未知的SIZE

};

const数据成员的初始化

*const数据成员的初始化只能在类构造函数的初始化表中进行,不能在构造函数中对它赋值。

*例:

class A

{

A(int size);  //构造函数

const int SIZE;

}

A::A(int size) : SIZE(size)  //构造函数的初始化表

{…}

A a(100);  //对象a的SIZE的值为100

A b(200);  //对象b的SIZE的值为200

静态数据成员

*静态数据成员不属于对象的一部分,而是类的一部分;

*静态数据成员的初始化不能放在类的构造函数中;

*类定义并不分配空间,空间是在定义对象时分配

*但静态数据成员属于类,因此定义对象时并不为静态成员分配空间

静态数据成员的定义

*为静态成员分配空间称为静态成员的定义

*静态成员的定义一般出现在类的实现文件中。如在SavingAccount类的实现文件中,必须要如下的定义:

double SavingAccount::rate = 0.05;

该定义为rate分配了空间,并给它赋了一个初值0.05。

*如果没有这个定义,连接器会报告一个错误

静态数据成员的使用

*可以通过作用域操作符从类直接调用。如: SavingAccount::rate

*但从每个对象的角度来看,它似乎又是对象的一部分,因此又可以从对象引用它。如有个SavingAccount类的对象obj,则可以用:obj.rate

*由于是整个类共享的,因此不管用哪种调用方式,得到的值都是相同的

静态成员函数

*成员函数也可以是静态的。静态的成员函数是为类的全体对象服务,而不是为某个类的特殊对象服务

*由于静态成员函数不需要借助任何对象就可以被调用,所以编译器不会为它暗加一个this指针。因此,静态成员函数无法处理类中的非静态成员变量。

*静态成员函数的声明只需要在类定义中的函数原型前加上保留词static。

静态成员函数的用途

*定义静态成员函数的主要目的是访问静态的数据成员。

*如在SavingAccount类中,当利率发生变化时,必须修改这个静态数据成员的值。为此可以设置一个静态的成员函数

static void SetRate(double newRate) {rate = newRate;}

静态成员函数使用说明

*静态成员函数可定义为内嵌的,也可在类外定义。在类外定义时,不用static。

*静态成员函数的访问:可以通过类作用域限定符或通过对象访问

类名::静态成员函数名()

对象名.静态成员函数名()

静态的常量数据成员

*静态的常量数据成员:整个类的所有对象的共享常量

*静态的常量数据成员的声明:

static const  类型  数据成员名 = 常量表达式;

*注意const数据成员和static const数据成员的区别。

老版本的兼容

*某些旧版本的C++不支持静态常量

*解决方法:用不带实例的枚举类型

*如:

class sample {

enum {SIZE = 10};

int storage[SIZE];

};

时间: 2024-08-06 07:59:19

创建功能更强的类型的相关文章

Poco vs Boost(Poco也有不少优点,特别是网络功能更强)

POCO的优点: 1) 比boost更好的线程库,特别是一个活动的方法的实现,并且还可设置线程的优先级. 2) 比 boost:asio更全面的网络库.但是boost:asio也是一个非常好的网络库. 3) 包含了一些Boost所不包含的功能,像XML,数据库接口等. 4) 跟Boost相比,集成度更高,是更加统一的一个库. 5) Poco的c++代码更清洁,现代和易理解.对不是模板编程专家的人来说,POCO的代码比大多数Boost库容易理解得多 6) 可以在许多平台上使用 POCO的缺点: 1

最新合购网源码net.asp程序 彩票合买功能采用全新内核、全新架构,更小巧、功能更强、更快、更安全稳定

合买代购功能 可购彩种:福彩3D.排列3.重庆时时彩.天津时时彩.广东11选5.11运夺金.江苏快3.广西快3.拥有上百种玩法,更多彩种即将开发完成,更多的彩种不断开发更新中... 选号投注:建立追号任务:会员在提交代购方案时,可以选择是否追号.如果选择追号,系统就会为会员建立一个追号任务,这样,在会员指定要追号的期数里,会员不需要登录合买大厅进行操作,系统将自动为会员购买同一代购方案,直到方案中奖,或者会员中途中止此追号任务:追号任务支持多样化的方案类型,包括单式方案(即上传文本文件).机选号

【C语言探索之旅】 第二部分第六课:创建你自己的变量类型

0 内容简介 1.课程大纲 2.第二部分第六课: 创建你自己的变量类型 3.第二部分第七课预告: 文件读写 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三个游戏. C语言编程基础知识 什么是编程? 工欲善其事,必先利其器 你的第一个程序 变量的世界 运算那点事 条件表达式 循环语句 实战:第一个C语言小游戏 函数 练习题 习作:完善第一个C语言小游戏 C语言高级技术 模块化编程 进击的指针,C语言王牌 数组 字符串 预处理 创建你自己的变量

利用pyinotify监控文件内容,像tailf命令但比它更强

Linux的tail/tailf命令使用了内核提供的inotify功能,下面的Python例子也使用inotify实现比tail/tailf更强的监控文件功能. watchfile.py #!/usr/bin/python import sys, os, pyinotify notifier = None monfile = None lastsize = 0 wm = None wd = 0 def roll_file(filename): global lastsize fd = os.op

第6课 类型别名和强枚举类型

一. typedef和using关键字 (一)两者的差异 ①C++11引入using关键字,覆盖了typedef的全部功能.它既可以用来定义类型的别名,也可以定义模板的别名.而typedef可以定义类型的别名,但不能用来重定义模板的别名. ②使用using不用写“::type”的后缀.在模板内,对于内嵌typedef的引用经常要加上typename前缀. ③using采用类似于赋值的方式,从语法比typedef更加清晰. (二)using在模板中的优势 ①using可以直接为模板取别名(alia

告别手写接口文档时代,比Swagger功能更强大的LKADocument接口文档管理框架诞生了!

更详细的更全面的教程请观看作者亲自录制的视频教程,地址: https://edu.51cto.com/sd/9cb7fLKADocument视频教程 一.介绍 在前后端分离,分工更加明细化的今天,为了减少前端和后台开发人员的沟通成本,能够让他们做到并行开发,同时也能保证前端和后端开发人员所看到的接口文档的一致性,即时性,以此来大大提高工作效率.所以出现了一些非常优秀的接口管理工具,具有代表性的像Swagger,因为它能够通过注解或yml和JSON描述文件来自动生成接口文档.但是我觉得它不管是在配

功能更强大的格式化工具类 FormatUtils.java

package com.util; import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * 功能更强大的格式化工具类 */ public class FormatUtils { private static SimpleDateFormat second = new SimpleDateForma

做数据分析,Python和R究竟哪个更强?

作者: Enoch Kan编译: Mika本文为 CDA 数据分析师原创作品,转载需授权 几十年来,研究人员和开发人员一直在争论,进行数据科学和数据分析,Python和R语言哪种才是更好的选择.近年来,数据科学在生物技术.金融和社交媒体等多个行业迅速发展.数据科学的重要性不仅得到了业内人士的认可,而且还得到了许多学术机构的认可,目前越来越多的学校都开始设立数据科学学位. 随着开源技术的迅速取代了传统的闭源技术,Python和R语言在数据科学中变得越来越受欢迎. 数据科学就业增长图--Indeed

一对一视频直播的功能更倾向于“直播+社交”

直播的爆发让行业看到了希望,各行各业都借着直播的热度,一路高歌猛进,现在一对一直播平台星期,一对一直播是在传统直播形式上的一大升级,是更加精细化的发展.从直播的兴起,再到一对一直播的落地,你如果没有在直播的浪潮中享受到红利,就不用改在错过一对一直播的风口. "得流量者得天下"这是移动互联网时代所奉行的宗旨,直播线视频.社交软件.短视频等娱乐应用软件在多个第三方数据统计机构的流量研究报告中都是位列前几位的类型. 2018年真的是在线视频社交大年,虽然头部势力依然稳固,但是用户疲劳带来的暗