C++ 核心知识点全解

一、基础语法与类型系统

1. 基本数据类型与大小

C++ 标准只规定最小范围,具体大小由平台决定。常见 32/64 位平台典型大小:

类型典型字节数说明
bool1true / false
char1字符 / 小整数
short2短整型
int4整型(常用)
long4 / 8长整型
long long8长长整型
float4单精度浮点
double8双精度浮点(常用)
long double8 / 16扩展精度
  • 无符号类型:unsigned charunsigned int 等,只存非负数。
  • 字面值后缀:
    • 10u / 10U → unsigned
    • 10l / 10L → long
    • 10ll / 10LL → long long
    • 3.14f → float

2. 类型限定符

  • const:只读变量,必须初始化,之后不能赋值。
    const int a = 10;
    a = 20; // 错误
    
  • constexpr:C++11,编译期可求值,性能更好。
    constexpr int square(int x) { return x*x; }
    int arr[square(5)]; // 合法,编译期确定大小
    
  • volatile:告诉编译器不要优化,每次必须从内存读,多用于硬件/多线程。
  • register:建议放入寄存器,现代编译器基本自动优化。

3. 初始化方式

C++11 推荐统一使用列表初始化,更安全。

int a = 10;     // 拷贝初始化
int b(10);      // 直接初始化
int c{10};      // 列表初始化(推荐)
int d = {10};   // 同上
  • 列表初始化禁止窄化转换
    int x{3.14};  // 编译报错,防止精度丢失
    

4. 左值与右值

  • 左值:可以放在 = 左边,有内存地址,可以被多次使用。
    int a = 10;
    a = 20;   // a 是左值
    
  • 右值:临时值,不能取地址,用完即销毁。
    10;           // 右值
    a + b;        // 表达式结果是右值
    

5. 自增自减

  • 前置 ++i:先自增,返回自身(左值),效率更高。
  • 后置 i++:先复制旧值,再自增,返回旧值(右值)。
int i = 0;
cout << ++i; // 输出 1
cout << i++; // 输出 1,i 变为 2

6. 流程控制

  • 范围 for(C++11):遍历容器/数组更简洁。
    vector<int> v = {1,2,3};
    for (int x : v) { ... }        // 拷贝
    for (int& x : v) { ... }       // 引用,可修改
    for (const int& x : v) { ... } // 只读,高效
    
  • switch:只能匹配整型/枚举常量,case 默认穿透,必须加 break

7. 函数重点

7.1 默认参数

  • 必须从右往左连续设置
    void f(int a, int b=1, int c=2); // 正确
    void f(int a=1, int b, int c);  // 错误
    
  • 声明时写默认参数,实现时不要重复写

7.2 函数重载

同一作用域内,函数名相同,参数列表不同(个数/类型/顺序)。

int add(int a, int b);
double add(double a, double b); // 重载
  • 返回值不同不能构成重载

7.3 内联函数 inline

  • 建议编译器将函数体展开到调用处,减少函数调用开销。
  • 适合短小、频繁调用的函数。
  • 类内定义的函数默认 inline

二、指针、引用、内存结构

1. 指针

  • 指针是存地址的变量
    int a = 10;
    int* p = &a;  // p 存 a 的地址
    *p = 20;      // 解引用,修改 a
    
  • 空指针nullptr(C++11),类型安全,优于 NULL
  • 野指针:指向无效/已释放内存,使用会崩溃或未定义行为。
  • 悬空指针delete 后未置 nullptr
  • 指针运算:p+1指向类型大小为单位,只在同一数组内合法。

2. 引用 &

  • 引用是变量别名,与原对象共用同一块内存。
int a = 10;
int& r = a;
r = 20; // a 也变成 20
  • 特性:
    • 必须初始化,不能“空引用”。
    • 不能重新绑定到另一个变量。
    • 编译器底层通常用指针实现,但使用更安全。
  • const 引用可以绑定临时对象(右值):
    const int& r = 10; // 合法
    
  • 常用场景:
    • 函数参数避免大对象拷贝。
    • 函数返回成员引用(注意生命周期)。

3. C++ 内存五大区

  1. 栈 stack

    • 局部变量、函数参数、返回地址。
    • 自动分配/释放,连续、小、极快。
    • 递归过深、超大局部数组会栈溢出
  2. 堆 heap

    • new/malloc 分配,手动管理。
    • 空间大、随机、较慢。
    • 忘记 delete内存泄漏
  3. 全局/静态区

    • 全局变量、static 变量。
    • 程序启动创建,结束释放,默认初始化为 0。
  4. 常量区

    • 字符串常量、const 全局数据。
    • 只读,修改会崩溃。
  5. 代码区

    • 存放可执行指令,只读共享。

4. new/delete 与 malloc/free

  • new/delete运算符,会调用构造/析构函数
  • malloc/free 是库函数,只分配/释放裸内存。
  • 必须配对:
    • newdelete
    • new[]delete[]
  • 常见错误:
    • free 释放 new 出来的内存。
    • delete 释放 malloc 出来的内存。
    • 重复释放、释放野指针。

三、面向对象 OOP

1. 类与访问控制

  • class 默认 privatestruct 默认 public
  • 访问权限:
    • private:仅本类/友元可访问。
    • protected:本类/友元/子类可访问。
    • public:全局可访问。

2. 六大特殊成员函数(C++11)

编译器会自动生成,但若你手写其中一部分,另一些可能被删除。

  1. 默认构造
  2. 析构函数
  3. 拷贝构造
  4. 拷贝赋值
  5. 移动构造
  6. 移动赋值

3. 构造函数

  • 与类同名,无返回值,创建对象时自动调用。
  • 初始化列表:比函数体内赋值更早、效率更高
    Person(string n, int a)
        : name(std::move(n)), age(a)
    {}
    
  • 必须用初始化列表的场景:
    • const 成员
    • 引用成员
    • 基类无默认构造
    • 成员对象无默认构造

4. 析构函数

  • ~类名(),无参无返回,不可重载。
  • 对象销毁时自动调用,用于释放资源(堆内存、文件、socket 等)。
  • 若类中有指针成员,通常需要手写析构,防止内存泄漏。
  • 多态基类的析构函数建议声明为 virtual,否则可能只调用基类析构,造成泄漏。

5. 拷贝构造 & 浅/深拷贝

  • 拷贝构造:用已有对象初始化新对象。
    Person(const Person& other);
    
  • 默认拷贝构造是浅拷贝:只复制成员值。
  • 浅拷贝问题:多个对象指向同一块堆内存,析构时重复 delete,崩溃。
  • 深拷贝:重新分配内存并复制内容,需手动实现。

6. 拷贝赋值运算符

Person& operator=(const Person& other);
  • 经典三步写法:
    1. 防止自赋值 if (this == &other) return *this;
    2. 释放当前对象资源
    3. 深拷贝资源
    4. 返回 *this 支持链式赋值

7. 移动语义(C++11)

  • 右值引用 T&&:专门绑定临时对象。
  • 移动构造 / 移动赋值:转移资源所有权,不拷贝数据,效率极高。
    Person(Person&& other) noexcept;
    Person& operator=(Person&& other) noexcept;
    
  • std::move:将左值强制转为右值引用,本身不移动任何数据
  • 标准库容器默认优先使用移动操作。

8. this 指针

  • 指向当前对象本身,类型为 类名* const
  • 用于区分成员与参数重名:
    void setAge(int age) {
        this->age = age;
    }
    
  • 链式调用:返回 *this
    Person& setName(string n) {
        name = std::move(n);
        return *this;
    }
    p.setName("Alice").setAge(20);
    

四、继承与多态

1. 继承方式

class 子类 : 继承方式 父类 { ... };
  • public 继承:最常用,基类权限基本保留。
  • protected 继承:基类 publicprotected
  • private 继承:基类所有成员变 private

2. 构造析构顺序

  • 构造:
    基类构造 → 成员对象构造 → 自身构造
  • 析构:
    自身析构 → 成员对象析构 → 基类析构
  • 子类必须在初始化列表中初始化基类,如果基类没有默认构造。

3. 同名隐藏

  • 子类定义与基类同名函数,会隐藏基类所有同名重载版本。
  • 如需在子类中使用基类重载版本:
    using Base::func;
    

4. 运行期多态

多态 = 接口统一,实现不同。
满足三条件:

  1. 公有继承
  2. 基类函数为 virtual 虚函数
  3. 基类指针/引用指向子类对象
  • 底层实现:虚表 vtable + 虚指针 vptr
  • 重写:子类重新实现虚函数,函数签名必须完全一致
  • override:显式标记重写,编译器检查拼写/签名错误,强烈推荐。
    void show() override;
    
  • final
    • 修饰类:禁止被继承。
    • 修饰虚函数:禁止被重写。

5. 纯虚函数与抽象类

virtual void draw() = 0;
  • 包含纯虚函数的类称为抽象类,不能实例化。
  • 子类必须实现所有纯虚函数才能实例化,用于定义接口。

6. 虚析构

  • 基类析构声明为 virtual,则通过基指针删除子类对象时,会多态调用子类析构
  • 否则只调用基类析构,子类资源泄漏。
    class Base {
    public:
        virtual ~Base() = default;
    };
    

7. 菱形继承与虚继承

  • 问题:顶层基类被多份继承,数据冗余、访问二义性。
  • 解决方案:虚继承
    class B : virtual public A { ... };
    class C : virtual public A { ... };
    
  • 虚继承下,由最派生类负责初始化公共基类。

五、模板与泛型编程

1. 函数模板

template <typename T>
T add(T a, T b) {
    return a + b;
}
  • 编译期根据实参类型生成对应版本(实例化)。
  • 支持多个类型参数、非类型模板参数(整数常量)。
    template <typename T, int N>
    T sum(T (&arr)[N]) { ... }
    

2. 类模板

template <class T>
class Stack {
    vector<T> data;
public:
    void push(const T& x);
};
  • 实例化:Stack<int> st;
  • 类模板成员函数通常全写在头文件/同文件,否则链接时可能找不到实例。

3. 模板特化

  • 全特化:对某一特定类型完全重写。
    template <>
    class Stack<int> { ... };
    
  • 偏特化(只适用于类模板):对一类类型(如指针)特殊处理。
    template <class T>
    class Stack<T*> { ... };
    

4. 可变参数模板(C++11)

template <class... Args>
void print(Args... args) { ... }
  • ... 表示参数包。
  • 展开方式:递归、逗号表达式、折叠表达式(C++17)
    template <class... Args>
    auto sum(Args... args) {
        return (args + ...); // 一元右折叠
    }
    

六、现代 C++ 核心特性(C++11/14/17)

1. auto 类型推导

  • 从初始化表达式自动推导类型。
  • 丢弃顶层 const、引用,如需保留:
    const auto& x = y;
    
  • 典型场景:迭代器、复杂模板、lambda。
    vector<int> v;
    auto it = v.begin();
    

2. decltype

  • 推导表达式类型,不丢弃 const/引用
  • 常用于返回类型后置、模板元编程。
    int a = 10;
    decltype(a) b = 20;    // int
    decltype((a)) r = a;   // int&
    

3. Lambda 表达式

[capture](params) mutable -> ret { body }
  • 捕获列表:
    • [] 不捕获
    • [=] 值捕获外部变量
    • [&] 引用捕获
    • [this] 捕获当前对象指针
  • mutable:允许修改值捕获的副本。
  • 无捕获 lambda 可隐式转为函数指针
  • 常用于 STL 算法、回调、线程函数。
    sort(v.begin(), v.end(), [](int a, int b) {
        return a < b;
    });
    

4. 智能指针()

用于自动管理堆内存,避免泄漏/重复释放。

  • unique_ptr:独占所有权,不能拷贝,只能移动,效率接近裸指针。
    unique_ptr<int> p(new int(10));
    auto p2 = std::move(p);
    
  • shared_ptr:引用计数共享,线程安全的计数,对象访问非线程安全。
  • weak_ptr:弱引用,不增加引用计数,解决 shared_ptr 循环引用。
  • 优先使用工厂函数:
    make_unique<T>(...)  // C++14
    make_shared<T>(...)
    

5. 其他常用现代特性

  • nullptr:类型安全空指针,替代 NULL
  • 列表初始化:统一语法,禁止窄化。
  • noexcept:声明函数不抛异常,利于优化,移动操作建议加。
  • constexpr:编译期计算,性能更好。
  • 结构化绑定(C++17)
    pair<int, string> f();
    auto [id, name] = f();
    

七、STL 标准模板库

1. 容器分类

  • 序列容器vectorlistdequearraystring
  • 关联容器setmapmultisetmultimap(红黑树,有序)
  • 无序关联容器unordered_setunordered_map(哈希表,平均 O(1))
  • 适配器stackqueuepriority_queue

2. 常用容器选型

  • 需要随机访问、尾部增删快vector
  • 需要任意位置频繁插入/删除list
  • 需要头尾快速增删deque
  • 需要key-value 有序查找map
  • 需要最高查询性能、无序unordered_map

3. 迭代器

  • 行为类似指针,提供统一遍历接口。
  • 区间 [begin(), end()) 左闭右开。
  • 类型(从弱到强):
    输入 → 输出 → 前向 → 双向 → 随机访问
  • cbegin()/cend():常量迭代器,不能修改元素。

4. 常用算法()

  • 遍历:for_each
  • 查找:findfind_ifcountbinary_search
  • 排序:sortstable_sort
  • 修改:copyreplacefillreverse
  • 删除惯用手法:erase-remove
    v.erase(remove(v.begin(), v.end(), val), v.end());
    

八、异常处理

  • 关键字:trythrowcatch
  • 匹配规则:从上到下,类型优先匹配。
  • 捕获所有异常:catch (...)
  • 标准异常继承自 std::exception,提供 what() 信息。
  • 常见标准异常:
    • bad_allocnew 分配失败
    • out_of_range:越界
    • invalid_argument:无效参数
  • RAII:利用对象生命周期自动管理资源,是异常安全的核心方案。
    • 智能指针
    • lock_guard/unique_lock

九、友元、运算符重载

1. 友元

  • 友元函数/友元类可以访问类的 private/protected 成员。
  • 友元关系:单向、不传递、不继承
  • 典型场景:重载 <</>>、工具类访问内部数据。
    class A {
        friend void func(A& a);
        friend class B;
    };
    

2. 运算符重载

  • 成员函数形式:返回值 operator op(参数)
  • 非成员(友元):适合对称运算符(+==)。
  • 不能重载的运算符:
    ::.*.?:sizeoftypeid
  • 自增自减区分:
    • 前置:T& operator++()
    • 后置:T operator++(int)
  • 流运算符 <</>> 必须重载为非成员函数

十、常用工程惯用法

  • RAII:资源获取即初始化,自动释放,安全简洁。
  • PImpl:接口与实现分离,降低编译依赖,加速编译。
  • 移动优先:能用移动就不用拷贝,提升性能。
  • 尽量用 const:提高可读性、安全性、优化空间。
  • 少用全局变量、宏:易冲突、难调试。
  • 头文件保护#pragma once#ifndef ... #define ... #endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ab123768

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

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

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

打赏作者

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

抵扣说明:

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

余额充值