带你初步了解 类和对象 的基本机制(下)

简介: 带你初步了解 类和对象 的基本机制(下)

计算类的大小:

大家觉得我们以下的代码,计算stu的大小是多大呢?

class stu
{
public:
    void test1(int a)
    {  
        _a = a;      
    }
    int test2()
    {
        ;
    }
    int _a;
    char _c;
    int _b;
};
int main()
{
    stu tmp;
    tmp.test1(10);
    tmp._b=10;
}


内存对齐?

答案当然是 12 至于为什么大家可以去看这篇博客力的内存对齐:

C语言进阶学习日志:自定义类型 (细中细)_luck++的博客-CSDN博客    


   可是我们这里不是还写得有函数嘛 为什么只算了成员变量的大小呢

   首先大家这样想:如果我们每个对象都保存一份代码,相同的代码被保存多次,这又是何必呢?

  所以有了只保存成员变量,成员函数放在公共的代码段(操作系统的角度来看,也就是内存中的代码段)调用的时候,也就是去公共代码段去找


如果是这样的类,大家觉得类的大小又会是多大呢?

1. class stu
2. {
3. public:
4. void test() {}
5. };
6. 
7. class s1
8. {
9. };

他们最终的答案都是 1 ,首先这里不可能是0,我们打印他们定义出来的对象的地址,是可以打印出来的,这里的 1 也不是用来存储有效地址的,这个是为了表示我们的对象存在过


this指针:

我们的成员函数中是存在又一个隐含的this指针的

class stu
{
public:
    stu(int a,int c,int b)
    {    
        _a=a;
        _c=c;
        _b=b;
    }
    void test1()
    {  
        cout<<_a<<"-"<<_c<<"-"<<_b<<endl;
    }
private:
    int _a;
    char _c;
    int _b;
};
int main()
{
    stu s1(10,'A',20);
    stu s2(30,'B',40);
    s1.test1();
    s2.test1();
}


这里的stu是一个构造函数,通过这样的调用来完成 s1和s2 的定义和初始化,无论是这里的构造函数,还是它下面的test1函数,他们都是存放在公共代码段的,我们调用都是同样的函数,那他是怎么样来区分到底是 s1 调用还是 s2调用呢

0d96d3f0a7d449c4bf7647f21031cc04.png


大家看这张图就可以看到,我们的函数都是调用的一样的,我们的上面方括号里面的内容确是不一样的,其实我们调用的时候,函数本样是这样的:

class stu
{
public:
    stu(stu* this,int a,int c,int b)
    {    
        this->_a=a;
        this->_c=c;
        this->_b=b;
    }
    void test1(stu* this)
    {  
        cout<<this->_a<<"-"<<this->_c<<"-"<<this->_b<<endl;
    }
private:
    int _a;
    char _c;
    int _b;
};
int main()
{
    stu s1(&s1,10,'A',20);
    stu s2(&s2,30,'B',40);
    s1.test1(&s1);
    s2.test1(&s2);
}


我们是通过传一个对象的地址,来通过地址来完成对象内容的改变与访问

  不知道大家会不会有个这样的疑问,之前我学习这里就是这样理解错误了,那个时候我觉得我去改变为什么一定要有一个this指针呢,我这里不是有那些成员变量嘛,这些成员变量不都是对象自己的嘛,直接去访问赋值不就好了嘛

当然这样肯定是不正确的,我们是不能这样去实现对象的赋值与访问的,我们那些成员变量都只是声明,声明我们这个类里面是有这个变量的,真正要访问它,是必须通过定义对象,通过一个对象去访问它的,所以我们这个隐含的this指针是必不可少的


还有一个点大家需要注意:我们实参、形参的位置是不能这样显示去写的(因为这个是隐含的,是编译器去实现的),但是我们在函数的内部是可以显示的写的 我们的this指针也是被const修饰的,自身不能被改变,指向的对象是可以改变的


this指针可以为0嘛?

就这个问题大家看了这段代码就知道了:

class stu
{
public:
    stu(int a,int c,int b)
    {    
        _a=a;
        _c=c;
        _b=b;
    }
    void test1()
    {  
        cout<<_a<<"-"<<_c<<"-"<<_b<<endl;
    }
    void test2()
    {
        cout<<"Yes"<<endl;
    }
private:
    int _a;
    char _c;
    int _b;
};
int main()
{
    stu* p =nullptr;
    p->test2();
}


大家觉得这段代码运行有问题嘛?

   如果拿去试过就知道了,这段代码是没有问题的,可我这里的p不是空嘛,去访问空指针里面的内容,不是会运行崩溃嘛,那我这样问:我的成员函数是在对象里面的嘛,他们不是存放在公共代码段嘛,我们在编译的时候,如果函数的篇幅较长,这里的调用就会转换成 去 call test2 的指令了,较短的话,就是直接展开了(因为内联) 就不存在访问空指针的问题了

  当然如果这里的成员函数是公有的,访问他们就不行了,这里的 test2函数 依旧也是存在隐含的this指针的,这里的this指针就是空了(我们这里也是把p的地址传过去了的)

617ad3c5b20e4932b687d94afb26dfc3.png

 最后给大家提一个东西,我们的this指针按理来讲是存在栈上面的,但通常编译器优化后会放到寄存器里面,上面那图,call指令上面的指令就是,把 p 存到了 ecx 寄存器里面去了

目录
相关文章
|
11月前
|
搜索推荐 Java
【JavaSE专栏59】方法重写的概念及优先级问题,面向对象的多态性机制
【JavaSE专栏59】方法重写的概念及优先级问题,面向对象的多态性机制
|
4月前
|
存储 编译器 C语言
【C++类和对象】类和对象的引入(下)
【C++类和对象】类和对象的引入
|
4月前
|
Serverless PHP
当谈论面向对象编程时,这四个概念是非常重要的。以下是对接口、继承、封装和多态的简要说明
本文介绍了面向对象编程的四个核心概念:接口、继承、封装和多态。接口定义对象的行为规范,类通过实现接口确保符合所需行为。继承允许在已有类基础上创建新类,实现代码重用。封装是将数据和操作捆绑并隐藏内部细节,提高安全性和可维护性。多态使对象在运行时能表现出不同行为,增加代码灵活性和可扩展性。文中还提供了代码示例来说明这些概念。
27 0
|
4月前
掌握面向对象程序设计继承和派生机制的概念
掌握面向对象程序设计继承和派生机制的概念
34 0
|
4月前
|
Java
Java面向对象编程,解释封装、继承和多态的概念。
Java面向对象编程,解释封装、继承和多态的概念。
64 2
|
9月前
|
存储 编译器 C++
C++中的多态机制
C++中的多态机制
|
11月前
|
存储 算法 Java
面向对象编程实践:类、对象与继承
面向对象编程实践:类、对象与继承
49 0
|
12月前
|
存储 数据安全/隐私保护 Python
面向对象编程(OOP):理解类、封装性的关键概念
面向对象编程(OOP):理解类、封装性的关键概念
127 0
多态性概念及案例
多态性概念及案例
51 0
|
编译器 C语言 C++
带你初步了解 类和对象 的基本机制(上)
带你初步了解 类和对象 的基本机制
38 0