C++11:现代C++的革命
一、核心语言特性
1. 自动类型推导(auto)
// 旧方式
std::vector<QString>::iterator it = vec.begin();
std::map<int, QString>::const_iterator cit = map.begin();
// C++11
auto it = vec.begin(); // 推导为 std::vector<QString>::iterator
auto cit = map.begin(); // 推导为 std::map<int, QString>::const_iterator
auto name = QString("Alice"); // 推导为 QString
auto count = 42; // 推导为 int
auto pi = 3.14159; // 推导为 double
优势:
-
简化复杂类型声明
-
减少输入错误
-
提高模板代码的可读性
2. 基于范围的for循环
QList<QString> names = {"Alice", "Bob", "Charlie"};
// 旧方式
for (int i = 0; i < names.size(); ++i) {
qDebug() << names[i];
}
// C++11
for (const QString& name : names) {
qDebug() << name;
}
// 配合auto
for (const auto& name : names) {
qDebug() << name;
}
// 修改元素
for (auto& name : names) {
name = name.toUpper();
}
3. Lambda表达式
// 基本语法:[捕获列表](参数列表) -> 返回类型 { 函数体 }
// 简单Lambda
auto print = []() { qDebug() << "Hello"; };
print();
// 带参数
auto add = [](int a, int b) -> int { return a + b; };
auto sum = add(3, 4);
// 捕获局部变量
int factor = 2;
auto multiply = [factor](int x) { return x * factor; };
// 引用捕获
QString message = "Hello";
auto modify = [&message]() { message.append(" World!"); };
// 在Qt中的应用
QTimer::singleShot(1000, []() { qDebug() << "1秒后执行"; });
QList<int> numbers = {1, 5, 3, 4, 2};
std::sort(numbers.begin(), numbers.end(),
[](int a, int b) { return a > b; }); // 降序排序
4. 右值引用和移动语义
class MyString {
char* data;
public:
// 移动构造函数
MyString(MyString&& other) noexcept
: data(other.data) {
other.data = nullptr; // 转移所有权
}
// 移动赋值运算符
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
other.data = nullptr;
}
return *this;
}
};
// std::move将左值转为右值
QString createString() {
QString str("大量数据");
return str; // 触发移动构造而不是拷贝
}
QString s1 = createString(); // 移动构造
QString s2 = std::move(s1); // s1现在为空,内容移动到s2
5. 智能指针
#include <memory>
// unique_ptr:独占所有权
std::unique_ptr<QWidget> widget(new QWidget());
auto dialog = std::unique_ptr<QDialog>(new QDialog());
// 转移所有权
auto widget2 = std::move(widget); // widget变为nullptr
// shared_ptr:共享所有权
std::shared_ptr<QObject> obj1 = std::make_shared<QObject>();
std::shared_ptr<QObject> obj2 = obj1; // 引用计数+1
// weak_ptr:弱引用,不增加引用计数
std::weak_ptr<QObject> weakObj = obj1;
if (auto sharedObj = weakObj.lock()) {
// 使用sharedObj
}
6. nullptr
// 旧方式的问题
void func(int) { qDebug() << "int version"; }
void func(char*) { qDebug() << "pointer version"; }
func(NULL); // 可能调用int版本(如果NULL是0)
func(nullptr); // 明确调用指针版本
// 在Qt中
QWidget* widget = nullptr; // 替代NULL
if (widget != nullptr) {
widget->show();
}
7. 强类型枚举
// 旧枚举的问题
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // 冲突!
// C++11枚举类
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green };
Color c = Color::Red;
TrafficLight t = TrafficLight::Red; // 无冲突
// 指定底层类型
enum class Status : uint8_t {
Ok = 0,
Error = 1,
Warning = 2
};
8. 委托构造函数和继承构造函数
class Widget : public QWidget {
public:
// 委托构造函数
Widget(QWidget* parent = nullptr)
: Widget("Default", parent) {} // 委托给下面的构造函数
Widget(const QString& name, QWidget* parent = nullptr)
: QWidget(parent), name(name) {}
private:
QString name;
};
// 继承构造函数
class MyLineEdit : public QLineEdit {
public:
using QLineEdit::QLineEdit; // 继承所有父类构造函数
};
9. override和final
class Base {
public:
virtual void show() { qDebug() << "Base"; }
virtual void display() final {} // 禁止派生类重写
};
class Derived : public Base {
public:
void show() override { // 明确表示重写
qDebug() << "Derived";
}
// void display() {} // 错误!final函数不能重写
};
10. 变长参数模板
template<typename... Args>
void log(Args... args) {
// 可以使用递归或折叠表达式(C++17)处理参数
(qDebug() << ... << args); // C++17折叠表达式
}
// 使用
log("Error:", 404, "at line", __LINE__);
C++14:完善和精炼
一、核心语言特性
1. 泛型Lambda
// C++11:参数类型必须明确
auto lambda11 = [](int x, double y) { return x + y; };
// C++14:参数类型可以用auto
auto lambda14 = [](auto x, auto y) { return x + y; };
// 使用示例
auto adder = [](auto a, auto b) { return a + b; };
qDebug() << adder(5, 3); // 8
qDebug() << adder(2.5, 3.7); // 6.2
qDebug() << adder(QString("A"), QString("B")); // "AB"
// 更复杂的例子
auto processor = [](auto container) {
for (const auto& item : container) {
qDebug() << item;
}
};
processor(QList<int>{1, 2, 3});
processor(QVector<QString>{"a", "b", "c"});
2. Lambda捕获表达式
// C++11只能捕获现有变量
int x = 10;
auto lambda11 = [x]() { return x * 2; };
// C++14可以初始化捕获
int y = 5;
auto lambda14_1 = [value = x + y]() { return value; }; // value = 15
// 移动捕获(重要!)
auto data = std::make_unique<QByteArray>(1000, 'A');
auto lambda14_2 = [ptr = std::move(data)]() {
// ptr拥有数据所有权,data现在为空
return ptr->size();
};
// 引用重命名
QString str = "Hello";
auto lambda14_3 = [&s = str]() {
s.append(" World!");
return s;
};
3. 函数返回类型推导
// 普通函数的auto返回类型
auto add(int a, int b) { // 编译器推导返回int
return a + b;
}
auto createWidget() { // 返回QWidget*
return new QWidget();
}
auto getConfig() -> std::map<QString, QVariant> {
return {{"width", 800}, {"height", 600}};
}
// 配合decltype(auto)保留引用和const
QString globalName = "Global";
decltype(auto) getNameRef() {
return (globalName); // 返回QString&,注意括号!
}
const QString& getConstName() {
static QString name = "Static";
return name;
}
decltype(auto) nameRef = getConstName(); // 推导为const QString&
4. 放宽constexpr限制
// C++11: constexpr函数只能有一条return语句
constexpr int factorial11(int n) {
return n <= 1 ? 1 : n * factorial11(n - 1);
}
// C++14: constexpr函数可以有局部变量、循环等
constexpr int factorial14(int n) {
int result = 1;
for (int i = 2; i <= n; ++i) {
result *= i;
}
return result;
}
constexpr int arraySize(int multiplier) {
int size = 10;
if (multiplier > 1) {
size *= multiplier;
}
return size;
}
// 编译期计算数组大小
std::array<int, factorial14(5)> arr1; // 120个元素的数组
std::array<double, arraySize(3)> arr2; // 30个元素的数组
5. 二进制字面量和数字分隔符
// 二进制字面量
uint8_t flags = 0b10101010; // 0xAA
uint16_t mask = 0b1111000011110000; // 0xF0F0
// 数字分隔符(提高可读性)
int million = 1'000'000;
double pi = 3.14159'26535'89793;
long bigNumber = 9'223'372'036'854'775'807LL;
// 在Qt中的实用示例
enum Permissions : uint8_t {
None = 0b0000'0000,
Read = 0b0000'0001,
Write = 0b0000'0010,
Execute = 0b0000'0100,
All = 0b0000'0111
};
const uint32_t ColorARGB = 0xFF'88'00'44; // ARGB颜色值
const int MaxBufferSize = 64'000'000; // 64MB
二、标准库增强
1. std::make_unique
// C++11只有std::make_shared
std::unique_ptr<QWidget> p1(new QWidget()); // 旧方式
// C++14引入make_unique
auto p2 = std::make_unique<QWidget>();
auto list = std::make_unique<QList<int>>(10, 0); // 10个0
// 带参数构造
auto button = std::make_unique<QPushButton>("Click Me");
auto dialog = std::make_unique<QFileDialog>(
nullptr,
"Select File",
QDir::homePath(),
"Images (*.png *.jpg)"
);
// 数组版本
auto arr = std::make_unique<int[]>(100); // 100个int的数组
2. std::exchange - 交换并返回旧值
#include <utility>
class ResourceHolder {
std::unique_ptr<QWidget> resource;
public:
// 设置新资源,返回旧资源
std::unique_ptr<QWidget> replaceResource(
std::unique_ptr<QWidget> newResource) {
// 交换并返回旧的
return std::exchange(resource, std::move(newResource));
}
// 清空资源并返回
std::unique_ptr<QWidget> releaseResource() {
return std::exchange(resource, nullptr);
}
};
// 使用示例
ResourceHolder holder;
auto oldWidget = holder.replaceResource(
std::make_unique<QWidget>()); // 设置新widget,得到旧widget
3. std::integer_sequence(编译期整数序列)
#include <utility>
// 展开参数包的辅助工具
template<typename T, T... Ints>
void printSequence(std::integer_sequence<T, Ints...>) {
((std::cout << Ints << ' '), ...); // C++17折叠表达式
}
// 生成索引序列用于访问tuple
template<typename Tuple, size_t... Is>
void printTuple(const Tuple& t, std::index_sequence<Is...>) {
((std::cout << std::get<Is>(t) << ' '), ...);
}
// 使用
printSequence(std::integer_sequence<int, 1, 2, 3, 4>{});
auto t = std::make_tuple(1, 2.0, "three");
printTuple(t, std::index_sequence<0, 1, 2>{});
C++17:重大功能增强
一、核心语言特性
1. 结构化绑定
// 从pair/tuple解包
std::pair<int, QString> result = {42, "Answer"};
auto [code, message] = result; // code=42, message="Answer"
// 从结构体解包
struct Point { int x; int y; };
Point p{10, 20};
auto [x, y] = p; // x=10, y=20
// 从map遍历
std::map<int, QString> m{{1, "one"}, {2, "two"}};
for (const auto& [key, value] : m) {
qDebug() << key << "->" << value;
}
// 引用绑定
auto& [refX, refY] = p; // refX和refY是p.x和p.y的引用
refX = 100; // 修改p.x
// 在Qt中的应用
QMap<QString, QVariant> config;
if (auto it = config.find("timeout"); it != config.end()) {
auto& [key, value] = *it; // key是QString, value是QVariant
int timeout = value.toInt();
}
2. if/switch初始化语句
// 旧方式
auto it = container.find(key);
if (it != container.end()) {
use(*it);
}
// C++17:将初始化限制在if作用域内
if (auto it = container.find(key); it != container.end()) {
use(*it); // it的作用域仅限于if和else
} else {
// 这里也可以访问it
qDebug() << "Key not found";
}
// it在这里已经销毁
// 多个初始化
if (auto x = getValue(); x > 0 && x < 100) {
qDebug() << "Valid range:" << x;
}
// switch语句同样适用
switch (auto code = getErrorCode(); code) {
case 0: qDebug() << "Success"; break;
case 1: qDebug() << "Error:" << code; break;
default: qDebug() << "Unknown code"; break;
}
// 实用示例:文件操作
if (std::ifstream file("data.txt"); file.is_open()) {
std::string line;
while (std::getline(file, line)) {
process(line);
}
} // file自动关闭
3. 内联变量
// 头文件 widgetconstants.h
#pragma once
// C++11/14需要在cpp文件中定义
// C++17可以直接在头文件中定义
inline const QString AppName = "MyApp";
inline constexpr int DefaultWidth = 800;
inline constexpr int DefaultHeight = 600;
namespace Colors {
inline const QColor Background = QColor(240, 240, 240);
inline const QColor Text = QColor(0, 0, 0);
inline const QColor Highlight = QColor(255, 200, 0);
}
// 类中的静态成员
class Config {
public:
static inline int MaxConnections = 100;
static inline QString LogPath = "/var/log/myapp/";
};
// 使用:包含头文件即可,无需链接定义
4. 折叠表达式
// 变参模板的参数包展开
template<typename... Args>
auto sum(Args... args) {
return (... + args); // 左折叠:((arg1 + arg2) + arg3)...
}
template<typename... Args>
auto sumRight(Args... args) {
return (args + ...); // 右折叠:arg1 + (arg2 + arg3)...
}
// 使用
qDebug() << sum(1, 2, 3, 4, 5); // 15
qDebug() << sumRight(1, 2, 3, 4, 5); // 15
// 逻辑操作
template<typename... Args>
bool allTrue(Args... args) {
return (... && args); // 所有参数为true
}
template<typename... Args>
bool anyTrue(Args... args) {
return (... || args); // 任一参数为true
}
// 输出所有参数
template<typename... Args>
void printAll(Args... args) {
(qDebug() << ... << args); // 输出所有参数
}
// 调用函数
template<typename... Args>
void callAll(Args... args) {
(args(), ...); // 依次调用每个函数
}
5. constexpr if(编译期if)
// 根据类型在编译期选择代码路径
template<typename T>
auto process(T value) {
if constexpr (std::is_pointer_v<T>) {
qDebug() << "Pointer:" << *value;
return *value;
} else if constexpr (std::is_integral_v<T>) {
qDebug() << "Integer:" << value;
return value * 2;
} else if constexpr (std::is_same_v<T, QString>) {
qDebug() << "QString:" << value;
return value.toUpper();
} else {
qDebug() << "Unknown type";
return value;
}
}
// 使用
int x = 42;
process(x); // 调用整数版本
process(new int(100)); // 调用指针版本
process(QString("hello")); // 调用QString版本
// 编译期递归的终止条件
template<typename T, typename... Args>
void printArgs(T first, Args... rest) {
qDebug() << first;
if constexpr (sizeof...(rest) > 0) {
printArgs(rest...); // 只在有剩余参数时递归
}
}
6. 嵌套命名空间简化
// 旧方式
namespace Company {
namespace Project {
namespace Module {
class Widget {};
}
}
}
// C++17简化
namespace Company::Project::Module {
class Widget {};
}
// 配合内联命名空间
namespace MyLib::v1::Utils { // 简洁明了
void helper() {}
}
二、标准库新容器和工具
1. std::optional
#include <optional>
// 表示"可能有值"的对象
std::optional<QString> findUserName(int id) {
if (id > 0 && id < 1000) {
return QString("User%1").arg(id);
}
return std::nullopt; // 无值
}
// 使用
auto name = findUserName(42);
if (name.has_value()) { // 或 if (name)
qDebug() << "Found:" << name.value();
qDebug() << "Found:" << *name; // 解引用
} else {
qDebug() << "Not found";
}
// 提供默认值
QString userName = findUserName(9999).value_or("Guest");
// 就地构造
std::optional<QByteArray> data;
data.emplace(1024, 'A'); // 构造QByteArray(1024, 'A')
// 与指针的区别:明确表达意图,避免空指针歧义
2. std::variant
#include <variant>
#include <string>
// 类型安全的联合体
using Value = std::variant<int, double, QString, bool>;
Value v1 = 42; // 存储int
Value v2 = 3.14; // 存储double
Value v3 = QString("Hello"); // 存储QString
Value v4 = true; // 存储bool
// 访问值
qDebug() << std::get<int>(v1); // 获取int值
qDebug() << std::get<QString>(v3); // 获取QString值
// 安全访问
if (auto intPtr = std::get_if<int>(&v1)) {
qDebug() << "Is int:" << *intPtr;
}
// std::visit访问(需要访问者模式)
struct Visitor {
void operator()(int i) const { qDebug() << "int:" << i; }
void operator()(double d) const { qDebug() << "double:" << d; }
void operator()(const QString& s) const { qDebug() << "QString:" << s; }
void operator()(bool b) const { qDebug() << "bool:" << b; }
};
std::visit(Visitor{}, v1); // 根据实际类型调用对应函数
// C++17配合泛型Lambda
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>) {
qDebug() << "Integer:" << arg;
} else if constexpr (std::is_same_v<T, QString>) {
qDebug() << "String:" << arg;
}
}, v1);
3. std::any
#include <any>
// 可以存储任意类型的容器
std::any anything;
anything = 42; // 存储int
anything = QString("Hello"); // 存储QString
anything = std::vector<int>{1,2,3}; // 存储vector
// 检查类型
if (anything.type() == typeid(int)) {
qDebug() << "Contains int";
}
// 获取值
try {
int value = std::any_cast<int>(anything);
qDebug() << "Value:" << value;
} catch (const std::bad_any_cast& e) {
qDebug() << "Wrong type!";
}
// 指针版本(不抛出异常)
if (auto ptr = std::any_cast<QString>(&anything)) {
qDebug() << "QString:" << *ptr;
}
// 重置
anything.reset(); // 变为空
if (!anything.has_value()) {
qDebug() << "Empty";
}
4. std::string_view
#include <string_view>
// 字符串的只读视图,不拥有数据
void printString(std::string_view sv) {
qDebug() << sv.data() << "(length:" << sv.length() << ")";
}
// 可以从多种类型构造
QString qstr = "Hello Qt";
std::string stdstr = "Hello STL";
char cstr[] = "Hello C";
printString("Literal"); // 字符串字面量
printString(qstr.toStdString()); // std::string
printString(cstr); // C字符串
printString(stdstr); // std::string
// 高效,避免拷贝
std::string_view view = qstr.toStdString(); // 不复制数据
qDebug() << view.substr(0, 5); // "Hello" - 子串也不复制
// 注意:要确保原字符串的生命周期
5. std::filesystem
#include <filesystem>
namespace fs = std::filesystem;
// 路径操作
fs::path p = "/home/user/docs/report.txt";
qDebug() << p.filename(); // "report.txt"
qDebug() << p.extension(); // ".txt"
qDebug() << p.parent_path(); // "/home/user/docs"
// 文件操作
if (fs::exists(p)) {
qDebug() << "File exists, size:" << fs::file_size(p);
// 拷贝
fs::copy(p, "/tmp/backup.txt");
// 重命名
fs::rename("/tmp/backup.txt", "/tmp/report_backup.txt");
// 删除
fs::remove("/tmp/report_backup.txt");
}
// 目录遍历
for (const auto& entry : fs::directory_iterator("/home/user")) {
qDebug() << entry.path();
if (entry.is_regular_file()) {
qDebug() << "File size:" << entry.file_size();
}
}
// 创建目录
fs::create_directories("/tmp/myapp/logs"); // 创建多级目录
6. 并行算法
#include <algorithm>
#include <execution>
std::vector<int> data(1000000);
// 顺序执行(传统方式)
std::sort(data.begin(), data.end());
// 并行执行
std::sort(std::execution::par, data.begin(), data.end());
// 更多并行算法
std::for_each(std::execution::par, data.begin(), data.end(),
[](int& n) { n *= 2; });
std::transform(std::execution::par,
data.begin(), data.end(), data.begin(),
[](int n) { return n * n; });
auto sum = std::reduce(std::execution::par,
data.begin(), data.end(), 0);
// 执行策略:
// - seq: 顺序执行(默认)
// - par: 并行执行
// - par_unseq: 并行+向量化
三、在Qt开发中的实用组合
模式1:安全配置读取
#include <optional>
class Config {
std::map<QString, QVariant> settings;
public:
template<typename T>
std::optional<T> get(const QString& key) const {
if (auto it = settings.find(key); it != settings.end()) {
if (auto value = it->second.value<T>(); value) {
return *value;
}
}
return std::nullopt;
}
template<typename T>
T getOr(const QString& key, T defaultValue) const {
return get<T>(key).value_or(defaultValue);
}
};
// 使用
Config config;
auto timeout = config.get<int>("timeout"); // optional<int>
if (timeout) {
qDebug() << "Timeout:" << *timeout;
}
int width = config.getOr("width", 800); // 默认800
QString title = config.getOr("title", QString("Default"));
模式2:类型安全的消息处理
#include <variant>
using Message = std::variant<
struct ConnectMessage { QString address; int port; },
struct DataMessage { QByteArray data; },
struct CommandMessage { QString command; QVariant args; },
struct ErrorMessage { int code; QString description; }
>;
class MessageProcessor {
public:
void handle(const Message& msg) {
std::visit([this](auto&& arg) {
this->process(std::forward<decltype(arg)>(arg));
}, msg);
}
private:
void process(const ConnectMessage& msg) {
qDebug() << "Connect to" << msg.address << ":" << msg.port;
}
void process(const DataMessage& msg) {
qDebug() << "Data size:" << msg.data.size();
}
void process(const CommandMessage& msg) {
qDebug() << "Command:" << msg.command;
}
void process(const ErrorMessage& msg) {
qDebug() << "Error" << msg.code << ":" << msg.description;
}
};
模式3:现代工厂模式
#include <memory>
#include <functional>
class WidgetFactory {
std::map<QString, std::function<std::unique_ptr<QWidget>()>> creators;
public:
template<typename T, typename... Args>
void registerType(const QString& name, Args&&... args) {
creators[name] = [args...]() {
return std::make_unique<T>(args...);
};
}
std::unique_ptr<QWidget> create(const QString& name) {
if (auto it = creators.find(name); it != creators.end()) {
return it->second();
}
return nullptr;
}
};
// 使用
WidgetFactory factory;
factory.registerType<QPushButton>("button", "Click Me");
factory.registerType<QLabel>("label", "Hello World");
factory.registerType<QLineEdit>("edit");
auto widget = factory.create("button");
if (widget) widget->show();
总结对比
| 特性类别 | C++11 | C++14 | C++17 |
|---|---|---|---|
| 自动类型 | auto变量 |
auto函数返回类型 |
结构化绑定 |
| Lambda | 基本Lambda | 泛型Lambda, 初始化捕获 | 常量求值Lambda |
| 智能指针 | shared_ptr, unique_ptr |
make_unique |
- |
| 模板 | 变参模板 | 变量模板 | 折叠表达式, if constexpr |
| 编译期计算 | 受限constexpr |
放宽constexpr |
constexpr if, Lambda |
| 新类型 | - | - | optional, variant, any |
| 字符串 | - | - | string_view |
| 文件系统 | - | - | filesystem库 |
| 并发 | 线程库 | - | 并行算法 |
| 语法糖 | 范围for, nullptr | 数字分隔符 | 嵌套命名空间, if初始化 |
学习建议:
-
C++11是基础,必须完全掌握
-
C++14是优化,让代码更简洁
-
C++17是生产力飞跃,提供全新编程范式
在Qt 6开发中,充分利用这些特性可以写出更安全、更高效、更易维护的代码。