二、基础与语法
约 1956 字大约 7 分钟
2026-04-13
介绍c++一下三大特性
封装、继承、多态。
封装:把数据和操作打包在一起,外人不能随便动,通过public,private,protected保护数据
继承:子类通过继承使用父类公开的数据,属性和方法,可以“复制+扩展”父类的内容,写代码更省力。
多态:用“同一种方式”操作不同的对象,扩展性强。
指针和引用的区别
指针:是一个变量,存储另一个变量的内存地址,用(*)访问目标值。可重新赋值指向其他对象,支持指针算术(如++),可为空(nullptr),指针可以有const。
占用独立的内存(通常是4或8字节),需要手动管理动态内存。
引用:是变量的别名,绑定后不能修改,且不能为空,使用时无需解引用,引用没有const。
区别:不存在指向空值的引⽤,但是存在指向空值的指针
详情
举个最简单的联合体:
union Test {
int a; // 4字节
char b; // 1字节
};内存布局是这样的:
内存地址: 0 1 2 3
┌───────────┐
a 存储: │ int a │ 4字节
└───────────┘
↑
b 存储: │ char b │ 共享同一起始地址!- 给 a 赋值,这块内存存的是整数
- 给 b 赋值,这块内存存的是字符
你不可能同时存两个值,后赋值的会覆盖先赋值的
- 为什么要这么设计? C++ 设计联合体的目的只有一个:
- 节省内存空间
- 在早期内存非常小的设备(单片机、嵌入式)里,有些数据同一时间只会用到一个,没必要给每个成员都分配独立内存。 比如:一个变量要么存整数,要么存浮点数,二选一,用联合体只占最大成员的大小,不浪费空间。
c++ 中 volatile 关键字的作用
volatile是一个类型修饰符,用于告知编译器该变量的值可能会被程序之外的、编译器无法感知的因素(如硬件、中断服务程序、其他线程等)意外修改。
它主要作用是:禁止编译器对该变量进行优化(如缓存到寄存器、消除“冗余”读写操作),确保每次访问变量时都直接从其内存地址中读取或写入。
用了volatile,编译器就会老老实实地每次去读它的真实值,而不会用之前缓存的旧值来替代。
c++ 中 inline 函数 vs #define 宏的区别
inline 是 C++ 语言层面的函数修饰符,由编译器处理,具有类型检查、作用域和调试支持;
而 #define 宏是预处理器指令,在编译前进行文本替换,缺乏类型安全但更灵活。调试时无法跟踪,看到的只是替换后的代码。
在现代C++中,应优先使用inline函数。
普通函数:调用时会跳转到函数定义处执行,执行完再跳回来,有函数调用开销。
inline 函数:编译时会把函数体直接复制粘贴到调用处,没有函数调用开销,但会让代码体积变大。
C++11 中的 auto 和 decltype 的区别
auto 和 decltype 都是 C++11 引入的类型推导关键字。
但是 auto 使用模板参数推导规则,会忽略顶层 const 和引用,除非显式声明; decltype 直接反映表达式的声明类型,保留所有类型修饰符。
sizeof 和 strlen 的区别
sizeof 是编译期运算符,计算的是类型或变量占用的内存字节数,适用任意类型;
strlen 是运行时标准库函数,计算的是字符串中 '\0' 之前的字符个数,仅限 char* / char[]。
// ── 情况1:字符数组 ──────────────────────────
char s1[] = "hello";
cout << sizeof(s1) << endl; // 6,数组大小 = 5字符 + 1个'\0'
cout << strlen(s1) << endl; // 5,不含'\0'
// ── 情况2:字符指针 ──────────────────────────
const char* s2 = "hello";
cout << sizeof(s2) << endl; // 8(64位系统指针大小),不是字符串长度!
cout << strlen(s2) << endl; // 5,正常计算
// ── 情况3:手动指定大小的数组 ──────────────────
char s3[20] = "hello";
cout << sizeof(s3) << endl; // 20,数组声明的总大小
cout << strlen(s3) << endl; // 5,只看'\0'之前
// ── 情况4:sizeof 用于非字符串类型 ─────────────
cout << sizeof(int) << endl; // 4
cout << sizeof(double) << endl; // 8
// strlen(42); // ❌ 编译错误:strlen 只接受 char*
// ── 情况5:含中间'\0'的字符数组(经典陷阱)──────
char s4[] = {'a', '\0', 'b', 'c'};
cout << sizeof(s4) << endl; // 4,数组真实大小
cout << strlen(s4) << endl; // 1,碰到第一个'\0'就停了!c++ 中浮点数相等比较的正确方法
浮点数在计算机中采用IEEE 754标准表示,存在以下问题:
精度限制:浮点数有有限的精度位数,无法精确表示所有实数
舍入误差:运算过程中会产生舍入误差,累积后可能显著
表示误差:某些十进制数在二进制中无法精确表示(如0.1)
正确比较方法:
绝对误差比较:适用于数值范围已知的情况,偏应用
相对误差比较:适用于数值范围变化较大的情况,偏理论
ULP比较:基于浮点数表示的精度的比较方法
静态局部变量,全局变量,局部变量的特点,以及使用场景
- 静态局部变量:函数内定义,用static修饰,生命周期为整个程序,该变量无法在作用域外修改,只能被初始化一次,之后每次调用都保持上以上调用结束时的值,存储在数据段。
- 全局变量:函数外定义,生命周期是整个程序运行区间,程序中可以在任何地方访问,创建在堆上。
- 局部变量:函数内定义,作用域和生命周期只能在声明该变量的函数体内或者类内访问,每次调用重新创建,生命周期随着函数的返回而结束,创建在栈上。
c++中四种类型的转换
C++引入了四种命名的类型转换运算符,以替代C风格强制转换,提供更清晰、更安全、更易于维护和查错的功能:
static_cast: 用于编译时已知的、相对安全的转换,如良性类型转换、编译器认可的向上向下转换。
dynamic_cast: 用于在继承层次结构中进行安全的向下或交叉转换,依赖运行时类型信息(RTTI),失败返回nullptr。
也可以理解为亲子转换,专门用来在父子类之间转换,速度有些慢但是安全。
const_cast: 用于增加或移除变量的const或volatile属性。
reinterpret_cast: 用于低级的、基于比特位的重新解释转换,高度依赖平台且不安全。
一般只在和底层硬件打交道时用reinterpret_cast。
