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:
//私有权限:类内可以访问,类外不可以访问(子类不可以访问);
};
不难发现,由于类需要向用户提供接口,而隐藏属性,所以类的成员函数一般都是放在公有域,成员变量都放在私有域。
需要注意的是:
定义类时,可以有两种方式:函数的声明与定义在一起放在类中或者函数的声明与定义分离,声明放在类中,函数定义放在.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字节。
对于空类,会有一个字节的空间以区分。
在上面提到过,私有的成员变量在类内部是可以访问的。比如我们对不同的类进行加天数的操作后的结果是不同的:
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指针。接下来会继续深入介绍类和对象的相关知识,希望大家持续关注哦
如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出
如果本文对你有帮助,希望一键三连哦
希望与大家共同进步哦