C++类的知识点与应用、学习心得总结
类的知识点
类的概述:类是 C++ 的核心特性,在C++中,除了已有的数据类型(整型,字符型)外,还可以自定义数据类型,这种类型通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法(数据类)和用于处理数据(操作类)的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成员。
类的定义:类定义是以关键字 class 开头,后跟类的名称。类的主体是包含在一对花括号中。类定义后必须跟着一个分号或一个声明列表。例如,我们使用关键字 class 定义 Box 数据类型,如下所示:
class Box
{
public://公有成员 在类外可以访问
double length; // 盒子的长度
double breadth; // 盒子的宽度
double height; // 盒子的高度
};
注:
① 如果类的定义和主函数在同一个源文件里,那么就会可能遇到这样的问题:在类定义之 前,主函数使用了这个类。这将会导致错误的发生,因为主函数还没有意识到这个类的存在。 所以必须在主函数之前声明这个类的存在。
② 还可以在头文件中定义类,然后在.Cpp源文件中包含这个头文件,由于包含头文件在主函数之前,所以不用在主函数中声明这个类。
Student.h //头文件
class Student //学生类的定义
{
……
};
main.cpp //文件
#include "Student.h" //要注意这里必须用双引号,而不能用尖括号
int main()
{
……
}
创建对象:对象是根据类来创建的,声明类的对象,就像声明基本类型的变量一样。
Box B; // 声明对象B,类型为Box型
对象的引用:
声明一个对象的引用方法是: 类名 &对象名 a=对象名 b;
对对象 a 的访问和操作就如同对对象 b 的访问和操作一样,对象 a 只是对象 b的一个“绰号”。
string A; //声明一个字符串对象
string &B=A; //声明一个引用
B.append("ABC"); //效果与 A.append("ABC")相同
对象指针:所谓对象指针,就是指向对象的指针
string A; //声明一个字符串对象
string *B=&A; //声明一个对象指针
B->append("ABC"); //效果与 A.append("ABC")相同
【箭头操作符->可以访问该指针所指向的对象的成员数据或成员函数】
访问数据成员:
在访问数据成员时,可以用访问运算符 . 操作 ,私有的【Private】成员和受保护的【 Protected】成员不能使用直接成员访问运算符 . 来直接访问。
#include <iostream>
using namespace std;
class Box //定义Box类
{
public://公有成员
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
int main( )
{
Box Box1; // 声明 Box1 对象,类型为 Box
double volume = 0.0; // 用于存储体积
// box 1 详述
Box1.height = 10.0;
Box1.length = 10.0;
Box1.breadth = 10.0;
// box 1 的体积
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Box1 的体积:" << volume <<endl;
return 0;
}
注:同一个类的成员数据(或成员函数)在其成员函数中可以直接使用。在这个类外使用一个对象的公有成员数据时,要写作“对象名.成员数据”,但是在成员函数中不需要也不能那样写。
常成员函数:
在成员函数后加上const,使用常成员函数,就保证了成员数据的安全。在此函数中任何修改成员数据的语句将被编译器拒之门外。
int readNo() const; //读取学号函数 只能读取不能修改数据
成员函数的重载:
和普通函数类似,在一个类中也可以有成员函数的重载,但是任意两个同名函数参数表中的参数个数、各参数的数据类型和顺序不能完全一样。
void set(int No,string Name); //设置学号姓名
void set(); // 重置学号姓名
构造函数:
构造函数是一种随着对象创建而自动被调用的函数,它的主要用途是为对象作初始化。在 C++中,规定与类同名的成员函数就是构造函数。构造函数应该是一个公有的成员函数,并且所有的构造函数都没有返回值类型。
#include<bits/stdc++.h>
using namespace std;
class Student//学生数据类
{
private:
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;
}
};
拷贝构造函数:
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
void display(){ //打印对象的长宽高
cout<<length<<endl<<breadth<<endl<<height<<endl;
cout<<endl;
}
Box(double l,double b,double h){//普通含参构造函数
length =l;
breadth=b;
height=h;
}
Box(Box &x){//自定义拷贝构造函数 复制一个现有的对象的数据
length=x.length;
breadth=x.breadth;
height=x.height;
}
};
int main( )
{
Box Box1(1,2,3); // 声明 Box1,类型为 Box 使用普通构造函数初始化
Box Box2(Box1); // 声明 Box2,类型为 Box 使用拷贝构造函数初始化 复制Box1的数据内容
Box1.display();//输出Box1内容
Box2.display();//输出Box2内容
return 0;
}
注意:
(1)拷贝构造函数可以读出相同类对象的私有成员数据。
(2)拷贝构造函数的实质是把参数的成员数据一一复制到新的对象中。
(3)拷贝构造函数也是构造函数的一种重载。
(4)在用户没有定义拷贝构造函数时,系统自动生成默认拷贝构造函数,默认拷贝构造函数能够满足大多数的需要,所以一般 不需要用户自定义拷贝构造函数。
(5)使用默认拷贝构造函数,则得到的对象存储空间地址将会与原对象一致。在编写深拷贝构造函数时,为新的对象也申请堆 内存空间,并把原对象堆内存的数据复制过来了。
析构函数:
析构函数是一种随着对象消亡而自动被调用的函数,它的主要用途是释放动态申请的资源或备份将消亡对象的重要数据。析构函数的函数名也是指定的,是在类名之前加一个“~”符号,析构函数没有返回值类型,没有参数,更没有重载。析构函数一般书写在主函数的末尾:
class Student
{
~Student(); //析构函数的声明
};
Student::~Student(){ //析构函数的定义
}
int main( )
{
return 0; //在这里调用析构函数
}
静态成员数据、成员函数:【成员数据不能在构造函数中被初始化】
//静态成员数据的声明
static 数据类型 成员变量名;
//静态成员数据的初始化语句为
数据类型类名::静态成员数据=初始值;
//静态成员函数的声明方法为 【在定义静态成员函数时的格式与普通成员函数一样,不必出现 static】
static 返回值类型 函数名(参数表);
//两种调用静态成员函数的方法 【静态成员函数不能访问非静态成员数据】
类名::静态成员函数名(参数表)
对象名.静态成员函数名(参数表)
友元类:【友元类就是指某个类的所有成员函数都能访问另一个类的私有成员】 //声明友元类的格式为:
friend class 类名;
友元函数:
//声明友元函数的格式为:
friend 返回值类型 函数名(参数表);
//如果该函数是某个类的成员函数,则格式为:
friend 返回值类型 类名::函数名(参数表);
友元的利与弊:友元使设计程序方便了很多。原先的那些私有成员都能轻松地被访问了。于是不用去写那些繁琐的成员函数,程序执行的时候也减少了函数的调用次数,提高了运行效率。友元的存在,破坏了类的封装性。一个类出现问题,就不仅仅是由这个类本身负责了,还可能和它众多的友元有关。
内联函数:
如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符。在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符。
引入内联函数的目的是为了解决程序中函数调用的效率问题,程序在编译器编译的时候,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体进行替换,而对于其他的函数,都是在运行时候才被替代。这其实就是个空间代价换时间的节省。所以内联函数一般都是1-5行的小函数。在使用内联函数时要留神:
- 1.在内联函数内不允许使用循环语句和开关语句;
- 2.内联函数的定义必须出现在内联函数第一次调用之前;
- 3.类结构中所在的类说明内部定义的函数是内联函数。
this指针:
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。this指针是一个常指针,this指针一般初始化(成员函数被调用后),获取了对象的地址,指针值就不能在修改和赋值,以保证不会指向其他对象。友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。
指向类的指针:【在使用指针之前,需要对指针进行初始化】
#include <iostream>
using namespace std;
class Box
{
public:
Box(double l, double b, double h) // 构造函数
{
length = l;
breadth = b;
height = h;
}
double Volume() // Box体积
{
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
Box *ptrBox; // Declare pointer to a class.
// 保存第一个对象的地址
ptrBox = &Box1;
// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
// 保存第二个对象的地址
ptrBox = &Box2;
// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
return 0;
}
类的学习心得
在学习C++类的过程中,我体会到了类面向对象的方便之处,与面向过程的C语言不同的是,C++能系统的、结构更清晰的去设计一个程序,可以让一个程序模块化,每一个功能都分配给不同的类去完成,这样哪里出问题了就去找相应的类去调试,避免了大面积出错,节约了调试时间。
类的知识点是抽象的,但是仔细琢磨后,就会发现,这其实和我们刚开始学习基本的数据类型是一样的,并没有很费脑的地方,说的通俗一点就是学会定义类的基本语法,掌握“套路”,去“套模板”,只要是细心一点,把每个功能都逐一调试,就不会大面积的出错。
在使用类设计程序时,要提前设计好方案,明确哪一个部分实现什么功能,需要什么数据,分别设计数据类和操作类,恰当的把数据私有化,在有了一个整体的思路之后,在去写代码,切记不能把全部的代码写完再去调试,这样出错以后会很难改正,而且非常浪费时间,应当做到写一个功能就去调试一个功能。