面向过程和面向对象初步认识
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
类的引入
C 语言结构体中只能定义变量,在 C++ 中,结构体内不仅可以定义变量,也可以定义函数。 比如:之前在数据结构初阶中,用C 语言方式实现的栈,结构体中只能定义变量 ;现在以 C++ 方式实现, 会发现 struct 中也可以定义函数 。struct Stack {int* a;int top;int capacity;void Init(){a = 0;top = 0;capacity = 0;}void Push(int x){//...} };int main() {Stack s1;s1.Init();s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);//c++写法/*struct Stack s1;StackInit(&s1);StackPush(&s1, 1);StackPush(&s1, 2);StackPush(&s1, 3);*//c语言写法return 0; }
我们看一下c++和c语言类有什么区别,发现c++规则变多了但是写起来更简洁了(平常我们用栈还得传参数,现在少传很多),c++直接在类里定义函数,短的函数默认是内联,长的函数建议声明和定义分离(在定义的地方要指明类域)
C++兼容c语言struct的所有用法 struct同时升级成了类
1、类名就是类型,Stack就是类型,不需要加struct
2、类里面定义函数
类的定义
class className{// 类体:由成员函数和成员变量组成}; // 一定要注意后面的分号class 为 定义类的 关键字, ClassName 为类的名字, {} 中为类的主体,注意 类定义结束时后面 分 号不能省略 。类体中内容称为类的成员: 类中的 变量 称为 类的属性 或 成员变量; 类中的 函数 称为 类的方法 或者 成员函数 。类的两种定义方式:1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::一般情况下,更期望采用第二种方式。 注意:我们为了方便演示一定义类,大家后序工 作中尽量使用第二种。成员变量命名规则的建议:class Date { public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:/*int year_;int m_year;int mYear;*/int _year;int _month;int _day; };
尽量将成员变量写成_的形式
类的访问限定符及封装
我们根据下边代码理解,public意思是公有,类外可以访问,可以看到成员函数放在公有,可以访问,private是私有,类外访问会报错,除非把私有访问限定符去掉,但我们一般不这么做( 默认成员函数放公有,成员变量私有 )struct和class区别:在c++中,二者没有显著区别,最大的区别是struct类不加访问限定符默认是公有,class类默认是私有【面试题】问题: C++ 中 struct 和 class 的区别是什么?解答: C++ 需要兼容 C 语言,所以 C++ 中 struct 可以当成结构体使用。另外 C++ 中 struct 还可以用来定义类。和class 定义类是一样的,区别是 struct 定义的类默认访问权限是 public , class 定义的类默认访问权限是private 。注意:在继承和模板参数列表位置, struct 和 class 也有区别,后序给大家介绍。此处封装作为了解,后续会一直渗透封装的理念
类的作用域
类定义了一个新的作用域 ,类的所有成员都在类的作用域中 。 在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。(刚才讲过)
类的实例化
类的实例化很重要,类没实例化之前只是一张图纸,实例化每个对象以后,对象是实体,存放了成员变量,成员函数存在公共代码区(代码段),因为所有对象都会用这一个函数,没必要存在对象中
class Date { public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:// 声明int _year;int _month;int _day; };int main() {//Date::_year = 1;声明没有开空间,不能赋值Date d1;Date d2;d1.Init(2025, 10, 7);d2.Init(2026, 10, 7);//d1._year++;//d2._year++;只有设置为public公有,才能在类外访问return 0; }
注意:我们一般都会将成员变量设置为私有,成员函数设置公有,因为规定默认不可以在类外访问成员变量(可能会有风险),成员函数在类内外都能访问,成员变量可以在类内访问,在类外可以调用成员函数去访问
类对象模型
class Date { public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}//private:// 声明int _year;int _month;int _day; };class A { private:char _ch;int _a; };class B {};class C { public:void f(){}; };int main() {Date d1;cout << sizeof(d1) << endl;cout << sizeof(A) << endl;B b1;B b2;cout << sizeof(B) << endl;cout << sizeof(C) << endl;// 无成员变量的类,对象大小开一个字节,这个字节不存储有效数据// 标识定义的对象存在过cout << &b1 << endl;cout << &b2 << endl;return 0; }
我们可以看到Date类大小是12,A类大小是8,我们猜测只计算成员变量的大小,并且是依照结构体内存对齐(我之前讲过内存对齐,特别详细,有兴趣可以看看),结构体的大小计算的是对象的大小
自定义类型:结构体,枚举,联合-CSDN博客https://blog.csdn.net/eixjdj/article/details/145168323https://blog.csdn.net/eixjdj/article/details/145168323我们也能看到空类和只有成员函数的类都是1字节,无成员变量的类,对象大小开一个字节,这个字节不存储有效数据 标识定义的对象存在过
这些我们都能理解,为什么成员函数不计算大小呢
因为类的大小计算的是对象的大小,对象里边只有成员变量,成员函数存在了共享区(代码段),所以我们计算大小的时候忽略了成员函数的
为什么放在共享区(因为每个对象存的成员变量的值可能是不一样的,要分开放;而成员函数每个对象都可以访问,所以把成员函数放在公共区域(代码段里))
this指针
最后我讲一个极其重要的东西-》this指针
代码示例
class Date { public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}// 不能显示的写实参和形参// void Print(Date* const this)void Print(){//this = nullptr;err 因为是const//cout << this << endl;// 但是可以在类里面显示的使用// cout << this->_year << "-" << this->_month << "-" << this->_day << endl;cout << _year << "-" << _month << "-" << _day << endl;}private:int _year; // 年int _month; // 月int _day; // 日 };int main() {Date d1;d1.Init(2025, 10, 7);Date d2;d2.Init(2026, 10, 7);d1.Print(); // d1.Print(&d1);d2.Print();return 0; }
为什么我们打印并没有传参数,就能打印不一样的呢
因为是编译器帮我们干了这件事情,当对象调用成员函数时,会将对象的地址传给成员函数的形参,这个形参就是this指针,只不过我们正常看到的是没有参数的版本,所以就能做到不同的对象地址不同,传递不同的参数给this,就会调用专属的实例化出来的对象(不能显示的写实参和形参)
this指针的特性(了解)
【面试题】1. this 指针存在哪里?存在栈帧上,vs默认是存在ecx寄存器中(this指针是在运行时由编译器自动传给非静态成员函数)this指针会频繁用所以存放在寄存器里2. this 指针可以为空吗?可以为空,下边通过题来证明这个// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行 class A { public:void Print(){//cout << _a << endl;cout << "Print()" << endl;} private:int _a; };int main() {A* p = nullptr;// 成员函数的地址不在对象中// 成员变量是存在对象中p->Print();//p->_a = 1;return 0; }
可以看到这次正常打印,因为p指针是空,并不会解引用Print函数,Print函数是在编译链接时候确定地址,在运行时call调用,和p的空指针没关系,空指针并没有做事情,这里p指针的主要作用是知道Print函数是这个类的,并且把nullptr传递给this指针(成员函数存在对象外边)而下边这个空指针会报错,会空指针解引用访问_a,那就直接nullptr解引用了(成员变量存在对象中)