目录
面向对象编程,即面对客观世界存在的对象进行编程,是一种必备的理解思路。
面向对象分为三大特点:封装、继承、多态。不说这些生涩的概念了,直接步入正题。
C++类
类的定义
类,顾名思义,我们平时所了解的类就是指具有某种共同属性的一些事物的集合体。
那么,类具体又该怎样定义呢?
class<类名>
{
public:
<成员函数或数据成员的说明> //公有成员、外部接口
protected:
<数据成员或成员函数的说明> //保护成员
private:
<数据成员或成员函数的说明> //私有成员
}; //一定不要忘记在类体外加上分号
//类的实现部分
<各成员函数的实现>
注意:
- 上述语法中,class是声明的类的关键词。分号表明类的声明结束。
- 类的成员包括数据成员和成员函数,分别描述类所表达的问题的属性和行为。
- 在类的定义中不能对数据成员进行初始化
- 类的成员可以是其它类的对象,成为类的组合,但是不能以该类本身的对象作为本类的成员。
访问权限
关键词public、protected、private是访问权限修饰符。它们限制了类成员的控制范围。
public | 公有成员 (外部接口,类外可以直接调用) |
protected | 保护成员(仅允许本类成员函数及派生类成员函数访问) |
private | 私有成员(仅允许本类函数使用) |
几点注意事项
- 在类中,不写访问权限的默认为私有成员,而在结构体中,默认为公有成员(结构体为特殊的类,所以在结构体中同样可以写构造函数等函数)
- 一般情况下,函数都写为公有成员,数据变量写成私有成员。
- 假如要更改类中的私有成员,定义一个函数,通过函数进行写入和输出操作
class Date
{
public:
Date(){}
Date(int y,int m,int d);
void SetDate(int y,int m,int d); //先写上一个函数原型,预先声明一下需要使用的函数
private:
int year,month,day;
}; //类的定义结束
Date::Date(int y,int m,int d)
{ year=y;
month=m;
day=d;
}
void Date::SetDate(int y,int m,int d)
{ year=y; //详细编写该函数的函数体部分,注意要声明作用域
month=m;
day=d;
}
此处定义了一个公有函数SetDate,由于在类内写的是函数原型,故在类外就应该写作用域运算符::,表示该函数的原型位置在Date类里面。
作用域运算符的内容如下
函数返回类型 类名::函数名(参数)
{函数体}
C++对象
对象的概念
对象,实际为类的一个具体表现。举个例子:假如说动物是一个类,那么猴子就可以成为一个对象,就是代表具有类的特征的一个实体。
对象的定义
类名 对象名; 即为定义一个对象的格式。
举一个具体例子如下:
class a
{
public:
private:
};
int main()
{ a s; // 定义对象的格式为: 类名 对象名; 此处定义了类a的一个s对象
return 0;
}
对象的访问
对象的访问形式共有两种:
圆点访问形式:
对象名.公有成员
指针访问形式:
对象指针变量名->公有成员
构造函数
构造函数起到对数据成员进行初始化的功能,由系统自动调用,用户不能自己调用。
那么什么叫初始化呢,用较为通俗的话来讲,就是给数据成员赋初值(类似于数组初始化)
构造函数的特点
- 构造函数名必须与类名保持一致
- 构造函数没有返回值
- 一般情况下构造函数中只进行初始化操作,不会进行其他操作。(其他操作一般只写在其他的函数中)
- 构造函数可以有一个或者多个参数,因此构造函数可以被重载。
- 只能在声明构造函数时让其带有默认值(Box(int h=10,int w=10 , int l=10);)
构造函数实例
class a
{
public:
a(int s,int g,int d); //
a(int s){a=s;} //上下两个构造函数进行了函数重载
private:
int a,b,c;
};
a::a(int s, int g, int d)
{a=s;
g=b;
d=c;
}
set/get函数
平时自己写的set函数(也就是给数据成员赋初值的函数),就是起到构造函数功能的函数,但是可以任由自己调用,而不是只能由系统自己调用。
void setdate(int y,int m,int d)
{
year=y;
month=m;
day=d;
} //此处的year,month,day就是类中的数据成员
get函数,就是一般用来返回函数的值(一般根据这个函数作为媒介,获取类中的数据成员信息,进行下一步操作)
int gety()
{
return year;
}
//year是类中的数据成员
利用构造函数创建对象
法一:类名 对象名(实参表)
#include<iostream>
using namespace std;
class Date
{
public:
Date(){}
Date(int a,int b,int c){year=a;month=b;day=c;}
void sety(int a){year=a;}
void setm(int b){month=b;}
void setd(int c){day=c;}
int gety(){return year;}
int getm(){return month;}
int getd(){return day;}
void getdate(){cout<<year<<"."<<month<<"."<<day;}
~Date(){}
private:
int year;
int month;
int day;
};
int main()
{
Date a(2022,12,1); //类名 对象名(实参表)
a.getdate();
return 0;
}
法二:通过指针和new,借助构造函数创建对象
类名 *指针变量 = new 类名[(实参表)]; (不是很常用)
用指针访问对象内容 指针名->函数名(参数表)
#include<iostream>
using namespace std;
class Date
{
public:
Date(){}
Date(int a,int b,int c){year=a;month=b;day=c;}
void sety(int a){year=a;}
void setm(int b){month=b;}
void setd(int c){day=c;}
int gety(){return year;}
int getm(){return month;}
int getd(){return day;}
void getdate(){cout<<year<<"."<<month<<"."<<day;}
~Date(){}
private:
int year;
int month;
int day;
};
int main()
{Date *p;
p=new Date(2021,4,15); //可以合写成Date *p=new Date(2021,4,15);
p->getdate(); //通过指针访问内容
delete p; //删除对象,此处会调用析构函数
return 0;
}
构造函数的初始化列表
函数名(参数列表):初始化列表
{ 函数体,可以是空函数体 }
初始化列表的形式:
成员名1(形参名1),成员名2(形参名2),成员名n(形参名n)
实例
class Date
{
public:
Date(){}
Date(int a,int b,int c):year(a),month(b),day(c){ } //初始化列表
void sety(int a){year=a;}
void setm(int b){month=b;}
void setd(int c){day=c;}
int gety(){return year;}
int getm(){return month;}
int getd(){return day;}
void getdate(){cout<<year<<"."<<month<<"."<<day;}
~Date(){}
private:
int year;
int month;
int day;
};
必须使用参数初始化列表对数据成员进行初始化
- 数据成员为常量
- 数据成员为引用类型
- 数据成员为没有无参构造函数的类的对象
类成员的初始化的顺序
按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关
析构函数
析构函数的概念
进行了一些操作后,为了不让很多已经不再使用的内存继续被原有的作废数据占用,这里就要引入析构函数的概念。他是在运行全部操作后,释放内存(对象)用的函数,一般情况下,假如你不写该函数,会存在一个默认的(缺省的构造函数),系统会自动给你补加一个构造函数,帮助你释放内存。
默认构造函数格式
类名::类名(){}
析构函数的特点
- 析构函数名必须与类名保持一致
- 析构函数在函数名之前加上~
- 析构函数与构造函数的功能正好相反
- 一个类只能定义一个析构函数
- 析构函数不能被重载
析构函数的格式(例子)
写法一:
class a
{
public:
~a(){}
};
写法二:
class a
{
public:
~a();
};
a::~a()
{
delete[] 数据成员名;
}
析构函数的几点注意事项
- 一般情况下,可以不定义析构函数
- 但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收