<一>、知识体系梳理:
本章的主要内容是类与对象,所以首先我们应该弄清楚他俩之间的关系,简单的说,对象就是类的具体表达,类是一个抽象的概念,而对象则是具象的。
其次,也是本章主要阐述 的内容,就是①:怎么定义一个类(或者说类是怎么组成的),以及②:怎样使用定义好的类。
一、类的定义过程:
1、先声明后定义(这两者可以分开也可以不分开):class 类名 { };(一定注意这后面的分号不能省略)。
2、类的主要组成部分:
<1>数据成员:int double float char bool 类类型数据或者是数组均可以,一般来说数据成员是private类型的。
<2>成员函数:(补充:一般来说在数据类中,我们重点关注的是各种类型数据间的组合,所以对于成员函数,一般就固定那么几个;而操作类,恰恰相反。)
①、先声明后定义:在类外定义时的基本格式:函数类型 类名::函数名(形参表){ }
②、主要组成:首先,构造函数是一定要有的,而且需要重载,或者说其个数一般>=2(必有一个是为无参对象的初始化准备的),其次,对于数据类来说set(与数据成员一一对应)、get(与数据成员一一对应,一般用于间接访问私有数据成员)、display这几个函数是必须要有的,其余的根据实际问题稍作添改即可。而对于操作类来说,几乎所有的成员函数都是根据实际问题所需要的功能来的,因此,也就比较复杂了。
3、关于静态成员、(常成员、友元)的问题:
①、静态成员:emm.......之前做过小总结(点击打开static总结链接),感觉还是比较详细的,所以在这里就简单提几个易错点:
第一、静态成员一般定义为public类型,如果是private类型,注意访问时要借助成员函数间接访问。
第二、静态成员不属于具体的对象,而是同一类的所有对象所共有的数据。
第三、静态数据成员不能在类内定义(初始化),基本格式为:数据类型+类 名::静态成员名=初始化值。
最后、对于静态数据成员,我们既可以用普通成员函数访问,也可以用静态成员函数访问,而对于静态成员函数来说,却只能用于访问静待成员。
②、常成员:(点击打开常成员易错点总结链接)
注意:
第一、常数据成员只能通过初始化列表(点击->初始化列表法)的方式初始化,且之后其值不可再更新;
第二、常对象只能调用其常成员函数、静态成员、以及其构造函数。(保证常对象的数据成员不会被修改)。
第三、常成员函数不能调用非常成员函数(static等除外),但可以调用非const数据成员(当然不能有改变其值的操作)。
二、类成员的初始化/访问/操作:
补充:对于私有类型的成员,同一类的不同对象之间是可以相互访问的(比如自己写的复制构造函数),再注意是在类内,而不是主函数内(或改种类外)。
1、直接访问(public类型成员):
①、圆点型访问:对象名.类成员名,//注意:访问成员函数别忘了加括号(形参表,即使无参也要加);
②、指针型访问:”->”就相当于“.”(目前水平来看,指针一般不常用);
2、间接访问:
即借助成员函数间接访问私有数据成员(别忘了成员函数后面的括号);
3、初始化问题:
①、一把来说,对于一般数据我们通过构造函数(初始化列表、函数体赋值)来初始化。
②、较复杂的,例如对象数组,我们用如下方式初始化(对象数组不能用初始化列表赋值):
Student a[5]={1,2,3,4,5};//(该类的构造函数的形参有一个时);
Student a[5]={Student(1,1),Student(2,2),Student(3,3),Student(4,4),Student(5,5)};//(有两个形参)特别注意:Student不能省略;
三、其他几个知识点:
深复制与浅复制以及复制构造函数:具体理解的话可以看一下这段代码:
补充:
目前来看,如果需要用到的是简单数据类型的浅复制,那么用系统自带的缺省的复制构造函数就可以了,但如果类中包含数组,指针等较复杂的数据类型,那么就要用开辟动态空间的方法(new+strcpy)进行深复制。
还有,复制构造函数只能在定义对象时调用(是一种特殊的构造函数),如果想先定义对象,再复制的话,需要重载“=”;
#include<bits/stdc++.h>
using namespace std;
class Student
{
private:
int ID;
char *name;
public:
Student(const Student &x)
{
ID=x.ID;
name=x.name;//能不能理解为直接把内容存在了name本身上,而new写法name还是存地址;~A
/*name=new char[strlen(x.name)+1]; //~B
strcpy(name,x.name);*/
}
Student(int x=1,char *n="xxx")//可以思考一下为什么这里没有赋给形参一个地址;
{
ID=x;
name=n;//~A
/*name=new char[strlen(n)+1];//B
strcpy(name,n);*/
}
~Student(){
cout<<name<<endl;
cout<<"end"<<endl;
delete []name;
}
void display()
{
cout<<ID<<endl<<name<<endl<<endl;
}
};
int main()
{
Student A(1001,"wang xiaojie");
A.display();
Student B(A);
Student C;
B.display();
C.display();
return 0;
}
运行结果:
A+A or B+B(代码中的标号) 结果理论上应该正确,A+B则输出有问题。。。
另外关于复制构造函数在定义时一定要注意其参数必须使用拷贝构造函数。
<二>心得:
刚开始接触面向对象,感觉目前基础的知识点还是都比较好理解的,不过可能是由于现在做的练习还比较少,所以对这些知识的应用还不够熟练,所以只能是勤加练习吧。其次,个人认为对于目前的自己来说,在做一个系统时的主要难点不在语法上,而是在怎么去设计类上,就比如说最近要写的一个关于ATM的系统,最基本的思路肯定是先分析功能,然后考虑所需数据以及怎么操作这些数据(数据类/操作类),但麻烦的地方在于我们在实际设计某个类的过程中(无论数据类还是操作类),我们总会遇到要使用“类中类”(类的组合)的情况,这时候,我们应该怎么去“划分子问题”(或者说怎么设计和组合几个不同的类)就成了一个难点,因此说,在写程序之前我们一定要把整个系统的框架想好,思路一定要清晰,然后在实践时才有可能一气呵成,才能降低重新返工的风险。(个人认为,对于大部分的信息管理类系统来说,“套路”应该都是差不多的,因此,只要我们能熟练掌握一些基础系统程序的思路及框架,然后在遇到新的类似问题时稍加变通,应该都是没有问题的),当然,尽管在实际开发过程中有很多套路都是固定的模板,但是这种分析问题的能力对于我们来说也还是很重要的,毕竟,我们不能对所有的东西都生搬硬套。所以,无论是为了熟练应用这些固定的套路,还是说提高自己分析问题,设计程序,设计类的能力,目前来说最好的方法还是实践,即多写多练多思考。
接下来,想分析一下前几天写的一个学生成绩管理类的系统:
首先按照基本套路,我们先分出数据类以及操作类,我们先根据所需功能设计出数据类来表示单个学生的各种信息,再设计操作类来实现对学生集合的增删查改等操作。而对于操作类的主要数据成员,因为考虑到一个操作类对象操作的目标是一个学生集合,所以我们用对象数组来表示这一集合(类的组合,“类中类”)。
其次,按照数据类套路(构造函数初始化、set、get、display......)以及实际所需的数据成员写出数据类并调通(判断是否实现所需功能,需要知道怎样访问数据成员/成员函数),然后根据所需要的增删查改等功能完成操作类的定义并调通。注意在增删查改操作中,有时我们要改变某单一数据成员或者得到某一数据成员的值(如更新名次中要更新order的值、用sort对所有学生排名时要用到当前同学的具体成绩),所以这时候我们就可以看出之前数据类中定义的set、get函数的方便之处了。
最后,雪球滚完之(滚雪球法,一步一调)后再整体测试一下整个程序是否可行就好了。