C++ 快速学习教程
文章目录
- 一、基础语法篇
- 1. 开发环境搭建
- 2. 程序结构
- 3. 基础数据类型
- 4. 流程控制
- 二、面向对象编程
- 1. 类与对象
- 2. 构造函数与析构函数
- 3. 继承与多态
- 4. 运算符重载
- 三、高级特性
- 1. 模板编程
- 2. 异常处理
- 3. 命名空间
- 4. 类型推导(auto/decltype)
- 四、标准库与 STL
- 1. 容器
- 2. 算法
- 3. 智能指针
- 4. 文件流操作
- 五、内存管理
- 1. 指针与引用
- 2. 堆与栈内存
- 3. 内存泄漏检测
- 4. 移动语义(C++11)
- 六、现代 C++ 特性
- 1. C++11/14/17/20 新特性
- 2. 多线程编程
- 七、项目实践
- 1. 代码规范(Google C++ Style)
- 2. 单元测试(Google Test)
- 3. 调试技巧(gdb/IDE 调试器)
一、基础语法篇
1. 开发环境搭建
- GCC/MinGW 编译器
- 详解:GCC 是 GNU 编译器套装,MinGW 是 Windows 平台上的 GCC 移植版。它们可以将 C++ 源代码编译成可执行文件。在 Linux 系统上通常默认安装了 GCC,而在 Windows 上可以通过安装 MinGW 来使用 GCC。
- 示例(Windows 下使用 MinGW):假设已经安装并配置好 MinGW,有一个简单的 C++ 源文件
hello.cpp
:
#include <iostream>
int main() {
std::cout << "Hello, MinGW!" << std::endl;
return 0;
}
在命令行中使用 g++ hello.cpp -o hello.exe
进行编译,然后运行 hello.exe
即可看到输出。
- IDE 配置(VS/VSCode/CLion)
- 详解
- Visual Studio(VS):是微软的集成开发环境,功能强大,有丰富的调试和代码分析工具。
- Visual Studio Code(VSCode):轻量级的跨平台代码编辑器,通过安装 C++ 扩展可以实现 C++ 开发,支持代码高亮、智能代码补全等功能。
- CLion:JetBrains 开发的跨平台 C++ IDE,对 C++ 支持非常好,有智能代码提示、强大的调试功能等。
- 示例(VSCode):安装 C++ 扩展,安装 MinGW 并配置环境变量。创建一个新的 C++ 文件,编写上述
hello.cpp
代码,然后按F5
进行调试运行。
- 详解
2. 程序结构
- main 函数
- 详解:C++ 程序的执行从
main
函数开始。main
函数的返回值类型通常是int
,返回 0 表示程序正常结束,非 0 值表示程序异常结束。 - 示例:
- 详解:C++ 程序的执行从
#include <iostream>
// main 函数是程序的入口点
int main() {
std::cout << "This is the main function." << std::endl;
return 0;
}
- 头文件与源文件
- 详解:头文件(通常以
.h
或.hpp
为扩展名)用于声明函数、类、常量等,源文件(通常以.cpp
为扩展名)用于实现头文件中声明的内容。这样做可以提高代码的模块化和可维护性。 - 示例:
example.h
- 详解:头文件(通常以
// 函数声明
void printMessage();
example.cpp
#include <iostream>
#include "example.h"
// 函数实现
void printMessage() {
std::cout << "This is a message from a function." << std::endl;
}
main.cpp
#include "example.h"
int main() {
printMessage();
return 0;
}
3. 基础数据类型
- 整型 / 浮点型 / 字符型
- 详解
- 整型:用于表示整数,如
int
、short
、long
等。 - 浮点型:用于表示小数,如
float
、double
。 - 字符型:用于表示单个字符,如
char
。
- 整型:用于表示整数,如
- 示例:
- 详解
#include <iostream>
int main() {
// 整型
int num = 10;
// 浮点型
double pi = 3.14159;
// 字符型
char ch = 'A';
std::cout << "Integer: " << num << std::endl;
std::cout << "Double: " << pi << std::endl;
std::cout << "Character: " << ch << std::endl;
return 0;
}
- 类型转换(static_cast 等)
- 详解:
static_cast
是一种编译时类型转换,用于在相关类型之间进行转换,如整数类型之间、浮点类型之间、整数和浮点类型之间等。它比 C 风格的强制类型转换更安全,因为它在编译时进行类型检查。 - 示例:
- 详解:
#include <iostream>
int main() {
double d = 3.14;
// 使用 static_cast 进行类型转换
int i = static_cast<int>(d);
std::cout << "Double value: " << d << std::endl;
std::cout << "Converted int value: " << i << std::endl;
return 0;
}
4. 流程控制
- 条件语句(if - else/switch)
- 详解
- if - else:用于根据条件执行不同的代码块。
- switch:用于根据一个表达式的值选择执行多个代码块中的一个。
- 示例:
- 详解
#include <iostream>
int main() {
int num = 5;
if (num > 0) {
std::cout << "The number is positive." << std::endl;
} else if (num < 0) {
std::cout << "The number is negative." << std::endl;
} else {
std::cout << "The number is zero." << std::endl;
}
int choice = 2;
switch (choice) {
case 1:
std::cout << "You chose option 1." << std::endl;
break;
case 2:
std::cout << "You chose option 2." << std::endl;
break;
default:
std::cout << "Invalid choice." << std::endl;
}
return 0;
}
- 循环结构(for/while/do - while)
- 详解
- for 循环:通常用于已知循环次数的情况。
- while 循环:在条件为真时重复执行代码块。
- do - while 循环:先执行一次代码块,然后在条件为真时继续执行。
- 示例:
- 详解
#include <iostream>
int main() {
// for 循环
for (int i = 0; i < 5; ++i) {
std::cout << "For loop iteration: " << i << std::endl;
}
// while 循环
int j = 0;
while (j < 3) {
std::cout << "While loop iteration: " << j << std::endl;
++j;
}
// do - while 循环
int k = 0;
do {
std::cout << "Do - while loop iteration: " << k << std::endl;
++k;
} while (k < 2);
return 0;
}
二、面向对象编程
1. 类与对象
- 成员变量 / 方法
- 详解:类是一种用户自定义的数据类型,它可以包含成员变量(数据成员)和成员方法(函数成员)。成员变量用于存储对象的状态,成员方法用于操作这些状态。
- 示例:
#include <iostream>
// 定义一个简单的类
class Rectangle {
private:
// 成员变量
int width;
int height;
public:
// 构造函数
Rectangle(int w, int h) : width(w), height(h) {}
// 成员方法
int area() {
return width * height;
}
};
int main() {
// 创建对象
Rectangle rect(5, 3);
std::cout << "Rectangle area: " << rect.area() << std::endl;
return 0;
}
- 访问控制(public/private/protected)
- 详解
- public:公有成员可以在类的外部被访问。
- private:私有成员只能在类的内部被访问,通常用于封装数据,防止外部直接修改。
- protected:受保护成员可以在类的内部和派生类中被访问。
- 示例:在上面的
Rectangle
类中,width
和height
是私有的,只能通过类的成员方法访问,保证了数据的安全性。
- 详解
2. 构造函数与析构函数
- 详解
- 构造函数:在创建对象时自动调用,用于初始化对象的成员变量。
- 析构函数:在对象销毁时自动调用,用于释放对象占用的资源。
- 示例:
#include <iostream>
class MyClass {
public:
// 构造函数
MyClass() {
std::cout << "Constructor called." << std::endl;
}
// 析构函数
~MyClass() {
std::cout << "Destructor called." << std::endl;
}
};
int main() {
{
MyClass obj;
} // 当 obj 离开作用域时,析构函数会被调用
return 0;
}
3. 继承与多态
- 虚函数与 override 关键字
- 详解
- 虚函数:在基类中用
virtual
关键字声明的函数,允许在派生类中重写。 - override 关键字:用于显式地表明派生类中的函数是重写基类的虚函数,避免因函数签名错误而导致的错误。
- 虚函数:在基类中用
- 示例:
- 详解
#include <iostream>
// 基类
class Shape {
public:
// 虚函数
virtual void draw() {
std::cout << "Drawing a shape." << std::endl;
}
};
// 派生类
class Circle : public Shape {
public:
// 重写虚函数
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};
int main() {
Shape* shape = new Circle();
shape->draw(); // 调用派生类的 draw 函数
delete shape;
return 0;
}
- 抽象类与接口
- 详解
- 抽象类:包含纯虚函数(用
= 0
声明的虚函数)的类,不能实例化,通常作为基类供其他类继承。 - 接口:在 C++ 中通常用抽象类来实现接口的概念,一个类可以继承多个抽象类来实现多个接口。
- 抽象类:包含纯虚函数(用
- 示例:
- 详解
#include <iostream>
// 抽象类
class AbstractShape {
public:
// 纯虚函数
virtual void draw() = 0;
};
// 派生类实现抽象类
class Square : public AbstractShape {
public:
void draw() override {
std::cout << "Drawing a square." << std::endl;
}
};
int main() {
AbstractShape* shape = new Square();
shape->draw();
delete shape;
return 0;
}
4. 运算符重载
- 详解:运算符重载允许自定义类的对象使用内置运算符(如
+
、-
、*
等)进行操作。通过重载运算符,可以使自定义类的对象的行为更像内置类型。 - 示例:
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
// 重载 + 运算符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
void print() {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
int main() {
Complex c1(1.0, 2.0);
Complex c2(3.0, 4.0);
Complex c3 = c1 + c2;
c3.print();
return 0;
}
三、高级特性
1. 模板编程
- 函数模板
- 详解:函数模板是一种通用的函数定义,它可以处理不同类型的数据,通过模板参数来实现类型的参数化。编译器会根据调用时的实际参数类型生成具体的函数实例。
- 示例:
#include <iostream>
// 函数模板
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int result1 = add(1, 2);
double result2 = add(1.5, 2.5);
std::cout << "Int result: " << result1 << std::endl;
std::cout << "Double result: " << result2 << std::endl;
return 0;
}
- 类模板
- 详解:类模板是一种通用的类定义,它可以根据不同的类型参数生成不同的类。类似于函数模板,编译器会根据实际使用的类型生成具体的类实例。
- 示例:
#include <iostream>
// 类模板
template <typename T>
class Pair {
private:
T first;
T second;
public:
Pair(T f, T s) : first(f), second(s) {}
T getFirst() const {
return first;
}
T getSecond() const {
return second;
}
};
int main() {
Pair<int> p(1, 2);
std::cout << "First: " << p.getFirst() << ", Second: " << p.getSecond() << std::endl;
return 0;
}
2. 异常处理
- 详解:异常处理机制允许程序在运行时处理错误和异常情况。
try
块中包含可能抛出异常的代码,catch
块用于捕获并处理异常,throw
语句用于抛出异常。 - 示例:
#include <iostream>
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero!");
}
return a / b;
}
int main() {
try {
int result = divide(10, 0);
std::cout << "Result: " << result << std::endl;
} catch (const std::runtime_error& e) {
std::cout << "Exception caught: " << e.what() << std::endl;
}
return 0;
}
3. 命名空间
- 详解:命名空间用于避免命名冲突,将不同的代码模块或库中的名称分隔开来。可以在命名空间中定义变量、函数、类等。
- 示例:
#include <iostream>
// 定义命名空间
namespace MyNamespace {
int value = 42;
void printValue() {
std::cout << "Value in MyNamespace: " << value << std::endl;
}
}
int main() {
// 使用命名空间中的成员
MyNamespace::printValue();
return 0;
}
4. 类型推导(auto/decltype)
- 详解
- auto:用于自动推导变量的类型,编译器会根据变量的初始化表达式来确定其类型。
- decltype:用于获取表达式的类型,常用于模板编程和泛型编程中。
- 示例:
#include <iostream>
int main() {
auto num = 10; // 自动推导为 int 类型
decltype(num) anotherNum = 20; // 与 num 类型相同
std::cout << "num: " << num << std::endl;
std::cout << "anotherNum: " << anotherNum << std::endl;
return 0;
}
四、标准库与 STL
1. 容器
- vector:动态数组,支持随机访问,元素在内存中连续存储。
#include <iostream>
#include <vector>
int main() {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
for (int i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
- list:双向链表,不支持随机访问,但插入和删除操作效率高。
#include <iostream>
#include <list>
int main() {
std::list<int> l;
l.push_back(1);
l.push_front(2);
for (int i : l) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
- map:关联容器,存储键值对,键是唯一的,元素按照键的顺序排序。
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> m;
m["apple"] = 10;
m["banana"] = 20;
for (const auto& pair : m) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
2. 算法
- sort:对容器中的元素进行排序。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {3, 1, 2};
std::sort(v.begin(), v.end());
for (int i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
- find:在容器中查找指定元素。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {1, 2, 3};
auto it = std::find(v.begin(), v.end(), 2);
if (it != v.end()) {
std::cout << "Found: " << *it << std::endl;
} else {
std::cout << "Not found" << std::endl;
}
return 0;
}
- transform:对容器中的元素进行变换。
#include <iostream>
#include <vector>
#include <algorithm>
int square(int x) {
return x * x;
}
int main() {
std::vector<int> v = {1, 2, 3};
std::vector<int> result;
std::transform(v.begin(), v.end(), std::back_inserter(result), square);
for (int i : result) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
3. 智能指针
- unique_ptr:独占式智能指针,同一时间只能有一个
unique_ptr
指向同一个对象。当unique_ptr
离开作用域时,它会自动释放所指向的对象。
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> p(new int(10));
std::cout << *p << std::endl;
return 0;
}
- shared_ptr:共享式智能指针,多个
shared_ptr
可以指向同一个对象,通过引用计数来管理对象的生命周期。当引用计数为 0 时,对象会被自动释放。
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2 = p1;
std::cout << *p1 << " " << *p2 << std::endl;
return 0;
}
4. 文件流操作
- ifstream:用于读取文件。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream file("test.txt");
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
std::cout << line << std::endl;
}
file.close();
}
return 0;
}
- ofstream:用于写入文件。
#include <iostream>
#include <fstream>
int main() {
std::ofstream file("test.txt");
if (file.is_open()) {
file << "Hello, World!" << std::endl;
file.close();
}
return 0;
}
五、内存管理
1. 指针与引用
- 指针:存储变量的内存地址,可以通过解引用操作符
*
访问所指向的变量。
#include <iostream>
int main() {
int x = 10;
int* p = &x;
std::cout << *p << std::endl;
return 0;
}
- 引用:是变量的别名,必须在定义时初始化,并且不能重新绑定到其他变量。
#include <iostream>
int main() {
int x = 10;
int& ref = x;
std::cout << ref << std::endl;
return 0;
}
2. 堆与栈内存
- 栈内存:由编译器自动分配和释放,用于存储局部变量。栈内存的分配和释放速度快,但空间有限。
- 堆内存:由程序员手动分配和释放(使用
new
和delete
运算符),用于存储动态分配的对象。堆内存的空间较大,但分配和释放速度相对较慢,并且容易出现内存泄漏。
3. 内存泄漏检测
- Valgrind:一个流行的内存调试工具,可以检测内存泄漏、越界访问等问题。
valgrind --leak-check=full ./your_program
4. 移动语义(C++11)
- 移动构造函数和移动赋值运算符:用于避免不必要的拷贝操作,提高性能。例如,
std::vector
在移动时会直接转移资源的所有权,而不是进行拷贝。
#include <iostream>
#include <vector>
class MyClass {
private:
std::vector<int> data;
public:
MyClass() = default;
MyClass(const MyClass& other) : data(other.data) {
std::cout << "Copy constructor" << std::endl;
}
MyClass(MyClass&& other) noexcept : data(std::move(other.data)) {
std::cout << "Move constructor" << std::endl;
}
MyClass& operator=(const MyClass& other) {
data = other.data;
std::cout << "Copy assignment" << std::endl;
return *this;
}
MyClass& operator=(MyClass&& other) noexcept {
data = std::move(other.data);
std::cout << "Move assignment" << std::endl;
return *this;
}
};
int main() {
MyClass a;
MyClass b = std::move(a);
return 0;
}
六、现代 C++ 特性
1. C++11/14/17/20 新特性
- lambda 表达式:一种匿名函数,可以方便地定义和使用函数对象。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {1, 2, 3};
std::for_each(v.begin(), v.end(), [](int x) {
std::cout << x << " ";
});
std::cout << std::endl;
return 0;
}
- 范围 for 循环:一种简化的循环语法,用于遍历容器中的元素。
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3};
for (int i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
- constexpr:用于在编译时计算表达式的值,提高性能。
#include <iostream>
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int result = square(5);
std::cout << result << std::endl;
return 0;
}
2. 多线程编程
- thread/async
- std::thread:用于创建和管理线程。
#include <iostream>
#include <thread>
void hello() {
std::cout << "Hello from thread" << std::endl;
}
int main() {
std::thread t(hello);
t.join();
return 0;
}
- std::async:用于异步执行任务,返回一个
std::future
对象,可以用于获取任务的结果。
#include <iostream>
#include <future>
int add(int a, int b) {
return a + b;
}
int main() {
std::future<int> result = std::async(add, 1, 2);
std::cout << result.get() << std::endl;
return 0;
}
- 互斥锁 / 条件变量
- std:mutex:用于保护共享资源,防止多个线程同时访问。
- std::condition_variable:用于线程间的同步,一个线程可以等待某个条件满足,另一个线程可以通知条件已满足。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
std::cout << "Worker thread is running" << std::endl;
}
int main() {
std::thread t(worker);
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
t.join();
return 0;
}
七、项目实践
1. 代码规范(Google C++ Style)
- 遵循统一的代码规范可以提高代码的可读性和可维护性。Google C++ Style 是一种广泛使用的代码规范,包括命名约定、代码格式、注释风格等方面的规定。
2. 单元测试(Google Test)
- Google Test 是一个开源的 C++ 单元测试框架,可以帮助你编写和运行单元测试。
#include <gtest/gtest.h>
int add(int a, int b) {
return a + b;
}
TEST(AddTest, PositiveNumbers) {
EXPECT_EQ(add(1, 2), 3);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
3. 调试技巧(gdb/IDE 调试器)
- gdb:一个强大的命令行调试工具,可以用于设置断点