全文目录
- 引言(初识面向对象)
- 类和对象
- 定义
- 类访问限定及封装
- 类定义的两种方式
- 类实例化与类对象大小
- this指针
- 总结
引言(初识面向对象)
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题;
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
比如洗衣服这个操作,对于面向过程的C语言而言,就是要根据洗衣服的过程按照逻辑逐步依次实现:
最多将放洗衣液的多个步骤封装一个函数,进行多遍的冲洗操作写一个循环。总之面向的是洗衣服的过程。
但对于面向对象的C++而言,洗衣服的过程就可以按照封装的几个对象之间的交互实现。比如分为人、衣服、洗衣液与洗衣机:
洗衣服只需要人将衣服与洗衣液放到洗衣机中,由洗衣机执行操作即可。有些洗衣机对象还可以实现自动加洗衣液,此时洗衣液就是洗衣机的一个内部对象。总之面向的是洗衣服时参与到的对象。
不难发现,面向对象的操作过程是要比面向过程的操作过程简单高效的。也就是说,面向对象编程是一种相对高级的编程方式。
类和对象
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数:
struct Stack
{
int* _data;
int _top;
int _capacity;
void init();
void push();
void pop();
void destory();
};
在C语言中实现的栈,结构体中只有变量,是通过函数来操作结构体中的变量;而C++的栈结构体中可以定义函数,这其实就是一个类。struct可以用来定义类,在C++中有一个专门的关键字class
来定义类:
定义
class定义类的格式如下:
class Classname
{
//类的主体(成员变量与成员函数)
};
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
类访问限定及封装
用类将对象的属性与方法结合到一起就是封装,并且通过访问限定隐藏属性以及方法的细节,只向用户提供接口,使用户更方便的使用类:
有三种访问限定符:public
(公有)、protected
(保护)、private
(私有):
class Classname
{
public:
//公共权限:类内可以访问,类外可以访问;
protected:
//保护权限:类内可以访问,类外不可以访问(子类可以访问);
private:
//私有权限:类内可以访问,类外不可以访问(子类不可以访问);
};
不难发现,由于类需要向用户提供接口,而隐藏属性,所以类的成员函数一般都是放在公有域,成员变量都放在私有域。
需要注意的是:
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止, 如果后面没有访问限定符,作用域就到 } ,即类结束;
- class的默认访问权限为private,struct为public(因为struct要兼容C)。
类定义的两种方式
定义类时,可以有两种方式:函数的声明与定义在一起放在类中或者函数的声明与定义分离,声明放在类中,函数定义放在.cpp文件中:
需要注意的是,如果在类中定义成员函数,这个函数会被默认当成内联函数(这里简单写一个日期类例举):
class Date
{
public:
void InitDate(int year = 2023, int month = 2, int day = 10)
{
_year = year;
_month = month;
_day = day;
}
Date& AddDate(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
int GetMonthDay(int year, int month)
{
int monthdays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
monthdays[month] = 29;
}
return monthdays[month];
}
private:
int _year;
int _month;
int _day;
};
如果声明和定义分离,在定义函数时需要类名::函数名
的方式定义:
class Date
{
public:
void InitDate(int year = 2023, int month = 2, int day = 10);
Date& AddDate(int day);
int GetMonthDay(int year, int month);
private:
int _year;
int _month;
int _day;
};
void Date::InitDate(int year = 2023, int month = 2, int day = 10)
{
_year = year;
_month = month;
_day = day;
}
Date& Date::AddDate(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
int Date::GetMonthDay(int year, int month)
{
int monthdays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
monthdays[month] = 29;
}
return monthdays[month];
}
在定义类时,建议使用第二种方式,并且将公有的成员函数定义在私有的成员变量之前。
类实例化与类对象大小
在上面定义的类只是一种类类型,相当于房子的图纸,并不在内存中开辟空间。
用类创建对象的过程称为类的实例化,实例化的对象占有具体的内存空间。
类名就是类类型的类型名,一个类类型可以实例化多个类对象,比如上面的日期类:
int main()
{
//实例化三个类对象
Date d1;
Date d2;
Date d3;
//对三个类对象调用其成员函数进行初始化
d1.InitDate();
d2.InitDate(2023, 5, 14);
d3.InitDate(2000, 1, 1);
return 0;
}
上面的内容并不难理解,但是由于成员函数也是包含在类对象中的,类的大小应该如何计算?
在C语言部分,我们了解了如何计算结构体的大小,结构体在内存分配时遵循内存对齐的规则:
戳我看结构体内存对齐详解哦
类的大小也遵循内存对齐的规则,但是对于成员函数,它虽然封装在类中,但是却不占用类对象的空间。成员函数的只保存一份在代码段,类对象中只保存成员变量:
int main()
{
Date d1;
cout << sizeof(d1) << endl;
return 0;
}
所以对于上述Date类对象d1的大小只包括三个int型的成员变量,根据内存对齐,大小就是12字节。
对于空类,会有一个字节的空间以区分。
this指针
在上面提到过,私有的成员变量在类内部是可以访问的。比如我们对不同的类进行加天数的操作后的结果是不同的:
int main()
{
//类实例化
Date d1;
Date d2;
//类初始化
d1.InitDate();
d2.InitDate(2023, 5, 14);
//不同的日期类对象+100天
d1.AddDate(100);
d2.AddDate(100);
return 0;
}
那么成员函数是怎么知道是哪个对象调用的,并且获取到该类的成员变量并对其进行操作的?
这是由于,我们在调用成员函数时,其实是在替某个对象调用其成员函数。类对象的成员函数参数列表中其实有一个隐含的this
指针,它在参数列表的左边。
this指针就是调用成员函数的对象的指针,它的作用就是帮助成员函数调用成员变量。其实上面的AddDate函数本质上是这样的:
//Date& Date::AddDate(Date *const this, int day); //错误代码:this指针必须隐式传参
在函数内部是这样实现访问成员变量的:
{
this->_day += day;
while (this->_day > GetMonthDay(this->_year, this->_month))
{
this->_day -= GetMonthDay(this->_year, this->_month);
++(this->_month);
if (this->_month > 12)
{
++(this->_year);
this->_month = 1;
}
}
return *this;
}
虽然显式的用this指针访问成员变量是可以的,但是这样的代码很不好看,不简洁。
this指针的特性:
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值;
- 只能在成员函数的内部使用;
- this指针本质上是成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针;
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
总结
在这篇文章中,初步了解了类和对象,包括定义、实例化以及this指针。接下来会继续深入介绍类和对象的相关知识,希望大家持续关注哦
如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出
如果本文对你有帮助,希望一键三连哦
希望与大家共同进步哦