C++ 学习阶段的三个参考文档
查看库文件(非官方文档): Cplusplus.com
该文档在 C++98、C++11 时期尚可,之后版本更新滞后。
准官方文档(同步更新): cppreference
推荐作为主要查阅资源,涵盖至 C++26 标准,语法查询准确。
官方文档(类似论坛): Standard C++
社区讨论活跃,适合深入探讨标准细节。
4. 可变参数模版
4.1 emplace 系列接口
4.1.1 不同容器 emplace 系列接口展示
STL 容器普遍支持 emplace_back 和 emplace 接口,用于原地构造元素。
4.1.2 emplace 系列接口概念
template<class... Args>
void emplace_back(Args&&... args);
template<class... Args>
iterator emplace(const_iterator position, Args&&... args);
C++11 引入的 emplace 系列接口基于模板可变参数,功能兼容 push 和 insert,但支持直接传递构造参数。相比 push_back 或 insert,emplace 能在容器内部空间直接构造对象,避免临时对象的拷贝或移动开销。
总体而言,emplace_back 效率更高,建议优先使用。当传入右值时,emplace_back 与 push_back 效率相近;但在处理 string 等复杂类型时,emplace_back 能一步到位直接构造,而 push_back 可能涉及额外的移动构造步骤。
模拟实现中,通过完美转发将参数包传递给节点构造函数,确保在容器存储位置直接完成 T 对象的构建。
4.1.3 万能引用
在参数包传递过程中,若使用 Args&&... args,需配合 std::forward<Args>(args) 进行完美转发,防止右值引用变量在展开后变为左值。
5. 新的类功能
5.1 默认成员函数:移动构造和移动赋值
C++ 类原本包含 6 个默认成员函数,C++11 新增了两个:移动构造函数和移动赋值运算符重载。
生成条件较为苛刻:若未自定义移动构造函数,且未定义析构函数、拷贝构造函数、拷贝赋值运算符中的任意一个,编译器会自动生成默认移动构造。对于内置类型执行逐字节拷贝,自定义类型则调用其移动构造(若存在),否则回退到拷贝构造。
注意:若提供了移动构造或移动赋值,编译器将不再自动生成拷贝构造和拷贝赋值。
5.2 成员函数声明时的缺省值
成员变量声明时的缺省值会在构造函数初始化阶段生效。若初始化列表中未显式指定,则使用声明时的缺省值;若显式指定,则以初始化列表为准。
5.3 default 和 delete
5.3.1 概念
C++11 允许更精细地控制默认函数的生成。若因定义了其他函数导致某些默认函数未生成(如定义了拷贝构造则不再生成移动构造),可使用 = default 显式请求生成。
若希望禁止某些函数被调用,C++98 需将其设为私有且不实现,C++11 则直接使用 = delete 标记为删除函数,编译期报错。
5.3.2 最佳实践
合理使用 default 和 delete 可明确表达设计意图,防止隐式行为带来的风险。
5.4 目标构造函数和委托构造函数
了解即可。目标构造函数指基类构造函数,委托构造函数指本类构造函数调用同类其他构造函数。二者有助于减少代码重复。
5.5 final 和 override
这两个关键字用于继承体系中的多态控制。
override:强制派生类虚函数重写基类虚函数,若签名不匹配则编译报错。final:阻止后续类进一步重写当前虚函数或继承当前类。
6. C++11:STL 的变化
6.1 新的容器
C++11 新增了 array、forward_list、unordered_map 和 unordered_set。其中真正高频使用的是无序关联容器 unordered_map 和 unordered_set。
6.2 新的接口
重点在于右值引用和移动语义相关的插入接口(push/insert/emplace)、移动构造/赋值以及 initializer_list 版本的构造器。其他如 cbegin/cend 等辅助接口按需查阅文档即可。
6.3 范围 for
范围 for 遍历简化了容器迭代写法,底层依赖迭代器实现,是 STL 交互的重要基础。
7. Lambda 表达式
7.1 Lambda 表达式的语法
Lambda 本质是匿名函数对象,可定义在函数内部。由于没有具体类型名,通常使用 auto 接收。
格式如下:
[capture-list](parameters)->return type { function body }
- capture-list:捕捉列表,决定外部变量的访问方式(传值/传引用)。不能省略。
- parameters:参数列表,若无参可连同括号省略。
- ->return type:返回值类型,可省略由编译器推导。
- function body:函数体,不可省略。
7.2 Lambda 的应用场景
相比函数指针和仿函数,Lambda 定义更简洁。适用于线程执行逻辑、智能指针定制删除器等场景。
例如在排序操作中,无需额外定义仿函数结构体,直接在 sort 中嵌入 Lambda 即可完成比较逻辑。
7.3 捕捉列表
7.3.1 概念
Lambda 默认只能访问自身参数和局部变量。访问外层变量需显式或隐式捕捉。
- 显式捕捉:
[x, y, &z]表示 x、y 传值,z 传引用。 - 隐式捕捉:
[=]隐式传值,[&]隐式传引用。编译器自动识别使用的变量。 - 混合捕捉:
[=, &x]其余传值,x 传引用。首元素必须是=或&。
默认情况下,传值捕捉的对象是 const 的。添加 mutable 修饰符后可修改副本,但不影响实参。
注意:静态局部变量和全局变量无需捕捉,可直接访问。
7.4 Lambda 的原理
Lambda 底层是编译器生成的仿函数类。不同的 Lambda 对应不同的类名,参数、返回类型和函数体映射到仿函数的 operator()。捕捉列表变量成为仿函数类的成员变量,通过构造函数初始化。
C++11 完整代码示例与实践演示
list.h
#pragma once
namespace jqj {
// --------------链表节点结构--------------
template<class T>
struct list_node {
list_node<T>* _next;
list_node<T>* _prev;
T _data;
list_node(const T& x = T()) : _next(nullptr), _prev(nullptr), _data(x) {}
};
// --------------链表迭代器--------------
template<class T, class Ref, class Ptr>
struct list_iterator {
using Self = list_iterator<T, Ref, Ptr>;
using Node = list_node<T>;
Node* _node;
list_iterator(Node* node) : _node(node) {}
Ref operator*() { return _node->_data; }
Ptr operator->() { return &_node->_data; }
Self& operator++() {
_node = _node->_next;
return *this;
}
Self operator++(int) {
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--() {
_node = _node->_prev;
return *this;
}
Self operator--(int) {
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& s) const { return _node != s._node; }
bool operator==(const Self& s) const { return _node == s._node; }
};
// --------------链表主体类--------------
template<class T>
class list {
using Node = list_node<T>;
public:
using iterator = list_iterator<T, T&, T*>;
using const_iterator = list_iterator<T, const T&, const T*>;
iterator begin() { return iterator(_head->_next); }
iterator end() { return iterator(_head); }
const_iterator begin() const { return const_iterator(_head->_next); }
const_iterator end() const { return const_iterator(_head); }
void empty_init() {
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
list() { empty_init(); }
list(initializer_list<T> il) {
empty_init();
for (auto& e : il) push_back(e);
}
template<class InputIterator>
list(InputIterator first, InputIterator last) {
empty_init();
while (first != last) {
push_back(*first);
++first;
}
}
list(size_t n, T val = T()) {
empty_init();
for (size_t i = 0; i < n; ++i) push_back(val);
}
~list() {
clear();
delete _head;
_head = nullptr;
_size = 0;
}
void swap(list<T>& lt) {
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
void clear() {
iterator it = begin();
while (it != end()) {
it = erase(it);
}
}
template<class... Args>
void emplace_back(Args&&... args) {
emplace(end(), forward<Args>(args)...);
}
template<class... Args>
void emplace(iterator pos, Args&&... args) {
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(forward<Args>(args)...);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
void push_back(T&& x) {
insert(end(), forward<T>(x));
}
void push_front(const T& x) {
insert(begin(), x);
}
void pop_back() {
erase(--end());
}
void pop_front() {
erase(begin());
}
void insert(iterator pos, const T& x) {
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
void insert(iterator pos, T&& x) {
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(move(x));
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
iterator erase(iterator pos) {
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
return next;
}
size_t size() const { return _size; }
private:
Node* _head;
size_t _size = 0;
};
}
Test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>
#include <map>
#include <list>
#include <string>
#include <algorithm>
#include <assert.h>
using namespace std;
namespace Alice {
class string {
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin() { return _str; }
iterator end() { return _str + _size; }
const_iterator begin() const { return _str; }
const_iterator end() const { return _str + _size; }
string(const char* str = "") : _size(strlen(str)), _capacity(_size) {
cout << "string(char* str)-构造" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s) {
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string(const string& s) {
cout << "string(char* str)-拷贝构造" << endl;
reserve(s._capacity);
for (auto ch : s) push_back(ch);
}
string(string&& s) {
cout << "string(char* str)-移动构造" << endl;
swap(s);
}
string& operator=(const string& s) {
cout << "string(char* str)-拷贝赋值" << endl;
if (this != &s) {
_str[0] = '\0';
_size = 0;
reserve(s._capacity);
for (auto ch : s) push_back(ch);
}
return *this;
}
string& operator=(string&& s) {
cout << "string(char* str)-移动赋值" << endl;
swap(s);
return *this;
}
~string() {
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos) {
assert(pos < _size);
return _str[pos];
}
void reserve(size_t new_capacity) {
if (new_capacity > _capacity) {
char* tmp = new char[new_capacity + 1];
if (_str) strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = new_capacity;
}
}
void push_back(char ch) {
if (_size >= _capacity) {
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& operator+=(char ch) {
push_back(ch);
return *this;
}
const char* c_str() const { return _str; }
size_t size() const { return _size; }
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
};
}
struct Date {
int _y;
int _m;
int _d;
Date(int year, int month, int day) : _y(year), _m(month), _d(day) {}
};
struct Goods {
string _name;
double _price;
int _evaluate;
Goods(const char* str, double price, int evaluate)
: _name(str), _price(price), _evaluate(evaluate) {}
};
int main() {
// emplace_back 测试
list<Alice::string> lt;
lt.emplace_back("111111111111111111");
// 日期类测试
list<Date> lt_date;
lt_date.emplace_back(2025, 11, 18);
// Lambda 排序测试
vector<Goods> v = {
{"苹果", 2.1, 5},
{"香蕉", 3, 4},
{"橙子", 2.2, 3},
{"菠萝", 1.5, 4}
};
sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {
return gl._price < gr._price;
});
// Lambda 捕获测试
int a = 0, b = 1;
auto func1 = [a, &b]() mutable {
int ret = a + b;
b++;
return ret;
};
cout << func1() << endl;
return 0;
}


