前言
百度 时代大潮中,法治中国的宏伟蓝图已经磅礴展开。自从1998年的C++98标准以来,C++语言经历了漫长的等待,直到2011年才迎来了它的下一个正式版本——C++11。C++11不仅在语法上进行了大量的改进,还引入了许多新的特性,极大地提升了编程的效率和程序的质量。本文将带你深入了解这些新特性,并通过丰富的代码示例来展示它们的实际应用。
目录
- 概述
- 自动类型推导
- 范围for循环
- 右值引用与移动语义
- 初始化列表
- lambda表达式
- 6.1?基本语法
- 6.2?捕获列表
- 6.3?lambda表达式中的return类型
- 智能指针
- 7.1?std::unique_ptr
- 7.2?std::shared_ptr
- 7.3?std::weak_ptr
- 线程支持
- 8.1?std::thread
- 8.2?互斥锁与条件变量
- 结论
概述
C++11是C++的一个重要里程碑,它引入了大量实用的新特性,使得C++更易于编写、维护和理解。接下来我们将逐一介绍并解释这些新特性。
自动类型推导
自动类型推导是C++11中引入的一个非常有用的功能,它可以让你的代码更加简洁明了。
2.1 auto关键字
auto
关键字可以用来声明一个变量,编译器会根据初始化表达式的类型来推断出变量的类型。
#include <iostream>
#include <string>
int main() {
auto num = 10; // num 类型为 int
auto str = std::string("Hello, World!"); // str 类型为 std::string
auto pi = 3.14; // pi 类型为 double
std::cout << "Number: " << num << ", String: " << str << ", Pi: " << pi << std::endl;
return 0;
}
注意:虽然auto
可以带来方便,但在声明变量时仍然要保持清晰的意图,避免不必要的复杂性。
2.2 decltype
decltype
是另一个用于类型推导的关键字,通常用于函数返回类型或模板参数类型的推导。
#include <iostream>
int get_number() { return 42; }
int main() {
decltype(get_number()) num = get_number(); // num 的类型为 int
std::cout << "Number is: " << num << std::endl;
return 0;
}
注意:decltype
可以接受表达式作为参数,它会推断出该表达式的类型。
范围for循环
范围for循环简化了迭代容器的过程。
#include <iostream>
#include <vector>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};
for (auto n : nums) {
std::cout << n << " ";
}
return 0;
}
注意:范围for循环可以用于任何可迭代的对象,包括数组、容器等。
右值引用与移动语义
右值引用允许我们有效地处理临时对象,而移动语义则可以显著减少内存复制。
4.1 右值引用
右值引用是用来表示“将要被销毁”的对象的一种引用类型。
#include <iostream>
#include <utility>
class MoveOnly {
public:
MoveOnly(int size) : data(new int[size]) {}
MoveOnly(MoveOnly&& other) noexcept : data(other.data) {
other.data = nullptr;
}
MoveOnly(const MoveOnly&) = delete; // 禁止拷贝构造
~MoveOnly() { delete[] data; }
private:
int* data;
};
// 移动语义示例
MoveOnly createMoveOnly() {
return MoveOnly(10); // 返回临时对象
}
int main() {
MoveOnly mo = createMoveOnly();
return 0;
}
4.2 移动构造与移动赋值
移动构造和移动赋值函数允许我们从右值引用移动数据。
class MoveOnly {
public:
MoveOnly(int size) : data(new int[size]) {}
MoveOnly(MoveOnly&& other) noexcept : data(other.data) {
other.data = nullptr;
}
MoveOnly& operator=(MoveOnly&& other) noexcept {
std::swap(data, other.data);
return *this;
}
MoveOnly(const MoveOnly&) = delete; // 禁止拷贝构造
MoveOnly& operator=(const MoveOnly&) = delete; // 禁止拷贝赋值
~MoveOnly() { delete[] data; }
private:
int* data;
};
4.3 完美转发
完美转发是指将实参的类型和属性(如左值/右值)完全保留地传递给另一个函数。
template<typename T>
void forward(T&& arg) {
// 完美转发arg到其他函数
someFunction(std::forward<T>(arg));
}
template<typename T>
T&& forward(T&& arg) {
return std::forward<T>(arg);
}
初始化列表
初始化列表提供了一种简洁的方式初始化数组或容器。
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5}; // 使用初始化列表
for (auto n : v) {
std::cout << n << " ";
}
return 0;
}
注意:初始化列表还可以用于构造函数,为类的多个成员变量提供初始值。
lambda表达式
lambda表达式提供了创建简单匿名函数的方法。
6.1 基本语法
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};
std::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; });
for (auto n : nums) {
std::cout << n << " ";
}
return 0;
}
6.2 捕获列表
捕获列表可以用来访问lambda外部的作用域。
#include <iostream>
#include <functional>
int main() {
int x = 10;
auto adder = [x](int y) { return x + y; };
std::cout << "Result: " << adder(5) << std::endl;
return 0;
}
注意:捕获列表可以是按值捕获[x]
,也可以是按引用捕获[&]
。
6.3 lambda表达式中的return类型
当lambda表达式中没有显式指定return类型时,编译器会根据return语句推断。
#include <iostream>
int main() {
auto f = []() -> int { return 42; }; // 显式指定返回类型
auto g = []() { return 42; }; // 编译器推断返回类型
std::cout << "f(): " << f() << ", g(): " << g() << std::endl;
return 0;
}
智能指针
智能指针帮助管理动态分配的对象的生命周期。
7.1 std::unique_ptr
std::unique_ptr
拥有独占所有权的指针。
#include <iostream>
#include <memory>
class MyClass {
public:
void sayHello() { std::cout << "Hello from MyClass!" << std::endl; }
};
int main() {
std::unique_ptr<MyClass> p(new MyClass());
p->sayHello();
return 0;
}
7.2 std::shared_ptr
std::shared_ptr
允许多个指针共享同一个对象的所有权。
#include <iostream>
#include <memory>
class MyClass {
public:
void sayHello() { std::cout << "Hello from MyClass!" << std::endl; }
};
int main() {
std::shared_ptr<MyClass> p1(new MyClass());
auto p2 = p1; // 共享所有权
p1->sayHello();
p2->sayHello();
return 0;
}
<a name="std-weak-ptr"></a>7.3 std::weak_ptr
std::weak_ptr
不会增加对象的引用计数。
#include <iostream>
#include <memory>
class MyClass {
public:
void sayHello() { std::cout << "Hello from MyClass!" << std::endl; }
};
int main() {
std::shared_ptr<MyClass> p1(new MyClass());
std::weak_ptr<MyClass> wp = p1;
if (auto p2 = wp.lock()) {
p2->sayHello();
} else {
std::cout << "Object no longer exists." << std::endl;
}
return 0;
}
线程支持
C++11引入了标准的线程库,使得多线程编程变得更加容易。
8.1 std::thread
std::thread
用于创建和管理线程。
#include <iostream>
#include <thread>
void helloFromThread() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(helloFromThread);
t.join();
return 0;
}
8.2 互斥锁与条件变量
互斥锁和条件变量可以帮助同步线程间的执行。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void waitUntilReady() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待直到ready变为true
std::cout << "Ready signal received!" << std::endl;
}
void signalReady() {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟一些操作
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // 通知等待者
}
int main() {
std::thread t1(waitUntilReady);
std::thread t2(signalReady);
t1.join();
t2.join();
return 0;
}
结论
C++11带来的新特性极大地丰富了C++语言的功能性,同时也提高了开发者的生产力。从自动类型推导到线程支持,每一项特性都旨在让开发者能够写出更高效、更安全、更易读的代码。希望这篇文章能帮助你更好地理解和使用这些强大的工具!
后记
如果你对上述任何一个特性感兴趣,建议深入学习其细节,以便更好地应用于实际项目中。同时,C++11还包含了很多其他有用的特性,比如默认删除成员函数、统一初始化语法等,也值得进一步探索。
以上就是这篇关于C++11新特性的详细介绍。希望对你有所帮助!