C++面试

本文深入探讨C++面试中的核心知识点,包括强制类型转换、指针与引用、泛型编程、类的访问控制、继承、面向对象以及RTTI。详细讲解了各种指针类型、函数指针的使用、const修饰指针的场景、STL的组成和ACM竞赛必备的STL技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

强制类型转换符

C++有四种强制类型转换符

  • dynamic_cast:将基类的指针或引用安全地转换为子类的指针或引用
  • const_cast:将常量指针或引用被转化成非常量的指针或引用,来修改常量
  • static_cast:子类指针或引用向上转换成基类(安全),基类指针或引用向下转换成子类(不安全),基本数据类型之间的转换,空指针转换成目标类型的空指针,任何类型的表达式转换成void类型
  • reinterpret_cast:用来处理无关类型之间的转换,它会产生一个新的值,这个值会有与原始参数有完全相同的比特位

指针

一级指针和二级指针

  • float(**a)[10];

    a是一个二级指针,指向一个一维数组的指针,数组元素为float类型

  • double*(*b)[10];

    b是一个一级指针,指向一个一维数组,数组元素为double*类型

  • double(*c[10])();

    c是一个10个元素的数组,数组元素为指向函数类型为没有参数且返回double的函数指针

  • int*((*d)[10]);

    同int(d)[10];d是一个一级指针,指向一个一维数组,数组元素为int*类型

  • long (*e)(int)

    e是一个一级指针,指向函数类型为参数为int且返回long的函数指针

  • int (*(*f)(int, int))(int)

    f是一个一级指针,指向函数类型为参数有两个int且返回一个函数指针的函数,返回的函数指针指向类型为参数为int且返回int的函数

函数指针和函数返回指针

  • 函数指针

    void (*f) ();

  • 函数返回指针

    void* f();

函数指针的使用

#include <cstdio>
int max(int x, int y) 
{
    return x > y ? x : y;
}
int main() 
{
    int max(int, int);
    int (*p) (int, int) = &max;
    int a, b, c, d;
    scanf(“%d %d %d”, &a, &b, &c);
    d = (*p) ((*p)(a, b), c);
    printf(“max: %d\n”, d);
    return 0;
}

const修饰指针的4种情况

  • const int *a = &b;

    const修饰指针指向的变量,指针指向的变量不可变

  • int const *a = &b;

    const修饰指针指向的变量,指针指向的变量不可变

  • int* const a = &b;

    const修饰指针,指针不可变

  • const int* const a = &b;

    指针和指针指向的变量都不可变

指针和引用的差别

  • 非空区别

    • 任何情况下都不能使用指向空值的引用。这意味着使用引用的代码比指针高
  • 合法性区别

    • 使用引用之前不需要测试合法性
    • 指针则应该总是被测试,防止其为空
  • 可修改区别

    • 指针可以被重新赋值以指向另一个不同的对象
    • 引用则总是指向在初始化时被指定的内容,以后不能改变
  • 应用区别

    • 在以下情况应该使用指针

      • 考虑存在不指向任何对象的可能
    • 需要能够在不同的时刻指向不同的对象

    • 否则应该使用引用

  • 内存大小

    • 指针是一种存储指向变量地址的类型,需要占用空间
    • 引用是变量的别名,不需要占用空间

泛型编程

泛型编程是一种基于发现高效算法的最抽象表示的编程方法。也就是以算法为起点并寻找能使其工作且有效率工作的最一般的必要条件集。

很多不同的算法都需要相同的必要条件集,并且这些必要条件有多种不同的实现方式。

泛型编程假定有某些基本法则在支配软件组件的行为,并且基于这些法则有可能设计可互操作的模块,甚至可以使用此法则去指导我们的软件设计。

STL是泛型编程的例子。

template<typename T>
const T* find(T* array, T n, T x) 
{
    const T* p = array;
    int i;
    for (i = 0; i < n; ++i) 
    {
        if (*p == x) 
        {
          return p;
        }
    }
    return NULL;
}

类的访问控制

访问范围

  • private

    • 该类的函数
    • 该类的友元函数
    • 该类的友元类
  • protected

    • 该类的函数
    • public继承的子类的函数
    • 该类的友元函数
    • 该类的友元类
  • public

    • 该类的函数
    • public、protected继承的子类的函数
    • 该类的友元函数
    • 该类的友元类
    • 该类的对象

继承

继承类型

  • private继承

    • 父类的protected和public变为private
  • protected继承

    • 父类的public变为protected
  • public继承

    • 不改变

虚继承

虚继承是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。

class Animal 
{
public:
        virtual void eat();
};

// Two classes virtuallyinheriting Animal:
class Mammal : public virtual Animal 
{
public:
    virtual voidbreathe();
};

class WingedAnimal: public virtual Animal 
{
public:
    virtual void flap();
};

// A bat is still awinged mammal
class Bat : public Mammal, public WingedAnimal 
{
};

Bat bat;

如果不采用虚继承,调用bat.eat()是有歧义的,因为在Bat中有两个Animal基类(间接的),所以所有的Bat对象都有两个不同的Animal基类的子对象。

采用虚继承后,Bat现在有且只有一个共享的Animal部分,不再有歧义

菱形继承

上述例子不采用虚继承,就会出现菱形继承的问题,在Bat中有两个Animal基类(间接的),所以所有的Bat对象都有两个不同的Animal基类的子对象。

多重继承

多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。

继承优缺点

优点

  • 复用性强

缺点

  • 可能出现二义性(菱形继承)

面向对象

面向对象程序设计

面向对象程序设计(英语:Object-orientedprogramming,缩写:OOP)是种具有对象概念的程序编程范型,同时也是一种程序开发的方法。

它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。

优点

  • 良好的可重用性
  • 易维护
  • 良好的可扩展性

三原则

  • 封装

  • 继承

    • 泛化

      • 实现继承:使用基类的属性和方法,无需额外编码
    • 可视继承:子类使用基类的外观和实现代码

    • 组合

      • 接口继承:仅使用属性和方法的名称、但是子类必须提供实现
    • 纯虚类
  • 多态

    • 覆盖:子类重新定义父类的函数(动态)

      • 虚函数
    • 接口

    • 重载:允许存在多个同名函数,而这些函数的参数表不同(静态)

特征

  • 里氏原则:子类型必须能够替换它们的基类型
  • 开闭原则:软件对扩展应该是开放的,对修改应该是关闭的
  • 封装:封装是通过限制只有特定类的对象可以访问这一特定类的成员,而它们通常利用接口实现消息的传入传出。同时还可以将相关的常量定义、类型定义、函数声明等通过文件进行封装;也可以通过namespace进行封装
  • 继承:在某种情况下,一个类会有“子类”,子类比原本的类(称为父类)要更加具体化。
  • 多态:多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

友元

定义在类外部的普通函数或类,需要在类体内进行说明,为了与成员函数区别,需要在前面加关键字friend

友元函数或友元类可以访问类中的私有成员

提高程序运行效率,破坏了类的封装性和隐藏性

RTTI(Run-Time Type Identification)

通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。

两个操作符

  • typeid(头文件):返回指针或引用所指的实际类型
  • dynamic_cast:将基类类型的指针或引用安全地转换为派生类型的指针或引用

STL

STL是标准模版库(StandardTemplate Library)的简写。

STL包含4个组件,分别为算法、容器、函数、迭代器。

STL的目的是提供对常用需求重新开发的一种代替方法,具有很高的性能并且免费。

标准模板库是可重用的,提高了开发效率。

算法

STL提供了一些常见的算法,如排序和搜索等。这些算法与数据结构的实现进行了分离。因此,用于也可对自定义的数据结构使用这些算法,只需让这些自定义的数据结构拥有算法所预期的迭代器。

容器

容器是包含其它对象的对象。标准模板库容器类包括两种类型,分别是顺序和关联

顺序容器可以提供对其成员的顺序访问和随机访问。

关联容器则通过优化关键值访问元素。

数据容器描述
序列容器 - 有序集
vector动态数组,兼容C语言数组。vector可以如同数组一样的访问方式,例如使用下标(operator[])运算符,并记得自己的长度信息(size),您也可以使用对象的方式来访问vector(push_back、pop_back)。使用vector可以轻易地定义多维可调整型数组(std::vector

迭代器

迭代器是泛化的指针,通过使用迭代器,开发者可以操作数据结构而无需关心其内部实现。根据迭代器的操作方式的不同,迭代器分为五种:

  • input iterators
  • output iterators
  • forward iterators
  • bidirectional iterators
  • random access iterators

ACM必备

  1. 容器相关

    vector不定长数组,图的邻接表常用vector+数组或vector套vector来表示

    list 双向链表,

    stack 栈

    queue单向队列,用于BFS等

    priority_queue优先队列,用于堆优化等

    pair 键值对

    set 集合,map 映射

    multiset,multimap 允许重复的set和map

    bitset 位集合

  2. 函数

    min, max求最小值,最大值

    swap交换两个元素

    sort,stable_sort排序

    lower_bound,upper_bound,binary_search,equal_range二分查找

    next_permutation求下一个字典序,常先sort然后与do…while结合进行全排列遍历

    random_shuffle打乱一段区间

  3. 常用类

    valarray数值数组

    complex复数类

  4. 常用函数

    find,find_if区间内查找元素

    count,count_if 区间内统计元素

    search区间内查找子区间

    min_element,max_element找区间的最小值, 最大值

    fill 填充区间

    unique去重,需要先sort,并且不是真正的去重

    reverse反转区间

    merge合并两个有序区间

    swap_range交换两个区间

    adjacent_find区间内查找相邻的相同的元素

    mismatch找两个区间第一个不同的地方

    equal比较两个区间是否相等

    partition,stable_partition把区间一分为二

    partial_sort局部排序

    nth_element把第n大的元素放在其相应位置,左边都比它小,右边都比它大,但不保证有序

    set_union,set_intersection,set_difference,set_symmetric_difference求有序集合的并,交,差,对称差

    make_heap,push_heap,pop_heap,sort_heap对一段区间建立堆,插入堆,弹出堆,堆排序

    accumulate求一段区间的累和或累乘等

    adjacent_difference求一段区间各元素的相邻差

    inner_product求两个区间对应元素乘积的和

    partial_sum求一段区间各个位置的前缀和

  5. C++11

    auto自动类型推导

    lambda匿名函数,用在函数里需要传入函数的地方

    for-range范围for循环,结合auto使用

    unordered_set,unordered_map利用hash表实现的set和map

    iota生成一段递增的区间,可用于并查集初始化等

c++面试题53个问题 1.C++的三大特性 2.C和C++的区别 3.全局变量和局部变量在内存分配上有何不同 4.static的作用 5.const解释其作用 6.指针和引用的区别 7.智能指针 8.简述深拷贝和浅拷贝的区别 9.编写my_strcpy函数,实现与库函数strcpy类似的功能,不能使用任何库函数 10.请讲述堆和栈的区别 11.全局变量和局部变量有什么区别?实怎么实现的?操作系统和编译器是怎么知道的 12.new、delete、malloc、free之间的关系 13. 头文件种的ifndef/define/endif 是干什么用的 14.TCP和UDP有什么区别 15.STL库用过吗?常见的STL容器有哪些?算法用过哪几个 16.同步IO和异步IO的区别 17.说下你对内存的了解 18.C++文件编译与执行的四个阶段 19.extern关键字的作用 20.#define和const的区别 21.结构体struct和共同体union(联合)的区别 22.C++中vector和list的区别 23.结构体和类的区别 24.STL中map和set的原理(关联式容器) 25.MFC的消息机制 26.消息映射 27.列举几种进程的同步机制,并比较其优缺点 28.数组和链表的区别 29.MFC主要要用到哪几个类?及其各个类的作用 30.MFC六大核心机制 31.OnDraw和OnPaint 32.win32程序的消息响应机制是如何实现的 33.MFC中的消息响应机制是如何实现的 34.WM_COMMAND命令消息处理顺序 35.MFC序列化的概念 36.PeekMessage和GetMessage的主要区别 37.WIN32创建窗口程序基本函数 38.Windows中的系统消息循环占用CPU的疑问 39.队列消息与非队列消息 40.GDI对象绘图步骤 41.设备上下文DC 42.GDI位图绘制步骤 43.当模态对话框点开后,主窗口还能响应处理消息吗 44.MFC的消息分类 45.CListCtrl 虚拟列表技术 46.虚函数是怎么实现的 47.什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法? 48.变量的声明和定义有什么区别 49.sizeof是一个操作符,strlen是库函数 50.写一个“标准”宏MIN 51.简述strcpy sprintf与mencpy的区别 52.链表与数组的区别 53.简述队列和栈的异同
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值