C C++最新【C++】类和对象1:初识类(2),2024京东C C++面试真题

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2.2访问限定符

class的默认访问权限是私有;struct默认为公有

你不知道什么是访问权限?那就继续往下看👇

我们可以用publicprivate这两个访问限定符来表明某一个具体类型的共有还是私有。它们的最大区别就是在这个类的外部能不能访问

image-20220519091246664

  • public成员可以在类外面直接使用
  • protected和private修饰的成员只能在类里面访问
  • 访问权限的作用域是从该访问限定符出现,到下一个访问限定符出现为止
  • 访问限定符是在编译过程中处理的,并不影响数据在内存上的存放

在前期学习的时候,我们可以认为protectedprivate是相同作用的

class Student{
public:
    //函数在类里面定义,编译器默认为内联函数
    void Print()
    {
        cout<<_name<<" "<<_sex<<" "<<_age<<" "<<_phone<<endl;
    }
    void Init(const char \* name,const char\*sex, int age,const char\* phone)
    {
        strcpy(_name,name);
        strcpy(_sex,sex);
        _age=age;
        strcpy(_phone,phone);
    }
    //函数在类里面声明
    void Delet();

private:
    //这个是对变量的声明
    //变量的声明:没有开辟空间
    //变量的定义:开辟了空间来存放内容
    char _name[20];
    char _sex[8];
    int _age;
    char _phone[20];
    //在创建对象的时候定义
};

  1. 数据和方法分装到一个类里面
  2. 想给你自用访问的(如函数接口)设置为共有
  3. 不想给你访问的(如通讯录每个用户的信息类型)设置为私有

这样可以做到,当函数定义修改之后,只要不修改函数声明,就不需要修改main函数中函数的调用!
这叫做:高内聚,低耦合

2.3封装

为什么会有权限的出现呢?我们可以细想一下下面这个场景:

假设我们编写了一个数组栈的代码,里面有一个top用来标明栈顶的元素位置。一般可以把top定义为0或者1,但是这两种方法的函数使用是不同的!如果有那个铁憨憨跑过来,非要把那个top的定义给改掉,那整个代码就废掉不能用辣!

设立私有和公有的初衷,就是为了避免这种情况。开放一些共有函数供类外面访问,这样对整个代码的访问会变得易于管理。当我们自己修改类里面的代码时,只需要做好处理,就不会影响类外面的函数调用(否则就是直接推翻重写了)

这便是我们常常提到的封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

3.类的实例化

用类类型创建对象的过程,称为类的实例化

  • 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
  • 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量

类就好比一个毛坯房,现在毛坯房建好了,要想它变得精致,我们还需要在main中调用这个类,不然毛坯房就要变成烂尾楼了

Student p1;
p1.Init("牛爷爷","男",58,"13251341680");
p1.Print(); 

4.计算类的大小

一般有4种类:包含函数和变量的、只包含函数的、只包含成员变量的、空类

QQ图片20220519093042

让我们来康康如何计算这些类的大小,解析见注释哦!

其实只需要记住,空类和只有函数的类会有1个字节的空间。计算类的空间的时候不会计算函数大小,成员变量的大小计算遵循结构体内存对齐的计算方法就行了!

#include<iostream>
using namespace std;

class A1{
public:
    void func1()
    {
        int ret=3;
        return ;
    }
};
class A2{
};

class A3{
public:
    char _a;
};

image-20220519095032249

5.this指针

5.1特点

当你用同样的图纸建了很多个屋子后,有没有想过应该如何区分它们呢?

C++在设计这部分的时候,添加了一个this指针来解决这个问题:

C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参 数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

  1. this指针的类型:类名* const
  2. 只能在“成员函数”的内部使用
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this 形参。所以对象中不存储this指针。
  4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

5.2显式使用this

就用下面这个函数举例

void Print()
{
	cout<<_name<<endl;
	cout<<_sex <<endl;
}

实际上,在调用它的时候,编译器会做如下处理

void Print(Student\*const this)
{
	cout<<this->_name<<endl;
	cout<<this->_sex <<endl;
}

因为只有这样,才能完整的区分两个不同的类


进一步看看下面这个代码,可以帮助你理解this指针

 bool operator==(const Date& d){
        return _year == d._year
            && _month== d._month
            && _day == d._day;
 }

这是一个日期的比较函数,是操作符重载(后面会讲到)

你可以看到,这个函数我们传入了一个Date类型的引用,这是区别于this的另外一个类的对象

如果没有this,那就很难区分两个变量的_year,于是编译器会把它优化成下面这样,就不会存在无法区分的问题了

 bool operator==(Date\*const this,const Date& d){
        return this->_year == d._year
            && this->_month== d._month
            && this->_day == d._day;
 }

5.3空指针问题

int main()
{
    int x=10;
    //在程序中,访问NULL不会报错,但是解引用Null会报错
    int\*a=NULL;
    int\*b=&x;

    //a=x;//err
    \*b=20;

    return 0;
}

#include<iostream>
using namespace std;

class ta{
public:
    void Print()
    {
        cout<<"print ta"<<endl;
        //cout<<\_a<<endl;
    }

private:
    int _a;
};
int main()
{
    ta\* p=nullptr;
    p->Print();
   //可以去访问空指针的函数
   //因为函数只是去调用了类里面的Print函数
   //同时传了一个p的this指针(空指针传参是不会报错的)
   //但是如果你去访问p里面\_a变量,就会报运行错误
    return 0;
}

6.类中成员函数名的处理

除了this指针之外,编译器在链接函数名的时候,也做了相应的处理。在Linux里面查看下面这串代码的汇编代码

#include<iostream>

class Test{
public:
    void func1(const int\* a1,int\* k,const int\*a2,char arr)
    {
        \*a1+\*a2;
        func2(a1,k,a2,arr);
    }
    
private:
    void func2(const int\* a1,int\* k,const int\*a2,char arr)
    {
        \*a1+\*a2;
    }
};

int main()
{
    Test t;
    int arr1[10];
    int arr2[20];
    int a=10;
    t.func1(arr1,&a,arr2,'a');
    

    return 0;
}

可以找到这两个类的成员函数的地址和函数名

方法参考我的这篇博客👉【末影门】

00000000004006da <_ZN4Test5func1EPKiPiS1_c>:

000000000040071a <_ZN4Test5func2EPKiPiS1_c>:

可以发现,这两个函数的地址不同,但函数名中并没有包含它的公有、私有信息。这也能证明2.2中写道的“访问限定符是在编译过程中处理的,并不影响数据在内存上的存放”

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

不同,但函数名中并没有包含它的公有、私有信息。这也能证明2.2中写道的“访问限定符是在编译过程中处理的,并不影响数据在内存上的存放”

[外链图片转存中…(img-DklcIzmo-1715723458698)]
[外链图片转存中…(img-Y97MQKAG-1715723458698)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值