小谈类机制相关

小谈类机制相关

本文主要涉及类相关的一些常见面试问题,以及相关特性。
包括 this 指针、拷贝构造函数相关以及类机制。

一、this指针

编译器在编译普通成员函数时,会隐式的分配一个形参指针,即this指针。并且当实例化对象调用该成员函数时,会把当前对象的地址赋值给this指针

1、this指针是常量指针

**this指针是常量指针,指向不可改变。**当使用const 关键字修饰成员函数时,该指针即是常量指针,又是指针常量,指向和指向的值都不可改变。

2、this 指针只存在于普通成员函数中

this指针是编译器为普通成员函数分配的隐式形参,对于静态成员函数和友元函数来说,都是没有 this指针的,因此一般双目运算符的重载都使用全局函数做友元的形式。
另外我们知道,返回局部变量的地址和引用会造成不可估计后果。但是在拷贝构造函数等成员函数中都会返回this指针的解引用,因为其是传入的有效形参,是有明确地址的。

3、成员函数中delete this 会发生什么?

1)禁止在析构函数中使用 delete this。因为delete this 会不断的调用本对象的析构函数,而析构函数中又会调用delete this,形成无线递归,从而造成堆栈溢出。
2)在普通成员函数中调用 delete this, 需要区分是栈对象和堆对象。因为delete this会执行析构函数,并释放内存。那么对于堆对象来说会执行析构,并且释放内存,那么当再次访问时就会出现内存问题;对于栈对象来说会执行析构,但是不会释放内存,那么再次访问也会出现内存错误。

二、拷贝构造函数

定义一个类实际就是声明一个组合类型,而构造函数就是初始化(成员变量赋值)的过程。拷贝构造函数就是根据一个现有的对象作为基础去创建一个新的对象。

1、为什么拷贝构造函数中形参一般使用const + &?

1)使用 & 是为了避免递归调用拷贝构造函数,从而造成堆栈溢出
2)使用const 是因为形成 常量左值引用,可以支持传入左值、右值、常量左值和常量右值,相当于扩大了可传入参数的类型范围

2、深浅拷贝与移动语义

1)浅拷贝。
两个指针指向同一块内存,容易造成 内存的重复释放问题。
2)深拷贝。
重新申请一块内存,然后再进行赋值操作。不会造成内存的重复释放,但是存在内存开销问题。
3)移动构造函数
主要是应用于临时变量的拷贝,减少不必要的内存开销问题。

3、拷贝构造函数和赋值操作符区别

1)拷贝构造函数是构造函数,而赋值操作符重载是成员函数
2)另外就是拷贝构造函数是去完成对未初始化的存储区的初始化,而赋值操作符是处理一个已经存在的对象。

三、类机制

1、class 和 struct 的区别

在C中struct中是不能存放函数的,如果想要实现封装、继承等特性需要使用函数指针。
在C++中class 和 struct 都是可以实现继承和多态的。区别是默认的访问权限不一样。

2、类在什么情况下不能被继承?

1)使用 final 关键字
2)构造函数私有化
3)虚继承 + 友元

3、当发生多种继承时类的构造顺序(虚继承、抽象类继承、普通继承)

静态成员> 虚基类> 抽象类> 基类 > 成员变量(按照声明顺序)> 派生类

4、类的大小

1)字节对齐
2)虚函数(虚函数表指针占用 8个字节)
3)虚继承(虚基类表指针占用8个字节)
4)空类的大小为1个字节。主要是用于区分不同的对象。

5、如何限制只能在栈上创建对象?

C++只有使用new 关键字,对象才会建立在堆上。并且C++允许重载operator new 运算符,那么只要讲operator new 运算符进行重载并私有化即可。

6、如何限制只能在堆上创建对象?

类对象只能建立在堆上,就是不能静态建立类对象,即不能直接调用类的构造函数。 容易想到将构造函数设为私有。在构造函数私有之后,无法在类外部调用构造函数来构造类对象,只能使用new运算符来建立对象。然而,前面已经说过,new运算符的执行过程分为两步,调用new 时仍然需要调用构造函数。
当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。编译器管理了对象的整个生命周期。如果编译器无法调用类的析构函数,情况会是怎样的呢?比如,类的析构函数是私有的,编译器无法调用析构函数来释放内存。所以,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。

7、如何判断类/结构体对象是否相等? 能否使用memcmp函数进行逐字符判断

1)判断类/结构体对象是否相等需要重载比较运算符(==)。
2)不可以使用memcmp函数进行比较。因为memcmp是进行逐字节比较的,而结构体在保存时进行了字节对齐,而在字节对齐的过程中的填充是随机的,因此无法直接进行比较。

四、成员初始化列表

采用初始化列表,成员初始化的顺序就是根据变量声明的顺序。

1、为什么使用成员初始化列表要比在构造函数中初始化要快一点?

严格来说,在构造函数中执行的是赋值操作,并不是初始化操作;因为在对象的构造顺序中,成员的初始化要比构造函数的初始化早;
那么如果使用构造函数对自定义成员对象进行赋值会执行两步(默认构造函数 + 拷贝赋值), 而如果使用成员初始化列表,则不会执行默认构造函数,而是直接根据初始化列表中传入的参数进行相应的成员构造。减少了不必要的开销。

2、三种情况必须使用成员初始化列表

1)引用类型成员(初始化时必须要进行赋值)
2)常量类型成员(初始化后不能被赋值,只能在初始化时赋值)
3)没有默认构造函数的成员类型(比如只重载了默认构造函数,那么该成员就没有默认的无参构造函数可以调用,就会出错)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值