C++面试八股文:static和const的关键字有哪些用法?

文章讨论了C++中的static和const关键字的用法,包括它们在全局作用域、方法内和类成员中的作用。此外,还解释了const在指针类型的两种不同位置产生的不同效果——常量指针和指针常量。面试中提到了顶层const和底层const的概念,以及constexpr、consteval和constinit等C++20的新特性,其中consteval确保函数在编译时求值,constinit用于静态变量的初始化以保证线程安全。

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

某日二师兄参加XXX科技公司的C++工程师开发岗位第7面:

面试官:C++中,staticconst的关键字有哪些用法?

二师兄:satic关键字主要用在以下三个方面:1.用在全局作用域,修饰的变量或者函数为静态的,限制在本文件内使用。2.方法内修饰修饰静态局部变量,在第一次访问的时候初始化。3.内类修饰成员函数和成员变量,此函数或变量由类持有,而非类的对象持有。

二师兄:const关键字主要也有三个用途:1.修饰函数参数,则在函数内部不可以修改此参数。2.修饰类的成员方法,表面此方法不会更改类对象的任何数据。3.修饰变量,表明变量不可以被修该。

面试官:现在两个指针,一个是 const int*,另一个是int * const,两者有什么区别?

二师兄:这要要看const*的位置,const在前称为常量指针,const在后称为指针常量。常量指针的指针部分可变,指针指向的内容不可变。指针常量则相反,指针指向的内容可变,指针不可变。

面试官:嗯,那么你知道顶层const和底层const吗?

二师兄:额。。。不知道。。。

面试官:知道constexpr关键字的作用吗?

二师兄:了解一些。constexpr关键字修饰的变量具有编译器求值的功能。修饰的函数是否在编译器求值要取决于传入的参数是不是编译器确定的,这属于元编程的范畴。

面试官:嗯,那你知道constevalconstinit这两个关键字的作用吗?

二师兄:额。。。了解过一些,忘记了。

面试官:好的,回去等通知吧。

让我们来复盘一下今日二师兄的表现:

const在前称为常量指针,const在后称为指针常量。

这里的表述仁者见仁智者见智。但是在大名鼎鼎的《C++ Primer Edition 5》中文版中,const int*被称为指向常量的指针,而int* const则被称为常量指针。这种表述更容易理解两种指针的差异。

你知道顶层const和底层const吗?

这里的顶层const和底层const概念主要是为了区分const修饰的是变量本身还是变量指向的内容。

int i = 42;
const int ci = 42;//顶层const
const int* pi = &i; //磁层const,因为const修饰的是i所在的地址,不能通过pi去修改i,但是可以修改pi
int* const pi2 = &i;//顶层const,因为pi2无法修改
const int& ri = i;    //底层const,不能通过ri修改i

在传递参数时,顶层const可以被忽略,而底层const不能被忽略。

你知道constevalconstinit这两个关键字的作用吗?

还记得constexpr是否在编译时求值要取决于传入的参数吗?如果传入的参数是编译时确定的,constexpr函数就会在编译时求值,反之则会在运行时求值。

constexpr int add(int a, int b)
{
    return a+b;
}

constexpr int sum1 = add(1,2);    //编译时求值

int a = 1, b = 2;
int sum2 = add(a,b);    //运行时求值

当使用consteval修饰函数和变量时,如果不能在编译时求值,则编译错误。

consteval int add(int a, int b)
{
    return a+b;
}
constexpr int sum1 = add(1,2);    //编译通过,在编译时求值
int a = 1, b = 2;
int sum2 = add(a,b);    //编译失败

constinit关键字用于声明一个变量为常量初始化变量。使用constinit可以保证静态变量的初始化顺序和线程安全性,从而避免竞争条件的发生。需要注意的是,constinit关键字只能用于静态变量的初始化,不能用于动态变量的初始化。

需要注意的是,consteval和constinit关键字是C++20引入的, 小伙伴们可以在编译时加入-std=c++20尝试使用这两个关键字。

好了,今日份面试到这里就结束了,小伙伴们,对于今天二师兄的面试,能打几分呢?

关注我,带你21天“精通”C++!(狗头)

<think>嗯,用户要求汇总C/C++面试常见问题,这是典型的求职准备需求。从引用内容看,用户可能正在收集技术面试高频考点,特别是内存管理、函数重载、虚函数等底层机制。需要覆盖语言特性、内存模型、面向对象等核心领域。 首先考虑基础概念:指针引用的区别是必考题,用户提供的引用[1]涉及malloc(0)的边界情况,说明面试官喜欢考察异常处理能力。引用[2]展示名字修饰机制,这是解释函数重载原理的关键。引用[3]的虚函数表内容正好切入面向对象核心。 内存管理部分要突出实践性。结合引用[1]的malloc案例,可以设计内存分配相关陷阱题。引用[4]提到的拷贝构造场景内存对齐原则,都是实际开发中容易出错的知识点。 面向对象设计需深入底层。虚函数表、纯虚函数等概念要关联实现机制,引用[3]提到构造函数不能虚化的原因就很典型。多态性部分需要区分静态绑定动态绑定的应用场景。 模板STL部分侧重应用。引用[2]隐含的名字修饰问题,在模板实例化时同样存在,可以引申出模板编译机制。智能指针的循环引用问题也是高频考点。 最后是编译调试。引用[2][3]都涉及编译器行为,说明用户需要理解从代码到可执行文件的转换过程。预处理指令、静态断言等实用技巧能提升回答深度。 准备用分类结构组织答案,每类包含理论要点典型例题。用户引用的四个例子正好对应内存管理、编译机制、多态实现三个维度,可以在相关小节重点展开。问题设计要模拟真实面试场景,比如引用[1]的malloc(0)就能延伸出堆内存分配的深度讨论。</think>以下是C/C++面试常见问题分类汇总,涵盖核心概念实际应用场景: --- ### **一、基础概念** 1. **指针与引用的区别** - 指针是变量,存储地址;引用是别名,必须初始化且不可变 - 引用更安全(无空引用),指针支持算术运算 *例:`int a=5; int *p=&a;` vs `int &r=a;`* 2. **`const` 关键字用法** - `const int* p`(指向常量的指针) vs `int* const p`(常量指针) - `const` 成员函数:禁止修改对象成员(如 `void func() const;`) --- ### **二、内存管理** 1. **堆与栈的区别** | **特性** | **栈** | **堆** | |----------------|----------------------------|----------------------------| | 管理方式 | 编译器自动分配释放 | 手动分配释放(`new`/`delete`) | | 大小限制 | 系统限定(通常几MB) | 受虚拟内存限制 | | 碎片问题 | 无 | 可能产生内存碎片 | 2. **内存泄漏场景与检测** - 常见原因:忘记释放堆内存、循环引用(智能指针未正确处理) - 工具:Valgrind、AddressSanitizer 3. **`malloc`/`free` vs `new`/`delete`** ```c++ int* p1 = (int*)malloc(sizeof(int)); // C风格,不调用构造函数 free(p1); int* p2 = new int(10); // C++风格,调用构造函数 delete p2; // 调用析构函数 ``` --- ### **三、面向对象** 1. **虚函数实现原理** - 通过虚函数表(vtable)实现动态绑定,每个类维护虚函数地址数组[^3] - 构造函数不能为虚函数(虚表在构造期间未初始化)[^3] 2. **继承中的访问控制** ```c++ class Base { protected: int data; }; class Derived : public Base { void access() { data = 10; } // 合法访问 }; ``` 3. **纯虚函数与抽象类** ```c++ class Shape { public: virtual void draw() = 0; // 纯虚函数 }; ``` --- ### **四、模板与STL** 1. **模板特化与偏特化** ```c++ template <typename T> void func(T a); // 通用模板 template <> void func<int>(int a); // 全特化 template <typename T> void func<T*>(T* a); // 偏特化 ``` 2. **`vector` 底层实现** - 动态数组,容量不足时以1.5~2倍扩容(`push_back`时间复杂度均摊O(1)) 3. **智能指针(C++11)** - `unique_ptr`:独占所有权,禁止拷贝 - `shared_ptr`:引用计数,支持拷贝 - `weak_ptr`:解决`shared_ptr`循环引用问题 --- ### **五、编译与底层** 1. **`extern "C"` 的作用** - 抑制C++的名称修饰(name mangling),确保C语言兼容性[^2] *例:`extern "C" void foo(int x);` → 编译后符号名为 `_foo` 而非 `_foo_int`* 2. **内存对齐原则** - 结构体成员按自身大小对齐(如`int`对齐到4字节) - 总大小为最大成员大小的整数倍[^4] 3. **拷贝构造函数的调用时机** - 对象初始化(`ClassA b = a;`) - 传参/返回值(值传递时)[^4] --- ### **六、经典问题** 1. **虚析构函数必要性** ```c++ Base* p = new Derived(); delete p; // 若~Base()非虚,仅调用Base的析构函数→内存泄漏 ``` 2. **深拷贝 vs 浅拷贝** - 浅拷贝:复制指针(同一内存) - 深拷贝:复制指向内容(新内存) 3. **`static` 关键字作用域** - 全局静态变量:文件内可见 - 局部静态变量:函数内持久化 - 类静态成员:所有实例共享 --- ### **相关问题** 1. 如何用C实现C++的类继承机制? 2. 在多线程环境下,`static`变量是否安全?如何保证线程安全? 3. 解释RTTI(运行时类型识别)的实现原理及其开销 4. C++11的移动语义如何提升性能?给出具体示例 5. 什么是“切片问题”(slicing problem)?如何避免? > 引用说明: > [^1]: 涉及内存分配的边界情况处理(如`malloc(0)`) > [^2]: C++名称修饰(name mangling)机制 > [^3]: 虚函数表构建时机与构造函数限制 > [^4]: 拷贝构造函数调用场景与内存对齐原则
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值