🐶博主主页:@ᰔᩚ. 一怀明月ꦿ
❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux
🔥座右铭:“不要等到什么都没有了,才下定决心去做”
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
类和对象
类的引入
类的定义
类的定义方式
1.声明和定义放在类体
2. 声明放在.h文件中,类的定义放在.cpp文件中
类的访问限定符及封装
访问限定符
类的作用域
类的实例化
类对象模型
结构体内存对齐规则
this指针
this指针的特性
this指针存在哪里?
this指针可以为空吗?
补充
类里的默认生成函数
构造函数
析构函数
类和对象
面向过程和面向对象初步认识
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成
类的引入
C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
struct ADD {void add(int x,int y){cout<<x+y<<endl;}int a;int b; };
C++兼容C语言,结构体可以继续使用,同时struct也升级成了类
struct A {int age;int score; }; int main() {struct A a1;A a2; } 定义变量时,既可以struct A a1;,也可以A a2;
但是在C++中更喜欢使用class来代替
类的定义
class 类名 {类体:成员函数和成员变量组成 };//一定主意后面的分号
class为定义类的关键字,类中的元素称为类的成员,类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。
类的定义方式
1.声明和定义放在类体
注意:成员函数如果在类中定义,编译器可能将其当成内联函数
class student {void show(){cout<<_name<<_sex<<_age<<endl;}char* _name;char* _sex;int _age; };
2. 声明放在.h文件中,类的定义放在.cpp文件中
class student {void show();char* _name;char* _sex;int _age; };#include"main.h" void student::show() {cout<<_name<<_sex<<_age<<endl; } 建议我们使用第二种方法
类的访问限定符及封装
访问限定符
public(公有) protected(保护) private(私有)
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体去使用。另外C++中struct还可以用来定义类。和class是定义类是一样的,区别是struct的成员默认访问方式是public,class是struct的成员默认访问方式是private
封装
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。
例如成员函数的类外定义
void student::show() {cout<<_name<<_sex<<_age<<endl; }
类的实例化
用类类型创建对象的过程,称为类的实例化
1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
class student {void show();char* _name;char* _sex;int _age; }; int main() {student s1;return 0; } 其中的student类实例化了一个对象s1
类对象模型
如何计算类对象的大小
首先我们得搞明白类的存储方式:只保存成员变量,成员函数放在公共的代码代码段
//成员函数和成员变量 class student1 {void show(){}char* _name;char* _sex;int _age; }; //成员函数 class student2 {void show(){} }; //成员变量 class student3 {char* _name;char* _sex;int _age; }; //空类 class student4 { }; int main() {cout<<"class student1:"<<sizeof(student1)<<endl;cout<<"class student2:"<<sizeof(student2)<<endl;cout<<"class student3:"<<sizeof(student3)<<endl;cout<<"class student4:"<<sizeof(student4)<<endl; } 结果: class student1:24 class student2:1 class student3:24 class student4:1
结论:一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类(就是告诉编译器这个类存在)。
结构体内存对齐规则
1.第一个成员与结构体变量偏移量为0的地址处
2.其他成员变量要对齐到某个数(对齐数)的整数倍的地址处
3.对齐数=编译器默认的一个对齐数(8)与该成员大小的较小值
4.结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍
5.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍对齐数的详细分析:「自定义类型」C语言中的构造数据类型如结构,联合,枚举-CSDN博客
this指针
在C++中,
this
指针是一个特殊的指针,它指向当前对象的地址。它是作为类的非静态成员函数的隐式参数传递给函数的,可以在成员函数中使用它来访问当前对象的成员变量和成员函数。事例
class Box { public:Box(double length,double width,double height){_length=length;_width=width;_height=height;}void volume(){cout<<_length*_width*_height<<endl;} private:double _length;double _width;double _height; }; int main() {Box box1(2,3,5);Box box2(1,2,3); }
c++为类的对象分配内存空间时只为对象的数据成员分配内存空间,而将对象的成员函数放在另外一个公共区域,同一个类的多个对象共享它们的成员函数。那么,同一个类的多个对象的成员函数在访问对象的数据成员时,怎么确保访问的是正确的数据成员呢?例如声明的长方体类Box,定义了两个对象box1和box2,对于调用“box1.volume()”,应该box1中的_height,_width和_length计算长方体box1的体积,对于“box2.volume()”,应该box2中的_height,_width和_length计算长方体box1的体积。现在box1和box2调用的都是同一段代码,系统是怎么区分应该访问box1的数据成员还是box2的数据成员呢?
其实在每一个成员函数中都包含了一个特殊的指针,这个指针的名字是固定的,称为this指针。this指针是指向本类对象的指针,它的指向是被调用成员函数所在的对象,即调用哪个对象的该成员函数,this指针就指向哪个对象。在成员函数内部访问数据成员的前面隐藏着this指针。如前面前面提到的Box类中的volume函数,其中的height*width*length实际等价于(this->_height)*(this->_width)*(this->_length)。如果调用box1对象的volume函数,则this指针就指向对象box1,所以(this->_height)*(this->_width)*(this->_length)就相当于(box1._height)*(box1._width)*(box1._length),这样就是计算的box1的体积。this指针的特性
1. this指针的类型:类类型* const(指针常量)
2. 只能在“成员函数”的内部使用
3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
this指针存在哪里?
其实编译器在生成程序时加入了获取对象首地址的相关代码。并把获取的首地址存放在了寄存器ECX中(vs2019编译器是放在ECX中,其它编译器有可能不同)。也就是成员函数的其它参数正常都是存放在栈中。而this指针参数则是存放在寄存器中。类的静态成员函数因为没有this指针这个参数,所以类的静态成员函数也就无法调用类的非静态成员变量。
this指针可以为空吗?
可以为空,当我们在调用函数的时候,如果函数内部并不需要使用到this,也就是不需要通过this指向当前对象并对其进行操作时才可以为空(当我们在其中什么都不放或者在里面随便打印一个字符串),如果调用的函数需要指向当前对象,并进行操作,则会发生错误(空指针解引用)
补充
下面的运行结果?1.编译报错 2.运行崩溃 3.正常运行
class A { public:void Print(){cout<<"void Print()"<<endl;} private:int _a; }; int main() {A* p=nullptr;p->Print();return 0; }
结果是正常运行,调用函数分为两步,第一步传参,第二步传函数的地址,成员函数的地址不存在对象里面,成员函数地址在公共代码区域,所以 p->Print();没有对p解引用,所以不会报错。
下面的运行结果?1.编译报错 2.运行崩溃 3.正常运行
class A { public:void Print(){cout<<_a<<endl;cout<<"void Print()"<<endl;} private:int _a; }; int main() {A* p=nullptr;p->Print();return 0; }
结果是运行崩溃,cout<<_a<<endl;这里会转化为 cout<<this->_a<<endl;this是指向p,所以这里对空指针解引用,所以运行崩溃
类里的默认生成函数
如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数
1.构造函数
2.析构函数
3.拷贝构造函数
4.赋值运算符的重载函数
5.取地址操作符的重载函数
6.const修饰的取地址操作符的重载函数
构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载
class Date { public://无参数的构造函数Date(){}//有参数的构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Display(){cout <<_year<< "-" <<_month << "-"<< _day <<endl;} private:int _year;int _month;int _day;}; int main() {//注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明Date d1;//调用无参的构造函数Date d2(2023,7,20);//调用带参数的构造函数return 0; }
5.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
class Date { public:void Display(){cout <<_year<< "-" <<_month << "-"<< _day <<endl;} private:int _year;int _month;int _day;}; int main() {Date d;return 0; }
6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。
class Date { public://无参数的构造函数Date(){}//全缺省参数的构造函数Date(int year=0, int month=0, int day=0){_year = year;_month = month;_day = day;}void Display(){cout <<_year<< "-" <<_month << "-"<< _day <<endl;} private:int _year;int _month;int _day;}; int main() {Date d1;Date d2(2023,7,20);return 0; }
这里编译会出错, Date d1;创建d1这个对象时,没有传递参数,但是构造函数一个是无参的,一个是全缺省的,编译器不知道调用哪一个,这里就是典型的二义性错误。
7.关于编译器生成的默认成员函数,很多人就会疑惑:在我们不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year,_month,_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有用?
C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如int/char…(所有的指针类型都是内置类型),自定义类型就是我们使用class/struct/union自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数
class Time { public:Time(int hour=0, int minute=0, int second=0){cout<<"Time(int hour=0, int minute=0, int second=0)"<<endl;_hour=hour;_minute=minute;_second=second;} private:int _hour;int _minute;int _second; }; class Date { private://基本类型(内置类型)int _year;int _month;int _day;//自定义类型Time _time1; }; int main() {Date d;return 0; } 结果: Time(int hour=0, int minute=0, int second=0)
析构函数
概念
前面通过构造函数的学习,我们知道一个对象时怎么来的,那一个对象又是怎么没呢的?
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作
特性
析构函数是特殊的成员函数。
其特征如下:
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
class Date { public:Date(int year=0, int month=0, int day=0){_year = year;_month = month;_day = day;}~Date(){cout<<"~Date()"<<endl;} private:int _year;int _month;int _day;}; int main() {Date d;return 0; } 结果: ~Date()
5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对会自定类型成员调用它的析构函数。
class Time { public:Time(int hour=0, int minute=0, int second=0){cout<<"Time(int hour=0, int minute=0, int second=0)"<<endl;_hour=hour;_minute=minute;_second=second;}~Time(){cout<<"~Time()"<<endl;} private:int _hour;int _minute;int _second; }; class Date { private://基本类型(内置类型)int _year;int _month;int _day;//自定义类型Time _time1; }; int main() {Date d;return 0; } 结果: Time(int hour=0, int minute=0, int second=0) ~Time()
设计析构函数的本意是对象生命周前结束前完成资源清理,比如,在C语言中写的栈,有个函数是完成栈的销毁,但是我们很容易忘记调用,在这里我们就可以把销毁的函数放在析构函数中,系统会自动调用。资源清理主要是清理系统不能执行的,清理动态内存申请的空间等。其实我们还可以利用析构函数会被自动调用的特性,完成一些打印的功能等。
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸