关闭

C++深入体验之旅十:C++对象

标签: C++C++开发C++深入体验之旅对象封装性
1041人阅读 评论(0) 收藏 举报
分类:

1.什么是对象

C++曾被称为“带‘类’的C语言”。虽然这样的称法并不科学,但是不可否认,面向对象的程序设计是C++的一个重要特征,也是C++学习过程中的一个难点。本章先不对面向对象的概念作详细的讲述,而是以字符串和向量为例,让读者感性地了解什么是对象,什么是类,并且掌握如何使用类和对象。
既然称为面向对象(Object Oriented,简称OO),我们就先要知道什么是对象。其实单词Object更直观的翻译应该是物体。世界就是由各种物体组成的,比如某一辆汽车、某一个人、某一个杯子等等,这些都可以看作对象。
任何一个对象往往有一些具体的属性,比如某汽车的品牌、型号、排量,某人的性别、身高、体重,某杯子的口径,材质等等。任何一个对象往往能进行一些操作,比如汽车可以开动、拐弯,人可以走路、吃饭,杯子可以被打破等等。
所以,对象就是任何我们可以想象出来的具体的物体。
某些物体具有一些共性,我们可以将他们归类。比如A汽车和B汽车都是汽车,我和你都是人类,大杯子和小杯子都是杯子。我们把这种能够抽象地描述某一些具有共性的物体的词称为类(Class)。即汽车是一个类,人类是一个类,杯子也是一个类。

2.字符串--特殊对象

字符串(String)“abc”是我们可以想象出来的具体物体,它的长度为3个字符,我们可以在它的第2个字符查找到字母“b”。字符串“abcdefg”是我们可以想象出来的具体物体,它的长度为7个字符,我们可以在它的第3个字符查找到字母“cd”。由于各个字符串都具有一些属性,都能对其进行一些操作,所以,字符串是一个类。
下面我们先来看一段程序,了解如何使用字符串:(程序13.2.1)

#include <string> 
#include <iostream> 
using namespace std; 
int main() 
{ 
   string a("abc");//创建一个字符串a,内容为“abc” 
   cout <<"Pos 'b'=" <<a.find("b") <<endl;//在字符串a中查找子串“b”的位置(从0开始) 
   cout <<"Length of a=" <<a.length() <<endl;//字符串a的长度 
   cout <<a <<endl;//输出字符串a 
   string b("abcdefg");//创建一个字符串b,内容为“ancdefg”  
   cout <<"Pos \"cd\"=" <<b.find("cd") <<endl; //在字符串b中查找子串“cd”的位置 
   cout <<"Length of b=" <<b.length() <<endl; //字符串b的长度 
   cout <<b <<endl; //输出字符串b 
   return 0; 
} 
运行结果:
Pos 'b'=1 
Length of a=3 
abc 
Pos "cd"=2 
Length of b=7 
abcdefg 
程序中string是类名。a和b是对象名,它们类似于变量名,也是一种标志符。而string a("abc");的写法是对象的初始化,在后面的章节我们会作详细介绍。

奇妙的点

程序13.2.1中出现了诸如a.length()和b.find("cd")之类的形式。在第九章,我们说过结构中有这种写法,即用成员操作符“.”来代表“的”,从而能够描述一个结构变量中的成员数据(某种属性)。
而在程序13.2.1中却是在成员操作符后面出现了函数的形式,我们把这种函数称为成员函数(Function Member),有时也称为操作或方法。成员函数就是对某个对象的操作。比如a.length()就是求字符串a的长度,b.find("cd")就是在字符串b中查找子串“cd”的位置。

对字符串的操作

对某个对象的操作并不是随心所欲的,而是事先设计好的。就像汽车不能飞,杯子不能吃一样,每个对象所能进行的操作总是和它所属的“类”相关的。那么字符串除了能够求长度和查找子串位置外还能进行一些别的操作么?当我们在输入程序13.2.1的时候就已经发现了一个“秘密”,如下图所示。

我们输入了对象名和成员操作符之后,VC会给出一个下拉式列表,里面罗列了所有和该对象相关内容。字符串能进行什么操作,就看这个列表啦!下表就是其他一些常用的字符串操作:

下面我们通过程序来实践一下这些操作:(程序13.2.2)

#include <string> 
#include <iostream> 
using namespace std; 
int main() 
{ 
   string a("abc");//创建字符串a 
   string b("StringB"); 
  cout <<"Length of a=" <<a.length() <<endl;//此时a的长度为3 
   cout <<a <<endl;//字符串a的内容为“abc” 
   a.append("EFG");//在字符串末尾添加“EFG” 
   cout <<"Length of a=" <<a.length() <<endl;//此时字符串长度为6 
   cout <<a <<endl;//字符串a的内容为“abcEFG” 
   a.insert(3,b);//在字符串a的第三个字符后插入字符串b 
   cout <<a <<endl;//字符串a的内容为“abcStringBEFG” 
   cout <<a.compare("ABCDEFG") <<endl;//字符串a与“ABCDEFG”比较,不同应输出1 
   cout <<a.compare(a) <<endl;//字符串a与自己比较,相同应输出0 
   cout <<a.empty() <<endl;//字符串a不是空的,应输出0 
   a.swap(b);//字符串a和b内容交换 
   cout <<"String a is "<<a <<endl <<"String b is "<<b <<endl; 
   return 0; 
} 
运行结果:
Length of a=3 
abc 
Length of a=6 
abcEFG 
abcStringBEFG 
1 
0 
0 
String a is StringB 
String b is abcStringBEFG 
类似于标准库函数,我们不需要记住每种“类”的全部操作,只需要在使用过程中记住一些常用的操作就可以了。如果有需要的话,可以求助于相关书籍或网络。

3.面线那个对象特点:封装性

在使用字符串类的时候,我们发现它和字符数组一个很明显的不同就是,我们无法对数据进行直接的修改和操作。如果有一个char a[]="Hello";,那么我们可以直接用a[0]= 'h';来修改存储在内存中的字符,甚至我们可以输出数组的首地址来了解这个数组到底存放在什么位置。而对于一个string a("Hello");,我们却无法直接修改它的数据,因为所有对a的操作都是由成员函数所定义的。我们只能了解这个字符串的存在,但它具体存储在于内存的什么位置,我们无法通过除了对应操作以外的简单方法得知。(如使用取地址操作符)
由于我们不是字符串类的设计者,当我们对string进行种种操作时,我们只能了解到它的操作结果,而对它的操作原理和操作实现过程却无法得知。
我们把类的数据不可知性和操作实现过程不可知性称为类的封装性(Encapsulation)
不难理解,作为使用者,我们不需要对数据和操作实现过程感兴趣。就好像买一个手机,我们只关心它是否能够正常通话,正常发短消息,却对它如何接通电话,如何把信号发送出去等等不感兴趣。类的封装性把类的设计者和类的使用者分隔开,使他们在设计程序时互不干扰,责任明确。

4.从数组到向量

向量(Vector)是一个深奥的词。不过这里的向量不是数学里的向量,也不是物理里的向量。在C++中的向量,就是一个存放数据的地方,类似于一维数组和链表。

向量的性能

在第九章末尾,我们介绍了数组存储和链表存储的优缺点。数组的缺点是分配空间不灵活;链表的缺点是无法通过下标快速找到结点。然而这里介绍的向量却吸收了这两种数据结构各自的优点,综合性能较高。
向量的分配空间是会随着数据的量而变化的,如果空间不够,那么向量的空间会自动增长。类似于数组,我们也可以通过下标来访问向量中的数据元素,增快找到数据的速度。

万用的模板

在编写链表程序的时候,我们一定有这样的困惑:链表里面存储的数据类型可能是各种各样的,难道我们要为各种数据类型都写一个链表程序么?我们能不能写一个万用的链表程序呢?
在PowerPoint之类的软件中,有一种模板功能。模板提供的文档框架是基本完整的,我们只需要在一些地方填写上自己需要的内容,就是一个完整的文档。在C++中,也有这么一种模板(Template),我们只需要在使用之前填写自己需要的数据类型,就是一个完整的程序。我们把具有模板功能的类称为模板类,向量就是一个模板类。在这一节,我们只需要了解如何使用向量这个模板类。关于更多模板的知识,将在后面的章节再作介绍。
在上一节中,我们不难看出创建一个对象的方法是:
类名对象名(初始化数据);
而创建一个模板类对象的方法是:
类名<数据类型列表> 对象名(初始化数据);
即在类名之后填写数据类型,来创造一个符合自己需要的对象。

对向量的操作

同字符串一样,向量也有着自己的各种操作。下表就是向量常用的一些操作:

由于涉及迭代器(Iterator)的知识,我们无法学习向量的插入数据和删除数据操作。有兴趣的读者可以去看一下《C++ Primer》的相关章节。
下面我们用向量来解决习题9.6.2,模拟一下栈操作:(程序13.4)

#include <vector> 
#include <iostream> 
using namespace std; 
int main() 
{ 
   vector<char> stack(0);//新建一个名为stack的存放字符数据的向量,初始元素个数为0 
   char temp; 
   cout <<"请输入指令:" <<endl; 
   do 
   { 
      cin >>temp; 
      if (temp!='#') 
      { 
         if (temp!='$') 
         { 
            stack.push_back(temp);//模拟压栈操作 
         } 
        else 
         { 
            stack.pop_back();//模拟退栈操作 
         }  
      } 
   }while (temp!='#'); 
   for (int i=0;i<stack.size();i++) 
      cout <<stack[i];//可以用下标访问数据元素 
   cout <<endl; 
   return 0; 
} 
运行结果:
请输入指令: 
ABC$DEFG$$$HIJ$KLM$# 
ABDHIKL 
不难发现,用现成的向量来实现模拟栈的功能非常方便。我们不需要研究压栈和退栈的详细实现方法,而只需要知道何时操作就行了。
读到这里,可能你还没有完全明白到底什么是类,什么是对象,甚至搞不清创建对象的时候,对象名旁边的括号里面应该填什么。没关系,这些都不是本章所要掌握的内容。你只要会照猫画虎地使用字符串和向量就可以了。

 


 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:364040次
    • 积分:4490
    • 等级:
    • 排名:第6866名
    • 原创:137篇
    • 转载:32篇
    • 译文:13篇
    • 评论:29条
    个人经历
    爱编程,爱晚起,偶尔也忙到深夜; 喜欢学习,努力工作,也享受生活; 我酷爱技术,崇尚简单的快乐和幸福; 我不是码农,我是程序员; 我和你一样,为理想而奋斗.
    文章分类
    博客专栏