类和对象、this指针、类里的默认生成函数

news2024/10/6 22:22:52

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,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语言中写的栈,有个函数是完成栈的销毁,但是我们很容易忘记调用,在这里我们就可以把销毁的函数放在析构函数中,系统会自动调用。资源清理主要是清理系统不能执行的,清理动态内存申请的空间等。其实我们还可以利用析构函数会被自动调用的特性,完成一些打印的功能等。

  🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1653188.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索数据结构

什么是数据结构 数据结构是由&#xff1a;“数据”与“结构”两部分组成 数据与结构 数据&#xff1a;如我们所看见的广告、图片、视频等&#xff0c;常见的数值&#xff0c;教务系统里的&#xff08;姓名、性别、学号、学历等等&#xff09;&#xff1b; 结构&#xff1a;当…

C++ 继承篇

面向对象语言的三大特性&#xff1a;封装&#xff0c;继承和多态 根据目前学到的知识&#xff0c;对于封装的理解&#xff0c;大致有两层&#xff1a; 将数据和方法封装&#xff0c;不想让外面看到用private/protected修饰&#xff0c;想让外面看到用public修饰类型的行为不满…

Intel® Platform Firmware Resilience (Intel® PFR):英特尔® 平台固件恢复力(Intel® PFR)

为了降低与固件相关的安全风险&#xff0c;英特尔为服务器平台开发了英特尔平台固件恢复力&#xff08;Intel PFR&#xff09;。 此功能可保护关键固件在启动和运行时免受攻击。这可以被视为是 Cerberus 项目或 NIST SP800-193 的实现。 英特尔平台固件恢复力&#xff08;Int…

SQL 基础 | JOIN 操作介绍

在SQL中&#xff0c;JOIN是一种强大的功能&#xff0c;用于将两个或多个表中的行结合起来&#xff0c;基于相关的列之间的关系。 JOIN操作通常用在SELECT语句中&#xff0c;以便从多个表中检索数据。 以下是几种基本的JOIN类型以及它们的用法&#xff1a; INNER JOIN&#xff1…

探秘编程之旅:Baidu Comate 智能代码助手的魔法揭秘

目录 Baidu Comate智能代码助手1.场景需求2.安装步骤3.功能介绍3.1 /指令3.2 插件3.3 #知识 4.使用体验5.总结 Baidu Comate智能代码助手 智能编程助手的意义在于提升编程体验和效率&#xff0c;使开发人员能够更轻松、更快速地完成编码任务&#xff0c;是如今人工智能技术的一…

2024年颠覆商业模式《本草生活》项目,巧妙三招营销引流裂变套路

2024年颠覆商业模式《本草生活》项目&#xff0c;巧妙三招营销引流裂变套路 文丨微三云营销总监胡佳东&#xff0c;点击上方“关注”&#xff0c;为你分享市场商业模式电商干货。 - 引言&#xff1a;现如今流量枯竭、降本增效、红利不再已是线上营销的常态&#xff0c;互联网…

中金:如何把握不断轮动的资产“风口”

从比特币到日股&#xff0c;到黄金与铜再到当前的港股&#xff0c;每次超预期大涨后都透支回调。 今年以来资产的“风口”不断轮动&#xff0c;从比特币到日股&#xff0c;到黄金与铜&#xff0c;再到当前的港股&#xff0c;资产仿佛“接力”般交替领先&#xff0c;同时“风口”…

【北京迅为】《iTOP-3588开发板快速烧写手册》-第10章 多设备量产升级固件

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

分享6个免费下载电子书的网站

着急看书的宝子们看这里&#xff01; 收藏了一堆电子书网站终于能派上用场了~ 01/Z-Library https://zh.zlibrary-be.se/ 世界上最大的电子图书馆&#xff0c;拥有超千万的书籍和文章资源&#xff0c;99%的书籍资料都能在这里找到。 我给的这个网址现在还能正常打开使用&…

vue+sortablejs来实现列表拖拽——sortablejs的使用

sortablejs官网:https://sortablejs.com/ 最近在看form-builder组件&#xff0c;发现里面有用到sortablejs插件&#xff0c;用于实现拖拽效果。 但是这个官网中的配置&#xff0c;实在是看不懂&#xff0c;太简单又太复杂&#xff0c;不实用。 下面记录一下我的使用&#xff…

sklearn的make_blobs函数

make_blobs是一个用于生成随机数据点的实用函数&#xff0c; from sklearn.datasets import make_blobs X,Y make_blobs(n_samples2000,n_features2,centers12,cluster_std0.05,center_box[-5,5],random_state21)n_samples: 要生成的样本数量。centers: 要生成的簇&#xff0…

JetsonNano —— Windows下对Nano板卡烧录刷机(官方教程)

介绍 NVIDIA Jetson Nano™ 开发者套件是一款面向创客、学习者和开发人员的小型 AI 计算机。按照这个简短的指南&#xff0c;你就可以开始构建实用的 AI 应用程序、酷炫的 AI 机器人等了。 烧录刷机 1、下载 Jetson Nano开发者套件SD卡映像&#xff0c;并记下它在计算机上的保存…

docker部署小试

一 1.1 需求&#xff1a;根据docker部署nginx并且实现https 1.2 前期准备 准备一台装备好的docker-ce虚拟机&#xff0c;容量至少满足4G/2C&#xff0c;同时做好关闭防火墙的操作 systemctl stop firewalld setenforce 0 1.3 实验部署 1.3.1 创建并进入文件夹 1.3.2 编辑run脚本…

XSKY SDS 6.4 重磅更新:NFS 性能飙升 3 倍,对象多站点等 10 多项功能强势升级

近日&#xff0c;XSKY星辰天合发布了 XSKY SDS V6.4 新版本&#xff0c;该版本在文件的性能提升、对象容灾能力完善方面改进异常显著&#xff0c;同时也大幅提高了存储系统的安全特性&#xff0c;适配更多的信创软硬件生态。 近来&#xff0c;软件定义存储&#xff08;SDS&…

pymysql用法整理--python实现mysql数据库操作

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文着重整理pymsql的常用方法 不专门讲解MySQL数据库的相关知识 常用基本语法汇总 import pymysql#连接数据库 connpymysql.connect(host127.0.0.1,port3306,userroot,password123456,charsetutf8,db"expe…

洛谷 P4148:简单题 ← KD-Tree模板题

【题目来源】https://www.luogu.com.cn/problem/P4148【题目描述】 你有一个 NN 的棋盘&#xff0c;每个格子内有一个整数&#xff0c;初始时的时候全部为 0&#xff0c;现在需要维护两种操作&#xff1a; ● 1 x y A → 1≤x,y≤N&#xff0c;A 是正整数。将格子 (x,y) 里的数…

韩国站群服务器提供高级安全防护以确保数据和网站的安全性

韩国站群服务器提供高级安全防护以确保数据和网站的安全性 在当今数字化时代&#xff0c;网站的安全性和数据保护已成为企业和个人不可忽视的重要议题。韩国站群服务器作为一个拥有发达科技和互联网基础设施的国家&#xff0c;通过提供高级安全防护措施&#xff0c;为用户的数…

安卓surfaceview的使用方式

1. 什么是surfaceview surfaceview内部机制和外部层次结构 在安卓开发中&#xff0c;我们经常会遇到一些需要高性能、高帧率、高画质的应用场景&#xff0c;例如视频播放、游戏开发、相机预览等。这些场景中&#xff0c;我们需要直接操作图像数据&#xff0c;并且实时地显示到…

后端常用技能:解决java项目前后端传输数据中文出现乱码、问号问题

0. 问题背景 最近做一个解析数据的小工具&#xff0c;本地运行时都正常&#xff0c;发布到服务器上后在导出文件数据时发现中文全部变成了问号&#xff0c;特此记录下问题解决的思路和过程 1. 环境 java 1.8 springboot 2.6.13 额外引入了fastjson&#xff0c;commons-csv等…

##07 从线性回归到神经网络:PyTorch实战解析

文章目录 前言线性回归基础理论背景实现步骤神经网络介绍理论背景实现步骤从线性回归到神经网络结论前言 在深度学习的浩瀚宇宙中,线性回归和神经网络是两个基本但极其重要的概念。线性回归模型是统计学和机器学习的基石之一,而神经网络则是深度学习技术的核心。本文旨在通过…