类的引入
在C语言中,结构体的作用是把一些具有不同类型的变量组合在一起。
struct Student
{
char _name[20];
char _gender[3];
int _age;
}
而在C++中,结构体内不止可以定义变量,还可以定义函数。
struct Student
{
char _name[20];
char _gender[3];
int _age;
void SetStudentInfo(const char* name, const char* gender, int age){
strcpy(_name, name);
strcpy(_gerder, gender);
_age=age;
}
};
int main(){
Student s;
s.SetStudentInfo('Bob', 'male', 18);
return 0;
}
此时的结果提就可以被称为类,但在C++中,更喜欢使用class代替struct。
C++类的定义
class className
{
//成员函数
//成员变量
};//注意分号
类是属性和方法的合集,属性即使类中的数据,方法就是调用这些数据进行操作的函数。
1. 声明和定义全部放在类体中。此时,成员函数在类中定义,编译器会将其当作内联函数处理。
内联函数:inline,当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。这样做的好处是省去了调用的过程,加快程序运行速度。
缺点:由于每当代码调用到内联函数,就需要在调用处直接插入一段该函数的代码,所以程序的体积将增大。拿生活现象比喻,就像电视坏了,通过电话找修理工来,你会嫌慢,于是干脆在家里养了一个修理工。这样当然是快了,不过,修理工住在你家可就要占地儿了。内联函数并不是必须的,它只是为了提高速度而进行的一种修饰。
class Person//一个类
{
public://成员函数
void showinfo()//方法:显示信息
{
std::cout<<_name<<std::endl;
std::cout<<_sex<<std::endl;
std::cout<<_age<<std::endl;
}
public://成员变量
char* _name;
char* _sex;
int _age;
};
2. 定义和声明分开放是项目中常用的。
类的访问限定符及封装
类的定义中,有public
、private
、protected
来修饰成员,其中private
、protected
修饰的成员不可再类的外面直接访问。
面向对象的三大特性:封装、继承、多态。
封装本质是一种管理手段,将属性(数据)和方法(接口)有机结合起来,再隐藏他们,只对外公开接口(函数)来和对象进行交互,不想给别人看的,使用protected/private进行修饰,而开放一些公有函数对成员进行合理访问。合理访问还可以理解为阅读,不合理访问可以理解为改写(不准确)。
此时可以解释为什么C语言不支持大型项目的编写,因为C语言的管理能力太弱了,不是函数就是数据,不支持面向对象。且struct默认是public的,安全性较低,模块与模块的独立性较差,因此不适合编写大型项目。
之前学C语言的时候,学过好多数据结构,比如栈,一般都是新建一个工程然后分别建立它的声明和定义等文件,此时数据和方法是分离的,是自由的。但如果再C++中,按照面向对象的考虑,一个栈就可以作为一个对象,这个对象有它的数据(动态增长的数组、长度、容量),还有方法(栈的初始化、进栈、出栈等等)。像这样的数据,应该就是私有的,因此要防止外部操作改变数据,一旦数据出错,那么方法也会受到相应的波及。这些操作要按照实际的需求让外部使用。
//stack.h
class Stack
{
public:
void Init(int capacity=4);//缺省参数声明
private://数据私有
int* _arr;
int _length;
int _capacity;
};
//stack.cpp
void Stack::Init(int capacity)//该方法的实现
{
_arr=(int*)malloc(sizeof(int)*capacity);
_length=0;
_capacity=capacity;
}
类的实例化
类可以理解为一个房屋的图纸,这个图纸规定了一些基本的信息,因此需要根据图纸的规定修出相应的房子。
通过实例化才可以真正有一个栈,这个栈占用实际的空间。
面向对象的编程可以理解为将一个问题拆分为不同的对象,依靠参数完成对象之间的交互。
举一个洗澡的例子,如果是面向过程角度考虑,首先我进入浴室,打开水龙头,然后洗漱,然后把身体擦干,这些都是解决问题的步骤。如果是面向对象考虑,那么”万物皆对象“,首先我传递参数给门,然后我传递消息给水龙头,最后传递消息给毛巾,毛巾可以调用自己的方法擦干身体。
类对象模型
类再计算大小时不管成员函数,只管成员变量和内存对齐。
类对象的存储方法
由于一个类可以实例化很多对象,就拿栈来说,这些栈它的存储的数据可能会不一样,但是它们的方法,如进栈出栈都是一样的。所以说实例化如果再为方法开辟空间,就会造成浪费。
解决的方法是:只保存成员变量,成员函数存放在公共的代码段(常量区)。
-
注意:一个类的大小,实际就是该类中的成员变量和内存对齐。但要注意空类,空类大小不是0,编译器为空类提供了一个字节表示它存在。
this指针
class Date
{
public:
void SetDate(int year, int month, int day)
{
_year=year;
_month=month;
_day=day;
}
private:
int _year;
int _month;
int _day;
}
int main()
{
Date d1;
Date d2;
d1.SetDate(2021,2,20);
d2.SetDate(2022,8,19);
}
类中成员函数是公用的,此处的d1.SetDate(2021.2.20)
,编译器为什么知道给d1这个实例对象去设置而不给d2设置?这是因为有this的存在。
C++编译器给每个给每个非静态的成员函数增加了一个隐藏的指针参数,让该指针指向当前函数(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,多是通过指针访问。
上述代码可以理解为下面的代码:
class Date
{
public:
void SetDate(Date* this, int year, int month, int day)
{
//生成一个类的指针
this -> _year=year;
this -> _month=month;
this -> _day=day;
}
private:
int _year;
int _month;
int _day;
}
int main()
{
Date d1;
Date d2;
d1.SetDate(*d1,2021,2,20);
d2.SetDate(*d1, 2022,8,19);
}