Bootstrap

嵌入式面试题 C/C++常见面试题整理_7

一.什么函数不能声明为虚函数?

常见的不能声明为虚函数的有:普通函数(非成员函数):静态成员函数;内联成员函数;构造函数;友元函数。

1.为什么C++不支持普通函数为虚函数?普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。

2.为什么C++不支持构造函数为虚函数?

因为构造函数本来就是为了明确初始化对象成员才产生的,虚函数表的指针是在构造函数中初始化的,,主要是从语义上考虑,所以不支持。

3.为什么C++不支持内联成员函数为虚函数?

其实很简单,那内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(再说了,inline函数在编译时被展开,虚函数在运行时才能动态的邦定函数)。

4.为什么C++不支持静态成员函数为虛函数?这也很简单,静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态邦定的必要性。

5.为什么C++不支持友元函数为虚函数?
因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。

二.纯虚函数指的是什么?

纯虚函数是一种特殊的虚函数,格式一般如下

由于在很多情况下,基类中不能对虚函数给出有意义的实现,只能把函数的实现留给派生类。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但是动物本身生成对象不合情理,此时就可以将动物类中的函数定义为纯虚函数,如果基类中有纯虚函数,那么在子类中必须实现这个纯虚函数,否则子类将无法被实例化,也无法实现多态。
含有纯虚函数的类称为抽象类,抽象类不能生成对象。纯虚函数永远不会被调用,它们主要用来统一管理子类对象。

三.基类的构造函数/析构函数是否能被派生类继承?

基类的构造函数析构函数不能被派生类继承。

基类的构造函数不能被派生类继承,派生类中需要声明自己的构造函数。设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员初始化,

也要考虑基类的数据成员的初始化。声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化,需要调用基类构造函数完成。

基类的析构函数也不能被派生类继承,派生类需要自行声明析构函数。声明方法与一般(无继承关系时)类的析构函数相同,不需要显式地调用基类的析构函数,

系统会自动隐式调用。需要注意的是,析构函数的调用次序与构造函数相反。

四.初始化列表和构造函数初始化的区别?

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:

Example::Example():ival(0),dva1(0.0){} //ival(0) 和dva1是类的两个数据成员

上面的例子和下面不用初始化列表的构造函数看似没什么区别:

Example::Example{
    ival = 0;
    dva7 =0.0;
}

的确,这两个构造函数的结果是一样的。但区别在于:上面的构造函数(使用初始化列表的构造函数显示的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显示的初始化。

初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。但有的时候必须用带有初始化列表的构造函数:

1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。

2.const成员或引用类型的员。因为const对象或引用类型只能初始化,不能对他们赋值.

五.C++中有那些情况只能用初始化列表,而不能用赋值?

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面都跟一个放在括号中的初始化式。

例如,Example:Example ival(o,dva(0.0),其中ival与dva是类的两个数据成员。

在C++语言中,赋值与初始化列表的原理不一样,赋值是删除原值,赋予新值,初始化列表开辟空间和初始化是同时完成的,直接给予一个值.

所以,在C++中,赋值与初始化列表的使用情况也不一样,只能用初始化列表,而不能用赋值的情况-般有以下3种:

1.当类中含有 const(常量)、reference(引用)成员变量时,只能初始化,不能对它们进行赋值。常量不能被赋值,只能被初始化,

所以必须在初始化列表中完成,C++的引用也一定要初始化,所以必须在初始化列表中完成。

2.派生类在构造函数中要对自身成员初始化,也要对继承过来的基类成员进行初始化当基类没有默认构造函数的时候,

通过在派生类的构造函数初始化列表中调用基类的构造函数实现。

3.如果成员类型是没有默认构造函数的类,也只能使用初始化列表。若没有提供显式初始化时,

则编译器隐式使用成员类型的默认构造函数,此时编译器尝试使用默认构造函数将会失败.

六.构造函数没有返回值,那么如何得知对象是否构造成功?

这里的“构造”不单指分配对象本身的内存,而是指在建立对象时做的初始化操作(如打开文件、连接数据库等)

因为构造函数没有返回值,所以通知对象的构造失败的唯一方法就是在构造函数中抛出异常。

构造函数中抛出异常将导致对象的析构函数不被执行,当对象发生部分构造时,已经构造完毕的子对象将会逆序地被析构。

七.C语言宏中的#和##的区别

区别

“#”:将其后面的宏参数进行字符串化操作。

“##”:在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。

例子一:

#include <stdio.h>
#define paster( n ) printf( "token" "#n" " = %d", token##n )
int main()
{
    int token9 = 10;
    paster(9);
    return 0;
}

paster(9)宏展开结果:

printf("token9 = %d", token9);

输出结果:

token9 = 10

例子二:

#define hehe(x,y) x##y
int main()
{
    char string[] = "hello world";
    printf("%s\n", hehe(str, ing));
    return 0;
}

输出结果:

hello world

八.在C语言中,为什么 static变量只初始化一次?

对于所有的对象(不仅仅是静态对象),初始化都只有一次,而由于静态变量具有“记忆”功能,初始化后,一直都没有被销毁,都会保存在内存区域中,所以不会再次初始化。存放在静态区的变量的生命周期一般比较长,

它与整个程序“同生死、共存亡",所以它只需初始化一次。而auto变量,即自动变量由于它存放在栈区,一旦函数调用结束,就会立刻被销毁。

九.函数指针和指针函数的区别

函数指针(Function Pointer): 函数指针是指向函数的指针。通过函数指针,可以间接调用函数,这在实现回调机制和动态链接时非常有用。

指针函数(Pointer Function): 指针函数是返回类型为指针的函数。也就是说,该函数会返回一个指针,而这个指针可以指向某种类型的数据。

函数指针示例:

#include <stdio.h>
 
// 定义两个简单的加法和乘法函数
int add(int a, int b) 
{
    return a + b;
}
 
int multiply(int a, int b) 
{
    return a * b;
}
 
int main() 
{
    // 定义一个指向函数的指针
    int (*operation)(int, int);
 
    // 将指针指向加法函数
    operation = add;
    printf("Addition: %d\n", operation(5, 3)); // 输出:Addition: 8
 
    // 将指针指向乘法函数
    operation = multiply;
    printf("Multiplication: %d\n", operation(5, 3)); // 输出:Multiplication: 15
 
    return 0;
}

指针函数示例:

#include <stdio.h>
#include <stdlib.h>
// 定义一个函数,返回指向int的指针
int* getArray(int size) 
{
    // 在堆上分配内存
    int *array = (int*)malloc(size * sizeof(int));
    for (int i = 0; i < size; i++) 
    {
        array[i] = i + 1; // 初始化数组
    }
    return array; // 返回数组的指针
}
 
// 主函数
int main() 
{
    int size = 5;
    // 获取数组的指针
    int *myArray = getArray(size);
    
    // 打印数组内容
    for (int i = 0; i < size; i++) 
    {
        printf("%d ", myArray[i]);
    }
    printf("\n");
 
    // 释放分配的内存
    free(myArray);
    return 0;
}

十.在头文件中定义静态变量是否可行,为什么?

不可行,如果在头文件中定义静态变量,会造成资源浪费的问题,同时也可能引起程序错误。因为如果在使用了该头文件的每个C语言文件中定义静态变量,按照编译的步骤,

在每个头文件中都会单独存在一个静态变量,从而会引起空间浪费或者程序错误所以,不推荐在头文件中定义任何变量,当然也包括静态变量。

;