高级C++特性
-
STL(标准模板库):熟练使用STL中的容器(如vector、list、map等)、迭代器、算法和函数对象。
-
异常处理:了解如何使用异常处理机制来提高程序的健壮性。
-
智能指针:掌握智能指针(如unique_ptr、shared_ptr、weak_ptr)的使用,以避免内存泄漏和提高资源管理的安全性。
-
多线程编程:理解并能够使用C++11及以后版本中的多线程库,如std::thread、std::mutex、std::future等。
STL(标准模板库)
C++标准模板库(STL)提供了一系列的容器、迭代器、算法和函数对象,它们是构建高效、可重用代码的基础组件。下面是这些组件的基本使用方法:
容器(Containers)
容器是用来存储数据的数据结构。STL提供了多种容器,包括序列容器(如vector、list、deque)和关联容器(如set、map、unordered_set、unordered_map)。
vector
vector是一个动态数组,支持随机访问。
#include <vector>
int main() {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
for (size_t i = 0; i < v.size(); ++i) {
std::cout << v[i] << " ";
}
std::cout << std::endl;
return 0;
}
list
list是一个双向链表,不支持随机访问,但插入和删除操作很快。
#include <list>
int main() {
std::list<int> l;
l.push_back(1);
l.push_front(2);
l.push_back(3);
for (auto it = l.begin(); it != l.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
map
map是一个关联容器,它存储键值对,并根据键自动排序。
#include <map>
int main() {
std::map<std::string, int> m;
m["one"] = 1;
m["two"] = 2;
m["three"] = 3;
for (auto it = m.begin(); it != m.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
return 0;
}
迭代器(Iterators)
迭代器是用于遍历容器中元素的对象。它们提供了一种统一的方式来访问容器中的元素,而不需要了解容器的内部实现。
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3};
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
算法(Algorithms)
STL提供了大量的算法,这些算法可以作用于各种容器。它们通常通过迭代器来访问容器中的元素。
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {3, 1, 2};
std::sort(v.begin(), v.end());
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
函数对象(Function Objects)
函数对象(也称为仿函数)是重载了函数调用运算符()的对象。它们可以像函数一样被调用,并且可以存储状态。
#include <vector>
#include <algorithm>
struct Compare {
bool operator()(int a, int b) const {
return a < b;
}
};
int main() {
std::vector<int> v = {3, 1, 2};
std::sort(v.begin(), v.end(), Compare());
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在这个例子中,Compare是一个函数对象,它定义了一个比较操作,用于std::sort算法。
总结
STL的容器、迭代器、算法和函数对象是C++泛型编程的核心组件。它们提供了一种高效、可重用的方式来处理数据,使得程序员可以专注于算法的设计,而不需要重复实现基本的数据结构和操作。通过合理地使用STL,可以显著提高代码的质量和开发效率。
异常处理
在C++中,异常处理机制是一种用于处理程序运行时错误的强大工具,它可以帮助提高程序的健壮性。通过合理地使用异常处理,可以确保程序在遇到不可预见的错误时能够优雅地处理,而不是直接崩溃。以下是C++中异常处理的基本概念和使用方法:
基本概念
异常(Exception):程序运行时出现的错误或异常情况。
抛出异常(Throwing an Exception):当检测到异常情况时,使用throw关键字抛出一个异常对象。
捕获异常(Catching an Exception):使用try和catch块来捕获并处理异常。
异常类(Exception Classes):可以自定义异常类,也可以使用标准异常类。
基本语法
try {
// 可能抛出异常的代码
} catch (exceptionType1& e) {
// 处理 exceptionType1 类型的异常
} catch (exceptionType2& e) {
// 处理 exceptionType2 类型的异常
} catch (...) {
// 处理所有其他类型的异常
}
示例
#include <iostream>
#include <exception>
class MyException : public std::exception {
public:
const char* what() const noexcept override {
return "Something bad happened!";
}
};
void mayCauseException() {
throw MyException(); // 抛出自定义异常
}
int main() {
try {
mayCauseException();
} catch (const MyException& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Caught standard exception: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Caught unknown exception" << std::endl;
}
return 0;
}
提高程序健壮性的建议
-
合理设计异常类:根据程序可能出现的错误类型,设计合适的异常类层次结构。
-
及时清理资源:在try块中分配的资源,在catch块中应该被正确释放,可以使用RAII(Resource Acquisition Is Initialization)技术。
-
避免过度使用异常:异常应该用于处理真正的异常情况,而不是作为常规流程控制的一部分。
-
使用标准异常类:尽可能使用C++标准库提供的异常类,如std::exception、std::runtime_error等。
-
异常安全:确保异常抛出时,程序的状态是安全的,不会导致资源泄露或数据损坏。
通过上述方法,可以有效地利用C++的异常处理机制来提高程序的健壮性和可靠性。
智能指针
在C++中,智能指针是一种用于自动管理动态分配内存的工具,它们可以帮助避免内存泄漏并提高资源管理的安全性。C++11引入了三种主要的智能指针:unique_ptr、shared_ptr和weak_ptr。
unique_ptr
unique_ptr是一个独占所有权的智能指针,它确保同一时间只有一个unique_ptr指向一个对象。当unique_ptr离开作用域或被重新赋值时,它所指向的对象会被自动删除。
#include <iostream>
#include <memory>
int main() {
// 创建一个 unique_ptr
std::unique_ptr<int> uptr(new int(42));
// 不能直接复制 unique_ptr
// std::unique_ptr<int> uptr2 = uptr; // 错误
// 可以通过移动语义转移所有权
std::unique_ptr<int> uptr2 = std::move(uptr);
if (!uptr) {
std::cout << "uptr is empty" << std::endl;
}
std::cout << "uptr2 points to " << *uptr2 << std::endl;
return 0;
}
shared_ptr
shared_ptr是一个共享所有权的智能指针,它允许多个shared_ptr指向同一个对象。对象的生命周期由引用计数管理,当最后一个指向对象的shared_ptr被销毁时,对象会被删除。
#include <iostream>
#include <memory>
int main() {
// 创建一个 shared_ptr
std::shared_ptr<int> sptr1(new int(42));
std::cout << "sptr1 use count: " << sptr1.use_count() << std::endl; // 输出 1
{
std::shared_ptr<int> sptr2 = sptr1;
std::cout << "sptr2 use count: " << sptr2.use_count() << std::endl; // 输出 2
}
std::cout << "sptr1 use count after sptr2 goes out of scope: " << sptr1.use_count() << std::endl; // 输出 1
return 0;
}
weak_ptr
weak_ptr是一个弱引用智能指针,它通常与shared_ptr一起使用,用于解决循环引用的问题。weak_ptr不控制对象的生命周期,它只是观察shared_ptr所指向的对象。
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sptr(new int(42));
std::weak_ptr<int> wptr(sptr);
if (auto ptr = wptr.lock()) {
std::cout << "Object is still alive, value: " << *ptr << std::endl;
} else {
std::cout << "Object is dead" << std::endl;
}
sptr.reset();
if (auto ptr = wptr.lock()) {
std::cout << "Object is still alive, value: " << *ptr << std::endl;
} else {
std::cout << "Object is dead" << std::endl;
}
return 0;
}
总结
智能指针是C++中管理动态内存的有效工具,它们可以自动处理内存的分配和释放,从而避免内存泄漏。unique_ptr适用于独占资源的情况,shared_ptr适用于共享资源的情况,而weak_ptr则用于解决shared_ptr可能导致的循环引用问题。合理使用这些智能指针可以显著提高C++程序的健壮性和安全性。
多线程编程
C++11引入了标准的多线程库,使得在C++中进行并发编程变得更加容易和安全。这个库包括了线程管理、互斥量、条件变量、原子操作和异步操作等功能。下面是一些主要组件的使用介绍:
std::thread
std::thread用于创建和管理线程。你可以通过它来启动一个新的线程,并执行一个函数。
#include <iostream>
#include <thread>
void threadFunction() {
std::cout << "Hello from a thread!" << std::endl;
}
int main() {
std::thread t(threadFunction);
t.join(); // 等待线程结束
return 0;
}
std::mutex
std::mutex是互斥量,用于保护共享资源,防止多个线程同时访问。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void threadFunction() {
mtx.lock();
std::cout << "Hello from a thread!" << std::endl;
mtx.unlock();
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
std::lock_guard 和 std::unique_lock
std::lock_guard和std::unique_lock是互斥量的RAII风格的包装,它们在构造时自动锁定互斥量,在析构时自动解锁,确保了互斥量的正确管理。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void threadFunction() {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Hello from a thread!" << std::endl;
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
std::future 和 std::promise
std::future和std::promise用于在线程之间传递结果。std::promise用于设置值,而std::future用于获取这个值。
#include <iostream>
#include <thread>
#include <future>
void setPromise(std::promise<int>&& prom) {
prom.set_value(42);
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t(setPromise, std::move(prom));
std::cout << "Result: " << fut.get() << std::endl;
t.join();
return 0;
}
std::async
std::async是一个便捷的函数,它启动一个异步任务,并返回一个std::future对象,你可以通过这个对象获取任务的结果。
#include <iostream>
#include <future>
int compute() {
return 42;
}
int main() {
std::future<int> fut = std::async(compute);
std::cout << "Result: " << fut.get() << std::endl;
return 0;
}
C++11及以后版本的多线程库提供了丰富的并发编程工具,包括线程管理、同步原语和异步操作等。合理使用这些工具可以编写出高效且安全的并发程序。在编写多线程代码时,应该注意避免数据竞争和死锁,确保线程间的同步和通信是正确和高效的。