欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > C++进阶-【高级语法】

C++进阶-【高级语法】

2025/2/26 22:24:04 来源:https://blog.csdn.net/qq_45161651/article/details/144792619  浏览:    关键词:C++进阶-【高级语法】

模板编程概述

模板编程是 C++ 语言的重要特性之一,允许程序员编写通用、可重用的代码。通过使用模板,可以创建适用于不同数据类型的函数和类,从而提高代码的灵活性和可维护性。本文将详细介绍模板编程的各个方面,包括函数模板、类模板、模板特化和模板元编程。

1.1 模板编程

1.1.1 函数模板

定义与使用

函数模板是一个为不同数据类型提供通用实现的机制。通过定义一个模板,编译器可以根据传入的参数类型生成相应的函数。

示例:

cpp

#include <iostream>
using namespace std;// 定义函数模板
template <typename T>
T add(T a, T b) {return a + b;
}int main() {cout << add(3, 4) << endl;        // 整数相加cout << add(2.5, 3.7) << endl;    // 浮点数相加cout << add(string("Hello, "), string("World!")) << endl; // 字符串相加return 0;
}
过载与特化

函数模板可以被重载,也可以被特化。重载是指在同一作用域中定义多个同名函数模板,特化则是为特定数据类型提供不同实现。

示例:

cpp

// 过载示例
template <typename T>
void print(T value) {cout << value << endl;
}template <>
void print(const char* value) { // 特化示例cout << "C-style string: " << value << endl;
}int main() {print(10);                     // 调用模板print(3.14);                  // 调用模板print("Hello");               // 调用特化return 0;
}

1.1.2 类模板

定义与使用

类模板允许定义一个类的通用版本,以便于处理不同的数据类型。

示例:

cpp

#include <iostream>
using namespace std;// 定义类模板
template <typename T>
class Box {
private:T value;
public:Box(T val) : value(val) {}T getValue() { return value; }
};int main() {Box<int> intBox(123);               // 创建整型 BoxBox<double> doubleBox(456.78);      // 创建浮点型 Boxcout << intBox.getValue() << endl;  // 输出: 123cout << doubleBox.getValue() << endl; // 输出: 456.78return 0;
}
模板参数的非类型参数

类模板可以接受非类型参数,这允许在模板中使用常量值。

示例:

cpp

template <typename T, int size>
class Array {
private:T arr[size]; // 使用非类型模板参数
public:int getSize() const { return size; }
};int main() {Array<int, 10> intArray; // 创建一个大小为 10 的整型数组cout << intArray.getSize() << endl; // 输出: 10return 0;
}

1.1.3 模板特化

全特化与部分特化

模板特化允许为特定类型或特定参数的组合提供不同的实现。全特化是针对特定类型的完全特化,而部分特化则是对模板参数的一部分进行特化。

全特化示例:

cpp

template <>
class Box<int> { // 针对 int 类型的全特化
private:int value;
public:Box(int val) : value(val) {}void display() { cout << "Integer: " << value << endl; }
};

部分特化示例:

cpp

template <typename T>
class Box<T*> { // 针对指针类型的部分特化
private:T* value;
public:Box(T* val) : value(val) {}void display() { cout << "Pointer to value: " << *value << endl; }
};
选择特化的条件

选择特化的条件依赖于模板参数的类型和数量。通常,在需要为特定类型提供优化或不同实现时会选择特化。

1.1.4 模板元编程

模板元编程是一种使用模板进行编译时计算的技术。它允许在编译时执行计算,从而产生高效的代码。

编写编译时计算

示例: 计算阶乘

cpp

template <int N>
struct Factorial {static const int value = N * Factorial<N - 1>::value;
};// 基础情况
template <>
struct Factorial<0> {static const int value = 1;
};int main() {cout << Factorial<5>::value << endl; // 输出: 120return 0;
}
常见应用示例
  • 类型选择:根据条件选择类型。

cpp

template <bool condition, typename T1, typename T2>
struct Conditional {using type = T1; // condition 为 true,选择 T1
};template <typename T1, typename T2>
struct Conditional<false, T1, T2> {using type = T2; // condition 为 false,选择 T2
};// 使用示例
using SelectedType = Conditional<true, int, double>::type; // 选择 int
  • 编写静态断言:在编译时检查条件。

cpp

template <typename T>
void assertIsIntegral() {static_assert(std::is_integral<T>::value, "Template parameter must be an integral type.");
}int main() {assertIsIntegral<int>(); // 正确// assertIsIntegral<double>(); // 错误,编译时断言失败
}

模板编程结论

模板编程是 C++ 中一个强大的特性,能够提高代码的灵活性和重用性。

异常处理概述

异常处理是 C++ 中用于管理运行时错误的重要机制。通过合理使用异常处理,可以提高程序的健壮性和可维护性。本文将详细介绍异常的捕获与抛出、自定义异常类,和 RAII(资源获取即初始化)原则。

1.2 异常处理

1.2.1 异常的捕获与抛出

try-catch 语句的使用

try-catch 语句用于捕获和处理异常。try 块中放置可能抛出异常的代码,而 catch 块中定义了如何处理这些异常。当 try 块中的代码抛出异常时,控制权会转移到对应的 catch 块。

示例:

cpp

#include <iostream>
#include <stdexcept> // std::runtime_error
using namespace std;void mightGoWrong() {throw runtime_error("Something went wrong!"); // 抛出异常
}int main() {try {mightGoWrong(); // 调用可能抛出异常的函数} catch (const runtime_error &e) {cout << "Caught an exception: " << e.what() << endl; // 捕获并处理异常}return 0;
}

说明:

  • runtime_error 是标准库提供的异常类,用于报告运行时错误。
  • what() 方法返回异常的描述信息。
throw 的语法与最佳实践

使用 throw 关键字抛出异常,通常与 try 块结合使用。最佳实践包括:

  • 只在错误情况下抛出:确保只在真实错误条件下抛出异常。
  • 提供清晰的错误信息:抛出异常时,提供有意义的错误消息,便于调试。
  • 使用标准异常类或自定义异常类:尽量使用标准库中的异常类或自定义类型,以便进行类型识别和处理。

示例:

cpp

void riskyFunction() {// 检查条件,抛出异常if (/* error condition */) {throw runtime_error("An error occurred in riskyFunction");}
}
异常安全性原则

异常安全性原则确保在抛出异常时,程序的状态保持一致。常见的异常安全性保证包括:

  • 基本保证:如果异常发生,程序的状态仍然有效,但可能没有完成预期的操作。
  • 强保证:如果异常发生,程序的状态将恢复到异常之前的状态,即所有操作都要么完全成功,要么完全不执行。
  • 不抛出保证:操作不会抛出异常,例如某些简单的 getter 函数。

示例:

cpp

class Resource {
public:Resource() { /* 资源初始化 */ }void doSomething() {if (/* error condition */) {throw runtime_error("Operation failed");}}
};void manageResource() {Resource res;try {res.doSomething();} catch (const runtime_error &e) {// 处理异常cout << "Caught: " << e.what() << endl;}
}

1.2.2 自定义异常类

继承 std::exception

自定义异常类通常继承自 std::exception,并重写 what() 方法以提供错误信息。这种方式不仅可以提供特定的错误信息,还能保持与标准异常机制的兼容性。

示例:

cpp

#include <exception>
#include <string>class MyException : public std::exception {
private:std::string message; // 存储错误信息
public:MyException(const std::string &msg) : message(msg) {}const char* what() const noexcept override { // 重写 what 方法return message.c_str(); // 返回错误信息}
};
添加上下文信息(如错误码、错误消息)

自定义异常类可以添加更多的上下文信息,例如错误码或详细的错误消息,帮助开发者更好地定位问题。

示例:

cpp

class FileReadException : public std::exception {
private:std::string message; // 错误消息int errorCode; // 错误码
public:FileReadException(const std::string &msg, int code) : message(msg), errorCode(code) {}const char* what() const noexcept override {return message.c_str(); // 返回错误消息}int getErrorCode() const { return errorCode; } // 获取错误码
};
示例:实现自定义文件读取异常

以下示例展示了如何实现一个自定义异常类,用于处理文件读取错误。

cpp

#include <iostream>
#include <fstream>
#include <exception>
#include <string>class FileReadException : public std::exception {
private:std::string message; // 错误消息
public:FileReadException(const std::string &msg) : message(msg) {}const char* what() const noexcept override {return message.c_str(); // 返回错误消息}
};void readFile(const std::string &filename) {std::ifstream file(filename);if (!file) {throw FileReadException("Failed to open file: " + filename); // 抛出自定义异常}std::string line;while (std::getline(file, line)) {std::cout << line << std::endl; // 读取并处理文件内容}
}int main() {try {readFile("non_existent_file.txt"); // 尝试读取不存在的文件} catch (const FileReadException &e) {std::cout << "Caught an exception: " << e.what() << std::endl; // 捕获并处理自定义异常}return 0;
}

1.2.3 RAII(资源获取即初始化)

RAII 是一种资源管理模式,确保资源在对象的生命周期内被正确管理。当对象被销毁时,资源会自动释放。这种策略极大地减少了内存泄漏和资源未释放的问题。

资源管理原则及其重要性

RAII 的核心原则是将资源的生命周期绑定到对象的生命周期。这意味着当对象超出作用域时,它所管理的资源也会被自动释放,从而避免内存泄漏和资源泄露的问题。

示例:

cpp

#include <iostream>class Resource {
public:Resource() { std::cout << "Resource acquired" << std::endl; }~Resource() { std::cout << "Resource released" << std::endl; }
};void useResource() {Resource res; // 资源在此处被获取// 使用资源...
} // 当 res 超出作用域时,资源会自动释放int main() {useResource(); // 调用使用资源的函数return 0;
}
实现智能指针的基本原理

智能指针是 RAII 的一种实现,用于管理动态分配的内存。常见的智能指针包括 std::unique_ptrstd::shared_ptr。它们自动管理内存的分配和释放,防止内存泄漏。

示例:

cpp

#include <iostream>
#include <memory> // std::unique_ptrclass Resource {
public:Resource() { std::cout << "Resource acquired" << std::endl; }~Resource() { std::cout << "Resource released" << std::endl; }
};void manageResource() {std::unique_ptr<Resource> res(new Resource()); // RAII 管理资源// 使用资源...
} // 当 res 超出作用域时,资源会自动释放int main() {manageResource(); // 调用管理资源的函数return 0;
}
结合 RAII 管理文件句柄与内存

RAII 可以用于管理文件句柄和动态内存,确保它们在不再需要时被正确释放。可以使用 std::ifstream 来管理文件句柄,并结合智能指针来管理动态分配的内存。

示例:

cpp

#include <iostream>
#include <fstream>
#include <memory>void readFile(const std::string &filename) {std::ifstream file(filename); // RAII 管理文件句柄if (!file.is_open()) {throw std::runtime_error("Failed to open file"); // 抛出异常}std::string line;while (std::getline(file, line)) {std::cout << line << std::endl; // 读取并处理文件内容}
}int main() {try {readFile("example.txt"); // 尝试读取文件} catch (const std::exception &e) {std::cerr << "Error: " << e.what() << std::endl; // 捕获并处理异常}return 0;
}

异常处理结论

异常处理是 C++ 中重要的编程概念。通过理解异常的捕获与抛出、自定义异常类和 RAII 原则,开发者能够编写出更健壮和可维护的代码。

Lambda 表达式概述

Lambda 表达式是 C++11 引入的一项强大特性,允许程序员定义匿名函数,并在需要时即时使用。通过 Lambda 表达式,可以以更简洁的方式编写代码,提高代码的可读性和可维护性。本文将详细介绍 Lambda 表达式的基本语法、在标准库中的应用以及状态与持久性。

1.3 Lambda 表达式

1.3.1 基本语法

Lambda 表达式的基本语法如下:

cpp

[capture](parameters) -> return_type {// 函数体
}
捕获列表与参数列表
  • 捕获列表:用于指定在 Lambda 表达式中使用的外部变量。可以按值捕获或按引用捕获。
  • 参数列表:与普通函数的参数列表相同,用于定义 Lambda 表达式的输入参数。

示例:

cpp

#include <iostream>
#include <vector>
using namespace std;int main() {int x = 10;vector<int> vec = {1, 2, 3, 4, 5};// 按值捕获 xauto lambda_by_value = [x](int y) {return x + y; // 使用捕获的 x};// 按引用捕获 xauto lambda_by_reference = [&x](int y) {return x + y; // 使用捕获的 x};cout << "By Value: " << lambda_by_value(5) << endl;       // 输出: 15cout << "By Reference: " << lambda_by_reference(5) << endl; // 输出: 15return 0;
}
返回类型推断

C++11 引入了自动推断 Lambda 表达式的返回类型。如果未显式指定返回类型,编译器会根据函数体自动推断返回类型。

示例:

cpp

auto lambda = [](int a, int b) {return a + b; // 返回类型为 int
};cout << "Sum: " << lambda(3, 4) << endl; // 输出: Sum: 7

如果需要更复杂的返回类型或使用 decltype 进行推断,可以显式指定返回类型:

示例:

cpp

auto lambda_with_type = [](int a, int b) -> double {return static_cast<double>(a) / b; // 显式指定返回类型为 double
};cout << "Division: " << lambda_with_type(5, 2) << endl; // 输出: Division: 2.5

1.3.2 在标准库中的应用

Lambda 表达式在 C++ 标准库中得到了广泛应用,特别是在 STL 算法中。以下是一些常见的应用示例。

std::for_each

std::for_each 可以与 Lambda 表达式结合使用,以遍历容器中的元素并对每个元素执行操作。

示例:

cpp

#include <iostream>
#include <vector>
#include <algorithm> // std::for_each
using namespace std;int main() {vector<int> vec = {1, 2, 3, 4, 5};// 使用 Lambda 表达式打印每个元素std::for_each(vec.begin(), vec.end(), [](int value) {cout << value << " "; // 输出: 1 2 3 4 5});cout << endl;return 0;
}
std::sort

std::sort 可以使用 Lambda 表达式定义自定义排序规则。

示例:

cpp

#include <iostream>
#include <vector>
#include <algorithm> // std::sort
using namespace std;int main() {vector<int> vec = {5, 3, 1, 4, 2};// 使用 Lambda 表达式按升序排序std::sort(vec.begin(), vec.end(), [](int a, int b) {return a < b; // 升序排序});cout << "Sorted: ";for (int value : vec) {cout << value << " "; // 输出: 1 2 3 4 5}cout << endl;return 0;
}

1.3.3 状态与持久性

Lambda 表达式可以捕获外部状态,使其在调用时保持该状态。捕获的变量的生命周期必须在 Lambda 表达式的使用期间内有效。

捕获外部状态的示例

cpp

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main() {int threshold = 3; // 外部状态vector<int> vec = {1, 2, 3, 4, 5};// 使用 Lambda 表达式捕获外部状态auto count_greater = [threshold](const vector<int>& numbers) {return std::count_if(numbers.begin(), numbers.end(), [threshold](int num) {return num > threshold; // 使用捕获的 threshold});};cout << "Count greater than " << threshold << ": " << count_greater(vec) << endl; // 输出: 2return 0;
}
状态的持久性

如果 Lambda 表达式捕获了状态(例如,通过按引用捕获),需要确保该状态在 Lambda 的生命周期内有效。如果状态被销毁,Lambda 将访问无效内存。

示例:

cpp

#include <iostream>
#include <functional> // std::bind
using namespace std;int main() {int x = 10;auto lambda = [&x]() {return x * 2; // 捕获 x};cout << "Before changing x: " << lambda() << endl; // 输出: 20x = 20; // 修改 xcout << "After changing x: " << lambda() << endl; // 输出: 40return 0;
}

Lambda 表达式结论

Lambda 表达式是 C++ 中一个强大的特性,能够以简洁的方式定义匿名函数。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com