类与对象(上)
- 一、面向过程和面向对象的区别
- 二、类
- 1、类的引入
- 2、类的定义
- (1)类的基本定义
- (2)类的成员函数的定义方法
- 3、类的访问限定符
- 4、封装
- 5、驼峰法命名规则
- 6、类的作用域
- 7、类的实例化
- (1)实例化的概念与作用
- (2)类没有实例化对象
- (3)类有实例化对象
- 8、生命周期与作用域的关系
- 三、类对象模型
- 1、类对象的存储方式(只保存成员变量,成员函数存放在公共的代码区中)
- 2、C语言结构体内存对齐规则
- 3、计算类对象的大小
- 四、this指针
- 1、示例代码
- 2、this指针的作用
- 3、成员函数的实际参数与错误
- 4、this指针的特性
一、面向过程和面向对象的区别
在学习C++之前,我们已经学了C语言,而C语言是面向过程的计算机语言,关注的是过程,即要求编程者分析出求解问题的步骤,进而通过函数调用的方式逐步解决问题;而C++是基于面向对象的计算机语言,关注的是对象,即将一件事情拆分成不同的对象,靠对象之间的交互完成要求。但是,因为C++是在C语言的基础之上创建出来的计算机语言,所以C++是兼容C语言的,即C++不是纯面向对象的语言,它可以面向对象和面向过程混编。
二、类
1、类的引入
在C语言中,有结构体struct的概念,在C++中,因为它兼容C语言的缘故,所以struct在C++中可以是结构体,但确切的说它升级成为了类,而类与结构体最明显的两个区别就是能在类里面定义函数和有访问权限的概念。比如用C语言实现栈(参见栈与队列),里面的结构体中只能有成员变量而不能有成员函数,所以,实现栈的一系列函数只能在结构体外去实现。但是,在C++中使用类时更喜欢用class而不是struct,虽然两者在C++中都是类,但class的默认访问权限为private,而struct的默认访问权限为public。
2、类的定义
(1)类的基本定义
class className
{//类体:由成员函数和成员变量组成
}; //记得加上分号
- class为定义类的关键字,ClassName为要定义的类的名字,{}中的内容为类的主体,在类定义结束时,}后面的分号不能省略。
- 类体中的内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数。
(2)类的成员函数的定义方法
- 声明和定义全部放在类体中,编译器可能会将其当成内联函数处理。
class Person
{
public:void ShowInfo(){cout << _name << " " << _sex << " " << _age << endl;}
private:char* _name;char* _sex;int _age;
};
- 类的成员函数的声明放在.h文件中,成员函数的定义放在.cpp文件中,此时在成员函数的定义中,成员函数名前需要加类名::。
- 如果类的成员函数比较小,能够成为inline函数,则可以直接在类里面定义,而如果是大函数、递归的成员函数,则应该用声明和定义分离的方法定义成员函数。这样代码比较简洁,可读性比较高,阅读用这种方法所写的代码时能够比较容易地了解类的作用。
- 什么样的函数适合inline函数参见万字讲解C++基础
3、类的访问限定符
- public修饰的成员在类外可以直接被访问。
- protected和private修饰的成员在类外不能直接被访问,即只能在类里面访问,而在此处protected和private的作用是相似的。
- 访问限定符的访问权限作用域从该访问限定符出现的位置开始,到下一个访问限定符出现时结束或者后面没有访问限定符了,则到 }结束。
- 访问限定符只在编译时有用,当数据映射到内存后,被访问限定符修饰的所有成员没有任何区别。
4、封装
将数据和操作数据的方法(类的成员函数)进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互。
5、驼峰法命名规则
- 函数名、类名等所有单词首字母大写,如SnowDragon
- 变量首字母小写,后面单词首字母大写,如snowDragon
- 成员变量,首单词前面加_,如_snowDragon
6、类的作用域
类似于函数、循环等等,它们都具有作用域,类也具有作用域,即类的{}所括起来的区域。类的所有成员都被包含在这个作用域中。当在类的类体外定义成员时,需要使用::作用域操作符指明定义的成员属于哪个类域。
7、类的实例化
(1)实例化的概念与作用
- 用类类型创建对象的过程,称为类的实例化。
- 类是对对象进行描述的,即它是一个像模型一样的东西,限定了类具有的成员,定义完成一个类时,编译器并没有分配实际的内存空间出来,即此时的类不能存储数据。
- 一个类可以实例化出多个对象,而实例化出的对象是具有实际的物理空间的,即它们能够存储类的成员变量的数据。
(2)类没有实例化对象
(3)类有实例化对象
- 因为类是定义在全局作用域中的,而main函数是在类外,所以此处要在main函数中调用类的成员变量时,得将类中类的成员变量用public访问限定符修饰,否则无法进行访问。
- 对_name和_sex成员变量赋值时,由于上图的代码在main函数中是用常量字符串对其进行赋值,所以_name和_sex需用const进行修饰。
8、生命周期与作用域的关系
- 作用域影响变量的访问,但不影响变量的生命周期。
- 生命周期与变量存储的位置有关,当变量所在的空间是在栈上的,当变量所在的作用域结束时,该作用域在栈上的空间将被操作系统回收,而变量也在这块被回收的空间中,即它的生命周期也结束了。
三、类对象模型
1、类对象的存储方式(只保存成员变量,成员函数存放在公共的代码区中)
- 如果要调用类的成员函数,编译器会在编译链接时根据函数名去公共代码区找到被调用的函数的地址,用反汇编的方式查看时,会有call该函数的地址的代码。
2、C语言结构体内存对齐规则
- 第一个成员从在与结构体偏移量为0的地址处开始存储。
- 对齐数为在编译器默认的对齐数与该成员类型的大小中的较小值(VS编译器中的默认对齐数为8)。
- 其他成员变量要对齐到对齐数的整数倍的地址处才可以开始存储。
- 结构体总大小为:最大对齐数(所有变量对齐数的最大值与默认对齐数,两者中最小的那个)的整数倍。
- 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,而结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
3、计算类对象的大小
- 一个类的大小就是该类中所有成员变量的大小之和,计算大小总和的方法要注意内存对齐规则。
- 如果类为空类,编译器会给该类一个字节来唯一标识这个类的对象。
四、this指针
1、示例代码
class Date
{
public:void Init(int year, int month, int day){//this = nullptr;_year = year;_month = month;_day = day;}void Print(){cout << _year << " " << _month << " " << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Init(2023, 8, 15);d1.Print();Date d2;d2.Init(2023, 8, 16);d2.Print();return 0;
}
2、this指针的作用
- 在上方的示例代码Date类中,有Init与Print两个成员函数,但在两个函数的函数体中没有关于不同对象的区分,那当d1调用Init函数时,该函数是通过this指针进行访问并设置对象。
- 即C++编译器给每个”非静态的成员函数“增加了一个隐藏的指针作为函数的形式参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中对所有“成员变量”的操作都是通过这个指针去访问的。只不过所有的操作对用户都是透明的,即用户不需要自己传递当前对象的地址作为参数,编译器会自动完成这些操作。
3、成员函数的实际参数与错误
4、this指针的特性
- this指针的类型:类类型* const,因为this被const修饰了,所以在成员函数中,不能给this指针赋值(上方代码中注释掉的那行代码)且只能在“成员函数”的内部使用。
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,编译器会将对象的地址作为实参传递给this形参,因为this是形参,所以它存在栈上。
- this指针是类的成员函数的形式参数中第一个隐含的指针形参,在VS编译器下,传给this的实参是通过ecx寄存器自动传递的,不需要用户自己填写该实参。而用寄存器传递会提高this访问变量的效率,是否对this指针进行优化是由编译器决定的。
- 只要类的成员函数内部不需要this进行访问,则该成员函数的this指针可以为空,即调用这个成员函数的对象可以为nullptr。
- 实参和形参位置不能显示传递和接收this指针,但是可以在成员函数内部使用this指针。
本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请务必点赞、收藏加关注💕💕💕