C++ 类与对象(二)—类成员初始化、静态分配和动态分配、this指针

类成员初始化

包括赋值初始化和列表初始化

赋值初始化

在函数体内复制初始化,这是会产生临时对象的。
在所有数据成员被分配内存空间时之后才进行的。

列表初始化

冒号之后使用初始化列表进行初始化,就是纯粹的初始化操作。
列表初始化时给数据成员分配内存空间的时候就进行初始化,也就是分配了内存空间之后进入函数体之前给数据成员赋值,也就是说初始化数据成员的时候函数体还没有执行

为什么列表初始化快一些?

少了一次调用构造函数的过程,而在函数体中赋值多了一次默认构造函数的调用过程。如果是在构造函数体内进行赋值,相当于是一次默认构造函数加一次赋值,而初始化列表只做一次赋值。

哪些情况必须使用列表初始化?

1、初始化一个引用成员
2、初始化一个常量成员
3、调用一个基类的构造函数,而它拥有一组参数的时候
4、调用一个成员类的构造函数,而它拥有一组参数的时候

初始化列表做了什么?

  • 编译器一一操作初始化列表,以适当的顺序在构造函数之内安插初始化操作,并且在任何显示用户代码之前
  • 列表中的项目顺序是由类中成员声明顺序决定的,不是由初始化列表顺序决定的。

派生类构造函数执行顺序

  1. 虚拟基类的构造函数(如果有多个虚拟基类则按照继承的顺序执行构造函数)
  2. 基类构造函数(如果有多个普通基类也按照继承的顺序执行构造函数)
  3. 类类型的成员对象的构造函数(按照初始化顺序)
  4. 派生类自己的构造函数

类如何实现只能静态分配和dongtaifenpe

静态分配

静态建立一个类对象,就是由编译器为对象在栈空间中分配内存。静态分配可以使代码更简单,因为不需要显式释放内存,对象的生命周期由编译器自动管理。但是,静态分配的对象无法在运行的时候改变大小或释放。而且如果对象生命周期比当前作用域范围长,可能导致过早释放或者内存泄漏。

动态分配

A*p =new A()
动态建立一个类对象,就是使用new运算符在堆空间中分配内存。这个过程分为两步:

  • 执行operator new()函数,在堆中搜索一块内存分配
  • 调用类构造函数
    动态分配可以更灵活地管理生命周期,可以在运行时动态地创建、销毁、大小调整对象。但是需要显式释放内存,否则可能导致内存泄漏。动态分配的对象的创建和销毁可能会影响程序性能。

只使用静态分配

限制new,delete为private类型

只使用动态分配

  • 将构造、析构函数设置为protected属性,再用子类来动态创建
  • 禁用、删除默认构造函数、拷贝函数
  • 提供一个只能通过动态分配获取对象的静态成员函数

this指针

C++中,this是一个特殊指针,指向当前正在执行的对象,可以将this指针视作当前对象的应用,可以在类的成员函数中使用。

this指针使用场景

类的成员变量与函数参数同名

class MyClass {
public:
    void setX(int x) {
        this->x = x;
    }
private:
    int x;
};

在一个类的成员函数中,需要访问类的其它成员变量或者成员函数

class MyClass {
public:
    void print() {
        cout << "x = " << this->x << endl;
        cout << "y = " << this->y << endl;
    }
private:
    int x;
    int y;
};

需要返回当前对象的引用

class MyClass {
public:
    MyClass& operator=(const MyClass& other) {
        this->x = other.x;
        this->y = other.y;
        return *this;
    }
private:
    int x;
    int y;
};

  • this指针只能在非静态成员函数中使用,因为静态成员函数不属于任何对象,不存在当前对象的概念。此外,this指针的类型是指向当前对象的指针,类型为指向类类型的指针。
  • this指向对象的首地址;只能在成员函数中使用。在全局函数、静态成员函数中都不能使用
  • 存储位置会因为编译器不同而有不同存储位置

this指针用处

一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。及时你没有写this指针,编译器在编译的时候也是加上了this指针的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行

this特点

  • 传入参数为当前对象的地址,成员函数第一个参数为T*const this。this在成员函数开始之前构造,在成员函数结束之后清除(如果class或者struct里面没有方法,那是没有构造函数的,只能当作C语言的struct来使用)
  • 如果采用TYPE XX的方式定义,就是在栈分配内存,this指向这块内存的值
    采用new分配内存的话,就是在堆上分配内存,new操作符通过eax累加寄存器返回分配的地址,然后设置给指针变量。之后去调用构造函数(如果有的话),这时将这个内存块的地址传给ecx(计数寄存器存储参数和一些局部变量,this指针)。
  • C++编译器有可能会对this指针进行一些优化

this指针一些常见的优化

  • 空对象优化(Empty-Base Optimization,EBO):当类没有任何成员变量时,编译器会将空对象的大小设为1,以便确保每个对象都有一个唯一的地址。这种优化可以避免使用this指针,因为空对象不需要任何额外的存储空间。

  • 寄存器调用(Register Calling Convention):如果函数参数太多,将它们传递到堆栈上可能会导致性能下降。因此,编译器可能会使用寄存器来传递this指针,从而减少访问堆栈的次数。

  • 内联函数(Inline Functions):编译器可能会将类的成员函数内联,以避免使用this指针和函数调用的开销。在内联函数中,this指针通常被编译器优化成一个常量

  • 基于指针的虚函数调用(Pointer-Based Virtual Function Calls):如果类有虚函数,编译器可能会将函数调用优化为指针调用,以避免使用this指针。在这种情况下,编译器会生成一个指向虚函数表的指针,用于查找正确的虚函数实现。
    具体优化策略会因为编译器版本、编译器选项等因素而异,所以编写程序时最好不要依赖于这些优化。

this指针存放位置

this指针会因为编译器不同而存放在不同位置。可能是栈、可能是寄存器、甚至是全局变量。在汇编里面,一个值只会以三种形式出现:立即数、寄存器值、内存变量值。不是存放在寄存器就是存放在内存中。

每个类编译之后,是否创建一个类中函数表来保存函数指针?

普通的类函数,都不会创建一个函数表来保存函数指针,只有虚函数才会被放到函数表中。正是由于this指针的存在,用来指向不同的对象,从而确保不同对象之间调用的函数可以互不干扰。

成员函数中调用delete this会出现什么问题

类对象内存空间被释放,之后其它任何函数调用,只要不涉及this指针内容,都可以正常运行,一旦涉及到this指针内容,比如操作数据成员,调用虚函数等都会出现不可预期的问题

为什么是不可预期的问题

因为这个时候内存空间暂时没有被系统收回,这段内存是可以访问的,但是里面的值是不能确定的。造成系统崩溃

在类的析构函数调用delete this会发生什么问题

堆栈溢出
delete的本质是为释放的内存调用一个或者多个析构函数,然后释放内存。delete this 会显式调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃。

this指针调用成员变量的时候,堆栈会发生什么变化?

当在类的非静态成员函数访问类的非静态成员时,编译器会自动将对象的地址传给作为隐含参数传递给函数,这个隐含参数就是this指针。
当建立类的多个对象时,在调用类的成员函数时,不知道具体是哪个对象在调用,此时可以通过查看this 指针来查看具体是哪个对象在调用。this指针首先入栈,然后成员函数的参数从右往左入栈,最后函数返回地址入栈

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值