目录
一、面向对象和面向过程的初步认识
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间完成交流。
C++基于面向对象的原因:它不是单纯面向对象,因为兼容了C语言,所以是面向对象和面向过程的混编。
二、类的引入以及定义
1、C语言struct升级成了类
在C语言中,结构体只能定义变量,在C++中,结构体升级为了类,不仅可以定义变量,也可以定义函数。
2、C++中struct和class的区别
因为C++兼容了C语言,所以C++中struct可以当作结构体去使用,也可以像class一样来定义类,
从访问权限来看,struct的成员默认访问权限是pubilc,class的成员默认访问权限是private。
3、类的定义
class className
{
//类体由成员变量和成员函数组成
}; //结尾由分号
定义类的关键字是class,className是类名,{}内部是类的主体,以分号结尾。
类的成员有成员变量和成员函数。
4、类的定义方式
类有两种定义方式:
一是声明和定义都在类中,当成员函数在类内定义,编译器可能会将其当作内联函数处理。
//声明和定义在类中
class Date
{
public:
//默认成员函数
Date(int year=2022, int month=5, int day=20)
{
_year = year;
_month = month;
_day = day;
}
void print()//成员函数
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
二是声明和定义分离,声明放在.h文件,定义放在.cpp文件(更倾向的做法)
意义:可以大概通过框架构成可以知道有哪些成员变量和成员函数,通过注释可以大概知道成员变量数据是什么和成员函数的功能是什么,能够大概理解类的框架,通过定义函数查看实现细节。
//.h文件
//声明和定义分离
class Date
{
public:
//默认成员函数
Date(int year=2022, int month=5, int day=20)
{
_year = year;
_month = month;
_day = day;
}
void print();//成员函数
private:
int _year;
int _month;
int _day;
};
//.cpp文件
#include"类的两种定义方式.h"
void Date::print()
{
cout << _year << " " << _month << " " << _day << endl;
}
三、封装特性
1、访问限定符
访问限定符有三种:public(公有)、protected(保护)、private(私有)。
1、public修饰的成员在类外可以直接被访问。
2、protected和private修饰的成员在类外不能直接访问
3、访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止。
4、从访问权限来看class和struct区别在于:class默认访问权限为private,而struct默认访问权限为public(因为C++兼容了C语言)。
2、封装
1、C++中面向对象的三大特性:封装、继承、多态。
2、封装:用类将对象的属性和方法结合在一起,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
3、封装的本质:封装的本质是一种管理,我们使用类数据和方法都封装到一起,如果不想让别人看见,就用访问限定符protected或者private把成员封装起来,开放一些公有的(public)成员函数对成员合理的访问。
四、类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。如果要在类外定义成员函数,需要加作用限定符::告诉编译器该成员是属于哪个类作用域的。
class Date
{
public:
//默认成员函数
Date(int year=2022, int month=5, int day=20)
{
_year = year;
_month = month;
_day = day;
}
void print();//成员函数
private:
int _year;//声明 没开空间是声明,开了空间是定义
int _month;
int _day;
};
//指明print()是属于Date作用域的
void Date::print()
{
cout << _year << " " << _month << " " << _day << endl;
}
五、类的实例化
1、概念
用类类型创建对象的过程,称为类的实例化。
1、定义出的类实际上没有分配内存空间,就好比房子和设计图的关系,设计图指定义的类,在房子建造出来之前不占用土地资源。
2、一个类可以实例化多个对象,实例化出的对象占用存储空间(即物理空间)。
2、类对象存储方式
类实际的存储方式是,每个对象只保存自己的成员变量,而成员函数是放在公共代码区的,不占用类的存储空间,需要调用即从类成员函数表中寻找。
3、计算类的大小
1、类的大小是该类"成员变量之和"(成员函数不占用类的大小),经过内存对齐后,即为该类的大小。
2、空类比较特殊,没有成员变量和成员函数,编译器给该空类一个字节的大小来标识这个类。
代码示例:
//类中既有成员变量,也有成员函数
class A1
{
public:
void f1()
{}
private:
int _a;
char _c;
};
//类中只有成员函数
class A2
{
public:
void f2()
{}
};
//空类 无成员变量和成员函数
class A3
{
};
int main()
{
cout << sizeof(A1) << " " << sizeof(A2) << " " << sizeof(A3) << endl;
return 0;
}
//运行结果 8 1 1
六、类成员函数的this指针
1.this指针的引入
为什么要使用this指针?请看下面代码
class Date
{
public:
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
int main()
{
Date d1;
Date d2;
d1.SetDate(2022, 5, 20);
d2.SetDate(2023, 5, 20);
}
问题:当我们实现一个日期类,实例化出两个对象,d1调用SetDate函数的时候,编译器是如何辨别出d1对象,并设置d1对象,而不是d2对象。
解决:C++是通过引入了this指针来解决该问题,因为C++编译器给每个非静态的成员函数增加了一个隐含的指针参数,让该指针指向当前对象,可以通过this指针访问非静态成员函数的成员。
2.this指针的特点
1、this指针的类型:类类型 * const。
例如 Date* const this ,this指针本身不能被修改,但其指向的内容可以修改。
2、this指针只能在类内部使用。
实参和形参位置不能显示传递和接收指针,编译器会报错,编译器会自动帮我们完成this指针的传递。
3、this指针本质上是一个成员函数的形参,在对象调用成员函数时,将对象地址当作实参传递给this指针(this指针是对象的地址),所以对象中不存储this指针。
4、this指针的存储位置:栈区 (特殊使用情况也会存储在ecx寄存器,编译器决定)。
因为this指针是形参,所以存放在栈区,出了作用域就被销毁
如果频繁使用this指针,存放在寄存器中会快一些,vs下面传递指针,是通过ecx寄存器传递的,这样this访问变量可以提高效率。
3、this指针相关问题
1、this指针是什么?
this指针是类地址,指向对象的首地址
2、this指针存放在哪里
栈区或在寄存器(编译器决定)
3、this指针可以为空吗?
this指针是可以为空的,如果成员函数内部用不上this指针,不对其解引用就不会出错。
代码示例:
class A
{
public:
//默认构造
A(int a=4)
{
_a = a;
}
void f1()
{
cout << _a << endl;
}
void f2()
{
cout << "f2()调用" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
//p->f2();
p->f1();
return 0;
}
结果:1、空指针p调用f2()函数正常运行
原因:空指针p调用f2()的时候,不会区指针指向的对象里面找,也没有解引用访问类内的数据
,函数是存放在公共代码区的,在编译链接阶段确定了函数地址,并调用该函数。
2、空指针p调用f1()函数运行崩溃
原因:p是空指针无法访问类中的数据。如图所示