面试总结一

1、基类的虚构函数为什么使用virtual?这种虚析构函数寻在的意义?
virtual虚函数是动态绑定的基础(这很重要)!在动态绑定的过程中,我们使用的是基类的指针指向子类(例如B派生于A,A *pA = new B();),如果没有使用virtual实现动态绑定,在进行delete A;时,不会调用B的析构函数,如果B的析构函数中写有释放内存的源码,此时可能就会造成内存的泄漏。
1)何为动态绑定与静态绑定?
举个示例:
动态绑定(pA可以灵活的指向继承于A的派生类):A *pA = new B();
静态绑定(pB只能指向B对象的地址空间):B *pB = new B();
使用virtual对一个成员函数进行什么声明后,会产生一个4字节的指向函数的指针(虚函数的实现原理可以查看这里https://blog.csdn.net/qq_30145355/article/details/78768950),子类继承于基类后,使用virtual声明的函数都指向同一个地址,这也是为什么析构函数要使用virtual的原因,因为这样在delete A的时候,会调用A中的析构函数地址,而B继承于A,且A的析构函数为虚析构函数,所以此时实际也会调用B的析构函数,避免了内存的泄漏问题。

2、以下的输出结果(考察函数重载与虚函数):

#include <iostream>
using namespace std;

class A
{
public:
    void FuncA()
    {
        printf("FuncA called\n");
    }
    virtual void FuncB()
    {
        printf("FuncB called\n");
    }
};
class B : public A
{
public:
    void FuncA()
    {
        A::FuncA();
        printf("FuncAB called\n");
    }
    virtual void FuncB()
    {
        printf("FuncBB called\n");
    }
};
void main(void)
{
    B  b;
    A  *pa;
    pa = &b;
    A *pa2 = new A;
    pa->FuncA(); 
    pa->FuncB();
    pa2->FuncA();
    pa2->FuncB();
    delete pa2;

    return;
}

答案:
FuncA called
FuncBB called
FuncA called
FuncB called

3、使用uint32来定义unsigned int:#define uint32 unsigned int
注意:写成#define uint32 (unsigned int)是错误的

4、sizeof与strlen的输出结果:6 0

char sz[6] = { 0 };
for (int i = 0; i < 5; i++)
{
    sz[i] = i;
}

cout << sizeof(sz) << endl;
cout << strlen(sz) << endl;

注:sizeof计算的是数组所占用的内存大小,包括结束的’\0’,所以一共占用6个字节(返回的是所占用的字节个数);strlen计算的是数组以’\0’结尾的长度,不包括’\0’,这里的sz是01234以及后面存有一个’\0’,但是第一个元素在内存中0x00,即’\0’,所以strlen为0

5、输出结果:2.19e+02

double  x;
x = 218.82631;
printf("%-6.2e\n", x);

注:
%:表示格式说明的起始符号,也是转义符号,有一题 printf(“%%%%”)输出几个?答案输出%% 两个
-:有-表示左对齐输出,如省略表示右对齐输出
0:有0表示指定空位填0,如省略表示指定空位不填
m.n m指域宽,即对应的输出项在输出设备上所占的字符数。
n指精度。用于说明输出的实型数的小数位数(此处为.19)。没有指定n时,隐含的精度为n=6位
e格式表示以指数形式输出实数
那么这题的意思是以左对齐、指数形式、总长度m =6、小数n=2两位 输出
对于域宽的补充:这里的域宽(总长度)为6,而2.19长度为4,6-4=2,还余两个长度,此时就在左边补两个空格把长度凑齐,但是我们这里使用的%-为左对齐,所以就没有写成( 2.19e+02)该方式。

6、const int *p、int const *p与int *const p
(1)const int *p与int const *p
表示*p的值不能改变(简单理解就是,const只能约束到它后面的对象,例如:const int *p,除去数据类型,可以看成 const *p,所以不难理解*p是不能改变的。再看int const *p,除去数据类型就是const *p,也是约束的是*p的值不能进行改变)
(2)int *const p
约束的是指针p的值(指向的地址)不能改变。(利用上面的理解方式,去除数据类型以及前面的符号*,剩下的是const p,约束的就是p的值)
(3)再看一个复杂的 const int *const p
先进行分解const (int *const p),把括号里面的看成一个整体,去除数据类型后 const (*const p),即*p的值不能进行改变,再分析括号里面,不能得出p的值也不能进行改变。综上所述,p所指向的对象及对象的值均不能改变。

7、sizeof(B):12

class A
{
public:
    void *p1;
private:
    void *p2;
protected:
    void *p3;
};
class B: public A {};

8、malloc申请的是虚拟内存,不是物理内存
malloc的内存分配再执行阶段

9、如果友元函数重载一个运算符时,运算符至少有一个参数

10、如果基类时抽象类,则子类需要实现基类的纯虚函数才能实例化。如果不实现基类的纯虚函数,也不会出现编译错误,只是子类也不能进行实例化。
虚基类:指虚继承的基类,主要用于解决从不同路径多次继承同一个基类的问题。

11、mutable(中文意思:可变的、易变的)关键词
作用:为了突破const的限制而设置。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中
例如有个成员函数:

int GetValue() const{
    mutable vv = 1;// 如此vv便能拜托const的限制
    return vv;
}

12、设x、y、t均为int型变量,则执行语句:t=3; x=y=2; t=x++||++y; 后,变量t和y的值分别为1__2
注:使用||操作符时,前面的x++为真后就不用再计算后面的++y,所以这里的y是2

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值