知识点
类的概述:类是 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()
{
……
}
创建对象:对象是根据类来创建的,声明类的对象,就像声明基本类型的变量一样。
对象的引用:声明一个对象的引用方法是: 类名 &对象名 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 返回值类型 类名::函数名(参数表);
友元的利与弊:友元使设计程序方便了很多。原先的那些私有成员都能轻松地被访问了。于是不用去写那些繁琐的成员函数,程序执行的时候也减少了函数的调用次数,提高了运行效率。友元的存在,破坏了类的封装性。一个类出现问题,就不仅仅是由这个类本身负责了,还可能和它众多的友元有关。
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++能系统的、结构更清晰的去设计一个程序,可以让一个程序模块化,每一个功能都分配给不同的类去完成,这样哪里出问题了就去找相应的类去调试,避免了大面积出错,节约了调试时间。
类的知识点是抽象的,但是仔细琢磨后,就会发现,这其实和我们刚开始学习基本的数据类型是一样的,并没有很费脑的地方,说的通俗一点就是学会定义类的基本语法,掌握“套路”,去“套模板”,只要是细心一点,把每个功能都逐一调试,就不会大面积的出错。
在使用类设计程序时,要提前设计好方案,明确哪一个部分实现什么功能,需要什么数据,分别设计数据类和操作类,恰当的把数据私有化,在有了一个整体的思路之后,在去写代码,切记不能把全部的代码写完再去调试,这样出错以后会很难改正,而且非常浪费时间,应当做到写一个功能就去调试一个功能。