一,基础知识
1,类的定义(PS)
概念:类是对具有相同属性和行为的一组对象的抽象与统一描述。是用户自定义的数据类型,包括行为和属性两个部分,属性以数据表示,行为通过函数实现。
(1)定义格式
class 类名
{
public:
公有数据成员和成员函数;
protected:
保护数据成员和成员函数;
private:
私有数据成员和成员函数;
};//注意一定不要忘记分号
(2)访问控制:
上方定义中的public,protected,private,即为访问修饰符。
它们的意义分别为:
public:公有成员,是类与外部的接口;
protected:保护成员,仅允许本类成员函数及派生类成员函数访问;
private:私有成员,仅允许本类成员函数访问。
需要注意的是:在结构体struct中,不进行声明的话,内部成员默认为公有的,及程序内任意函数都可调用其内部成员。
而在类class中,如果不进行声明,则默认为私有成员,即仅允许本类成员函数或友元中函数进行访问。
(3)成员函数
类的成员函数是实现类的行为属性的成员。一般将成员函数在类内声明为函数原型,在类外具体实现成员函数,使条理清晰。
1-成员函数在类外定义的形式
返回值类型 类名::成员函数名(参数表)
{
函数体
}
定义类时需注意:1-在类的定义中不能对数据成员进行初始化。
2-类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,
将成员函数定义为公有成员。
3-类的成员可以是其他类的对象,称为类的组合(稍后会提到)。但不能以类自身的对象作
为本类的成员。
2,对象
概念:对象是类的实例或实体。类与对象的关系,就像是数据类型(int,char,bool)和该类型的变量(变量名,例a,b,c等)之间的关系。(相当于你写了一个数据类型(类),如果你想用它,就必须通过实实在在的变量名(类的对象)来构造这样的一个数据) 。
(1)定义格式
类名 对象名1,对象名2,…,对象名n;
3,从类外访问类的成员
(1)圆点型
对象名.公有成员
(2)指针型
对象指针变量名->公有成员
4,内联函数
(1)作用:减少频繁调用小子程序的运行的时间开销。
(2)使用机制:编译器在编译时,将内联函数的调用以相应代码代替。
(3)在类外的声明形式:
inline 函数原型
使用时应注意:1-内联函数仅在函数原型作一次声明。
2-适用于只有1~ 5行的小函数。
3-不能含有复杂结构控制语句 ,不能递归调用。
5,重载成员函数
含义:函数名相同,但参数不相同(1-类型不同;2-或者个数不同)的函数。
6,构造函数与析构函数
(1)构造函数
含义: 构造函数是用于创建对象的特殊成员函数 ,当创建对象时,系统自动调用构造函数
1-作用:分配空间;对数据成员赋初值;请求其他资源。
2-默认构造函数:如果类中没有定义构造函数,系统将自动生成一个默认形式的构造函数,用于创建对象,
默认构造函数形式:
类名::类名(){} //默认构造函数是一个空函数
3-利用构造函数创建对象。
a, 其一般形式为:
类名 对象名[(实参表)];//这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数。
b, 或者通过指针和new来实现:
类名 *指针变量 = new 类名[(实参表)];
4-初始化类的成员:
A,使用构造函数的函数体进行初始化;
B,使用构造函数的初始化列表进行初始化。
ps:必须使用参数初始化列表对数据成员进行初始化的几种情况:
a,数据成员为常量;
C,初始化的顺序:
按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关。
5-构造函数重载:
6-带默认值的构造函数:
PS:定义了全部带默认值的构造函数,不能再定义无参构造函数;
只能在声明构造函数时指定默认值。
(2)析构函数
含义:析构函数是用于取消对象的成员函数,当一个对象作用域结束时,系统自动调用析构函数。
1-作用:析构函数的作用是进行对象消亡时的清理工作,释放成员(指针)所占有的存储空间。
2-类外定义格式:
类名::~类名()
{
函数语句
}
ps:析构函数没有返回值,不能有参数,也不能重载,因此在一个类中只能有一个析构函数;。
3-调用析构函数:当撤消对象时,编译系统会自动地调用析构函数。
4-默认析构函数:
含义:若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。
函数形式为:
类名::~类名(){}
PS:一般情况下,可以不定义析构函数,但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收。
5-析构函数的调用顺序: 与构造函数调用次序相反 。
7,this指针
(1)作用:用来指向不同的对象,一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。
(2)需显式引用的的三种情况:
1-在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用return *this,返回本对象的地址时,return this。
2-当参数与成员变量名相同时,如this->x = x,不能写成x = x。
3-避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。
8,复制构造函数
(1)语法形式
类名 :: 类名(const 类名 & 引用名 , …);
PS:1-复制构造函数名与类名相同,并且也没有返回值类型。
2-复制构造函数要求有一个类类型的引用参数。
(2)作用:用一个已有同类对象创建新对象进行数据初始化
(3)默认构造函数:没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。
(4)使用:
下列情况系统自动地调用:
1-声明语句中用类的一个已知对象初始化该类的另一个对象时。
1-浅复制:
含义:在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容。
ps:默认复制构造函数所进行的是简单数据复制,即浅复制 。
2-浅复制存在的问题:当被复制对象中含有指针时,则只复制了指针中的地址,而没有复制存在的空间范围,当被创建对象启用析构函数时,则其指针所指向的地址也被释放,出现错误。
3-深复制:
含义:通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。
ps:自定义复制构造函数所进行的复制是浅复制。
A,深复制的复制构造函数:
a,深复制构造函数必须显式定义。
b,深复制构造函数的特点:
类名::类名([const]类名 &对象名);
9,类的其他成员
(1)常成员
1-常数据成员
含义:常数据成员是指数据成员在实例化被初始化后,其值不能改变。(使用const说明的数据成员称为常数据成员)
PS:A,如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值。
B,不能在类外修改常数据成员。
2-常成员函数
含义:在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数。
A,说明格式
类型说明符 函数名(参数表) const;//const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
B,常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)3-常对象
含义:如果在说明对象时用const修饰,则被说明的对象为常对象。
A,常对象说明形式:
a,
类名 const 对象名[(参数表)];
b,
const 类名 对象名[(参数表)];
ps:1-在定义常对象时必须进行初始化,而且不能被更新。
2-C++不允许直接或间接更改常对象的数据成员。
3-C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
(2)静态成员
含义:类成员冠以static声明时,称为静态成员。 静态成员函数与静态数据成员协同操作。
1-静态成员函数
静态成员不属于某一个单独的对象,而是为类的所有对象所共有
A,静态成员函数的作用:不是为了对象之间的沟通,而是为了能处理静态数据成员;保证在不依赖于某个对象的情况下,访问静态数据成员。
B,定义格式:
static 返回类型 静态成员函数名(参数表);
PS:A,静态函数仅可以访问静态成员,
B,或是静态成员函数或是静态数据成员。
C,静态成员函数没有this指针,只能对静态数据操作
2-静态数据成员
静态数据成员为同类对象共享。
类型 类名::静态数据成员[=初始化值]; //必须进行声明
PS:A,不能在成员初始化列表中进行初始化
B,如果未进行初始化,则编译器自动赋初值(默认值是0)
C,初始化时不能使用访问权限
A,公有访问权限的静态成员,可以通过下面的形式进行访问
a,
类名::静态成员的名字//或类名::静态成员函数名(实参表)
b,
对象名.静态成员名字//对象. 静态成员函数名(实参表)
c,
对象指针->静态成员的名字//对象指针->静态成员函数名(实参表)
d,在静态成员函数内部,直接访问。// PS:A, 可以在建立对象之前处理静态数据成员。
(3)友元
10,类的包含
含义:类的包含是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类“抄”进来。
构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ;
(1)对象成员初始化
3-建立一个类的对象时,要先执行成员对象自己的构造函数,再执行当前类的构造函数
4-成员对象的构造函数调用次序:
和成员对象在类中的说明次序一致(声明顺序为:a1、b1、b2),与它们在成员初始化列表中出现的次序无关(初始化列表顺序为:b1、b2、a1)。
11,对象数组
含义:是指每一数组元素都是对象的数组。
定义一个一维对象数组的格式如下:
类名数组名[下标表达式];
(1)对象数组初始化:
1-当对象数组所属的类中包含带参的构造函数,可用初始化列表完成对象数组的初始化。
2-当对象数组所属的类中包含无参的构造函数,也可以先定义,再给每个数组元素赋值
3-当对象数组所属的类中包含单个参数的构造函数,可简写。
(2)成员对象数组初始化
成员对象的初始化可在构造函数初始化列表中进行,推想对于成员对象数组的初始化,也可以在本类构造函数使用初始化列表。
PS:无法通过初始化成员列表对对象数组进行初始化
二,简单应用
例:学生类
1,定义一个学生类
class student// 1,定义一个学生类
{
string name;
int no;
int score[3];
float average;
int order;
public:
student(int id,string na,int x,int y,int z):name(na),no(id)
{
score[0]=x,score[1]=y,score[2]=z;
order=-1,average=(score[0]+score[1]+score[2])/3;
}
student()
{
score[0]=score[1]=score[2]=0;
order=-1,average=0;
}
int getNo(){return no;}
void setNo(int a){no=a;}
float getAverage(){return average;}
void setAverage(int avg){average=avg;}
void setOrder(int x){order=x;}
int getOrder(){return order;}
string getName(){return name;}
void setName(string name){this->name=name;}
void setScore(int a,int b,int c){score[1]=a;score[2]=b;score[3]=c;}
void display();
};
void student::display()
{
cout<<name<<"\t"<<no<<"\t"<<score[0]<<"\t"<<score[1]<<"\t"<<score[2]<<"\t"<<average<<"\t\t"<<order<<endl;
}
2,定义一个学生类的对象me
3,访问me的成员
(1)圆点
(2)指针
int main()
{
student me(20171830,"GuoMiao",95,79,100);//定义一个学生类的对象me
me.display();//访问me的成员
//student()
cout<<"no="<<me.getNo()<<endl;
cout<<"average="<<me.getAverage()<<endl;
me.setAverage(100);
cout<<"变牛B后:\n average="<<me.getAverage()<<endl;
me.setOrder(1);
cout<<"变牛B后:\n order="<<me.getOrder()<<endl;
cout<<"问:谁是这个世界上最美的女人?\n 答:当然是"<<me.getName()<<endl;
me.setName("GuoBeauty");
cout<<"问:听说世界上最美的女人改名了?\n 答:是的呢,她改成了"<<me.getName()<<endl;
me.display();
return 0;
}
4,重载构造函数Student()
class student// 1,定义一个学生类
{
string name;
int no;
int score[3];
float average;
int order;
public:
student(int id,string na,int x,int y,int z):name(na),no(id)//参数不同,对构造函数进行了重载
{
score[0]=x,score[1]=y,score[2]=z;
order=-1,average=(score[0]+score[1]+score[2])/3;
}
student()
{
score[0]=score[1]=score[2]=0;
order=-1,average=0;
}
int getNo(){return no;}
void setNo(int a){no=a;}
float getAverage(){return average;}
void setAverage(int avg){average=avg;}
void setOrder(int x){order=x;}
int getOrder(){return order;}
string getName(){return name;}
void setName(string name){this->name=name;}
void setScore(int a,int b,int c){score[1]=a;score[2]=b;score[3]=c;}
void display();
};
void student::display()
{
cout<<name<<"\t"<<no<<"\t"<<score[0]<<"\t"<<score[1]<<"\t"<<score[2]<<"\t"<<average<<"\t\t"<<order<<endl;
}
5,定义一个student的内联函数
inline void student::newName(string mm)
{
name=mm;
}
6,定义构造函数
1-利用构造函数创建对象
2-初始化列表
3-构造函数重载
4-带默认值的构造函数
class student// 1,定义一个学生类
{
string name;
int no;
int score[3];
float average;
int order;
public:
student(int id,string na,int x,int y,int z):name(na),no(id)//初始化列表&&构造函数重载
{
score[0]=x,score[1]=y,score[2]=z;
order=-1,average=(score[0]+score[1]+score[2])/3;
}
student(int id=0,string na="haha",int x=60,int y=60,int z=60):name(na),no(id)//带默认值的构造函数
{
score[0]=x,score[1]=y,score[2]=z;
order=-1,average=(score[0]+score[1]+score[2])/3;
}
student() { score[0]=score[1]=score[2]=0; order=-1,average=0; } int getNo(){return no;} void setNo(int a){no=a;} float getAverage(){return average;} void setAverage(int avg){average=avg;} void setOrder(int x){order=x;} int getOrder(){return order;} string getName(){return name;} void setName(string name){this->name=name;} void setScore(int a,int b,int c){score[1]=a;score[2]=b;score[3]=c;} void display();};
8,析构函数
~student()
{
cout<<"I am over"<<endl;
}
三,学习感悟
通过学习c++的类,我掌握到了类的定义语法,类的各种函数与成员;也了解到了类的封装性的实现,也简单了解了面向对象的编程思想。这一课给我的感觉是有点难以理解,但是通过预习和及时复习,解决了大部分的遗留问题。最大的收获就是了解到了面向对象的思想过程。以前一直觉得这个概念非常抽象,但是学了这一章之后,在面对一个问题时,就有了大体的思路。以学生类为例,学生是一个对象,我们可以把它的特征数据统一集合起来,形成一个新的数据类型,这个叫数据类,然后通过模拟人的思维方式,去描述一个学生,去得到一个学生的信息,这就需要另一个类,操作类。这种与c语言的差异一旦理解,感觉就有点入了编程的门,但是心里还是有一些疑问的,例如,怎样实现数据实时更新,怎样在多个类中相互操作而又无差错,这些都是需要我进一步去思考和学习的问题。(也祝愿自己有一个充实的五一假期,更好更快的调整状态与作息,少年,真的不要再水了!&&一定要一直抬头看!)