目录
前言:
一、初识类:
二、类的引入:
三、类的定义:
1.类的两种定义方式:
2.成员变量的命名规范:
四、类的访问限定符与封装:
五、类的实例化:
1.类的空间计算:
六、this指针:
this指针特性:
总结:
前面我们学习C++一些基础的内容,也可以说C++针对C语言的缺陷进行改进。而今天我们要学的是C++的内容也就是类和对象。
我们先来看看C语言解决一个问题的过程:
假设有以下这个场景:你需要手洗一件衣服,你需要做那几步?
拿盆—>放水—>放衣服—>放洗衣粉—>手搓—>换水—>放洗衣粉—>手搓—>拧干—>晾衣服
上面就是C语言解决一个问题的过程,现在我们来看看C++是怎么解决这个问题的:
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。例如上面例子:可以归类成这几个对象:你、衣服、洗衣机、洗衣粉。这几个对象都会有自己的属性,也就是自己的动作,比如对象“你”会实现:拿盆—>放水—>放衣服—>放洗衣粉。有了对象之后,洗衣服这个动作就由这四个对象交互完成,人不再关系洗衣机该如何洗、如何拧干等问题。
先来看看我们C语言中的struct,在C++中,它被升级成了类.
struct date
{
int year;
int month;
int day;
};
这是在C语言中的我们使用的方法,现在他被升级成了类,我们就可以在一个结构体中定义一个函数了:
#include
using namespace std;
struct date
{
int Add(int a,int b)
{
return a + b;
}
int year;
int month;
int day;
};
int main()
{
date a;
cout << a.Add(2, 8) << endl;
return 0;
}
大家发现没有,再main函数里使用结构体的时候我没有加struct,编译器也没有报错,这是因为C++把结构体当成类,而类不需要加struct,但是在C++中更喜欢用class来替换struct。
class classname
{
//类体;
};
class为定义类的关键字,classname为类的名称,{}为类的主体,与结构体相同,类结束时也要加一个;
类体中内容称之为类的成员:
1.类的变量称为类的属性或成员变量。
2.类中的函数称为类的方法或者成员函数。
类定义了一个新的作用域,仅能在这个作用域里访问private成员,与C相同定义一个函数的时候也可以采用声明与定义放在同一个地方:
class date
{
public:
int max(int a, int b)
{
return a>b?a:b;
}
};
虽然可以生命和定义放在一起,但是更推荐将函数声明放在类中,定义写在这个类的外面(也可以解释为将类的生命放在头文件当中,类的具体定义放到源文件中,这样做的好处和C一样),但注意,这样写要在定义出加上类作用声明符“::",例如:
class date
{
public:
int max(int a, int b);
};
int date::max(int a, int b)
{
return a > b ? a : b;
}
我们先来看看这段代码:
#include
using namespace std;
class date
{
public:
int Init(int day)
{
day = day;
}
int days()
{
return day;
}
private:
int day;
};
int main()
{
date d1;
d1.Init(6);
cout << d1.days() << endl;
return 0;
}
这段代码中的day=day原意是想要将外部传进的参数放进这个d1的day中,但是编译器会优先使用局部变量,所以不能达到我们想要的结果。所以我们一般将成员变量名字前面加一个_来区分。
这里介绍一下上述代码中出现的三个关键字:private、public、protected。这几个是访问限定符。
1.在类中出现访问限定符的作用域一直到下一个访问限定符出现为止。
2.public修饰的成员在类外可以直接被访问。
3.private与protected(他们有些差别,但是目前暂且认为他们是一样的)修饰的成员在类外无法被访问,通常通过public里的对应函数来进行访问。
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
类是对一个对象的描述,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;可以把类看成你建房子之前设计的图纸,这张图纸设计了你房子几个房间,几个客厅等等一系列东西。当你用这个图纸去造出一个个房子时,它就占用土地面积。建房子过程就是类实例化过程,类实例化之后就占用内存空间了。
#include
using namespace std;
class date
{
public:
int Init(int day)
{
_day = day;
}
int days()
{
return _day;
}
private:
int _day;
};
int main()
{
date d1;//类的实例化
d1.Init(6);
cout << sizeof(date) << endl;
return 0;
}
我们的正常想法是:类中有一个整形变量,还有两个成员函数,所以这个类的大小应该在4字节以上,但是结果呢?
代码运行之后:
为什么只有4字节呢?
还是刚刚那个图纸,但是现在的图纸是建小区的图纸,小区都有篮球场,但我需要为每个家庭都建一个篮球场吗?显然不需要,如果每个家庭都建一个篮球场的话,而且篮球场需要好几个人才能一起打,会造成大量的空间浪费,所以我们就画出一块地方专门建篮球场供大家使用。类中的成员函数也一样,我们为了避免造成空间浪费将他们放在公共区域,这样每一个对象使用的成员函数都是用一个。所以计算一个对象的所占内存大小的时候仅需要计算成员变量的内存大小(符合结构体内存对齐的规则)
我们来看看这段代码:
#include
using namespace std;
class date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1;
date d2;
d1.Init(2023, 5, 20);
d1.Print();
return 0;
}
输出结果:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参 数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该 指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
也就是说其实上面的代码在编译器看来其实是这样的:
#include
using namespace std;
class date
{
public:
//因为规定this只能在成员函数内部使用,所下面采用屏蔽
//void Init(date* this,int year, int month, int day);
void Init(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
//因为规定this只能在成员函数内部使用,所下面采用屏蔽
//void Print(date* this);
void Print()
{
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1;
d1.Init(2023, 5, 20);
d1.Print();
return 0;
}
1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
2. 只能在“成员函数”的内部使用。
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。 所以对象中不存储this指针。
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
5.this指针存储在栈中。
这篇文章,我们学习了什么是类,如何定义一个类,类的实例化还有如何计算类的空间计算,最后介绍了this,解释了函数是如何知道应该设置对象的。今天我们的学习到此结束。