第2章 开始学习C++

环境搭建

编译器选择

C++是一种编译型语言,需要使用编译器将源代码转换为可执行文件。以下是一些常用的C++编译器:

1. GCC (GNU Compiler Collection)

  • 适用平台:Windows、Linux、macOS
  • 特点:开源、免费、支持最新的C++标准
  • 安装方法
    • Linux:通过包管理器安装(如apt install g++
    • Windows:安装MinGW或MSYS2
    • macOS:安装Xcode命令行工具或Homebrew

2. Clang

  • 适用平台:Windows、Linux、macOS
  • 特点:开源、免费、错误信息友好、编译速度快
  • 安装方法
    • Linux:通过包管理器安装(如apt install clang
    • Windows:安装LLVM
    • macOS:默认包含在Xcode命令行工具中

3. MSVC (Microsoft Visual C++)

  • 适用平台:Windows
  • 特点:与Windows平台集成良好、支持最新的C++标准
  • 安装方法:安装Visual Studio或Visual Studio Build Tools

4. 其他编译器

  • Intel C++ Compiler:针对Intel处理器优化
  • Embarcadero C++ Builder:集成开发环境
  • Oracle Solaris Studio:针对Solaris平台

集成开发环境(IDE)

使用IDE可以提高开发效率,以下是一些常用的C++ IDE:

1. Visual Studio

  • 适用平台:Windows
  • 特点:功能强大、集成调试器、代码编辑器、项目管理
  • 版本:Community(免费)、Professional、Enterprise

2. Visual Studio Code

  • 适用平台:Windows、Linux、macOS
  • 特点:轻量级、可扩展、支持多种语言
  • 插件:C/C++、C++ Intellisense、Code Runner等

3. CLion

  • 适用平台:Windows、Linux、macOS
  • 特点:专为C++设计、智能代码补全、重构工具
  • 价格:付费,学生可免费使用

4. Eclipse CDT

  • 适用平台:Windows、Linux、macOS
  • 特点:开源、可扩展、支持多种语言

5. Code::Blocks

  • 适用平台:Windows、Linux、macOS
  • 特点:开源、轻量级、跨平台

第一个C++程序

让我们创建一个简单的C++程序,输出”Hello, C++!”。

1. 使用文本编辑器

在任意文本编辑器中创建一个名为hello.cpp的文件,输入以下代码:

1
2
3
4
5
6
#include <iostream>

int main() {
std::cout << "Hello, C++!" << std::endl;
return 0;
}

2. 编译和运行

使用GCC编译
1
2
3
g++ hello.cpp -o hello
./hello # Linux/macOS
hello.exe # Windows
使用Clang编译
1
2
3
clang++ hello.cpp -o hello
./hello # Linux/macOS
hello.exe # Windows
使用MSVC编译
1
2
cl hello.cpp
hello.exe

3. 程序解析

1
2
3
4
5
6
#include <iostream>

int main() {
std::cout << "Hello, C++!" << std::endl;
return 0;
}
  • #include <iostream>:预处理器指令,包含标准输入/输出流库
  • int main():主函数,每个C++程序都必须有一个主函数
  • std::cout:标准输出流对象,用于输出数据
  • <<:插入运算符,将右侧的数据插入到左侧的流中
  • std::endl:换行符,同时刷新输出缓冲区
  • return 0:主函数返回值,0表示成功

4. 现代C++特性示例

4.1 使用auto关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>

int main() {
// 自动类型推导
auto number = 42; // 推导为int
auto pi = 3.14159; // 推导为double
auto message = "Hello, Modern C++!"; // 推导为const char*

// 在容器遍历中的应用
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (auto num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

4.2 使用lambda表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9};

// 使用lambda表达式排序
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a < b;
});

// 使用lambda表达式遍历
for_each(numbers.begin(), numbers.end(), [](int num) {
std::cout << num << " ";
});
std::cout << std::endl;

return 0;
}

4.3 使用智能指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <memory>

class MyClass {
public:
MyClass() { std::cout << "MyClass constructed" << std::endl; }
~MyClass() { std::cout << "MyClass destructed" << std::endl; }
void doSomething() { std::cout << "Doing something" << std::endl; }
};

int main() {
// 使用unique_ptr(独占所有权)
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
ptr1->doSomething();

// 使用shared_ptr(共享所有权)
std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>();
std::shared_ptr<MyClass> ptr3 = ptr2; // 引用计数增加
ptr2->doSomething();

return 0; // 智能指针自动释放内存
}

4.4 使用标准库容器和算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

// 计算总和
int sum = std::accumulate(numbers.begin(), numbers.end(), 0);
std::cout << "Sum: " << sum << std::endl;

// 查找元素
auto it = std::find(numbers.begin(), numbers.end(), 3);
if (it != numbers.end()) {
std::cout << "Found: " << *it << std::endl;
}

// 转换元素
std::vector<int> squared(numbers.size());
std::transform(numbers.begin(), numbers.end(), squared.begin(),
[](int x) { return x * x; });

// 输出结果
std::cout << "Squared: ";
for (auto num : squared) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

4.5 使用结构化绑定(C++17+)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <tuple>
#include <string>

int main() {
// 结构化绑定用于元组
auto [name, age, score] = std::make_tuple("Alice", 25, 95.5);
std::cout << "Name: " << name << ", Age: " << age << ", Score: " << score << std::endl;

// 结构化绑定用于数组
int arr[] = {1, 2, 3};
auto [a, b, c] = arr;
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;

return 0;
}

4.6 使用if constexpr(C++17+)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <type_traits>

// 编译时条件判断
template <typename T>
void printTypeInfo(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integral type: " << value << std::endl;
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Floating point type: " << value << std::endl;
} else if constexpr (std::is_same_v<T, std::string>) {
std::cout << "String type: " << value << std::endl;
} else {
std::cout << "Other type" << std::endl;
}
}

int main() {
printTypeInfo(42);
printTypeInfo(3.14);
printTypeInfo(std::string("Hello"));
return 0;
}

4.7 使用范围库(C++20+)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>
#include <ranges>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 使用范围库过滤和转换
auto result = numbers | std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; });

// 输出结果
std::cout << "Even squares: ";
for (auto num : result) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

4.8 使用format库(C++20+)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <format>

int main() {
std::string name = "Alice";
int age = 25;
double score = 95.5;

// 类型安全的字符串格式化
std::string message = std::format("Name: {}, Age: {}, Score: {:.2f}", name, age, score);
std::cout << message << std::endl;

return 0;
}

C++程序的基本结构

1. 预处理指令

预处理指令以#开头,在编译前由预处理器处理:

1
2
3
4
5
#include <iostream>  // 包含头文件
#define PI 3.14159 // 定义宏
#ifdef DEBUG // 条件编译
// 调试代码
#endif

2. 全局声明

在函数外部声明的变量、函数、类等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

int globalVar = 100; // 全局变量

void function(); // 函数声明

class MyClass { // 类声明
// 类成员
};

int main() {
// 主函数代码
return 0;
}

void function() {
// 函数定义
}

3. 主函数

每个C++程序都必须有一个主函数,是程序的入口点:

1
2
3
4
5
6
7
8
9
10
int main() {
// 函数体
return 0;
}

// 带命令行参数的主函数
int main(int argc, char* argv[]) {
// 函数体
return 0;
}

4. 函数定义

函数是C++程序的基本组成单位:

1
2
3
4
5
6
7
8
9
返回类型 函数名(参数列表) {
// 函数体
return 返回值;
}

// 示例
int add(int a, int b) {
return a + b;
}

5. 注释

C++支持两种注释方式:

1
2
3
4
5
6
// 单行注释

/*
多行注释
可以跨越多行
*/

C++的基本语法

1. 变量和常量

变量声明和初始化

1
2
3
4
5
6
7
8
int age;  // 声明变量
age = 25; // 赋值

int score = 95; // 声明并初始化

// C++11及以后的初始化方式
int value{100}; // 列表初始化
int number = {50}; // 等号+列表初始化

常量

1
2
3
4
5
const int MAX_AGE = 120;  // const常量

constexpr int MIN_AGE = 0; // 编译时常量(C++11+)

#define PI 3.14159 // 宏定义常量

2. 数据类型

基本数据类型

类型大小(字节)范围
bool1true/false
char1-128 到 127
unsigned char10 到 255
short2-32768 到 32767
unsigned short20 到 65535
int4-2147483648 到 2147483647
unsigned int40 到 4294967295
long4 或 8取决于平台
unsigned long4 或 8取决于平台
long long8-9223372036854775808 到 9223372036854775807
unsigned long long80 到 18446744073709551615
float4约±3.4e38
double8约±1.7e308
long double8 或 16取决于平台

类型修饰符

  • signed:有符号类型(默认)
  • unsigned:无符号类型
  • short:短整型
  • long:长整型
  • long long:更长的整型(C++11+)

类型转换

1
2
3
4
5
6
7
8
// 隐式类型转换
int i = 100;
double d = i; // int转换为double

// 显式类型转换(强制类型转换)
double pi = 3.14159;
int intPi = static_cast<int>(pi); // C++风格的类型转换
int oldStylePi = (int)pi; // C风格的类型转换

3. 运算符

算术运算符

运算符描述示例
+加法a + b
-减法a - b
*乘法a * b
/除法a / b
%取模(余数)a % b
++自增a++ 或 ++a
自减a– 或 –a

关系运算符

运算符描述示例
==等于a == b
!=不等于a != b
>大于a > b
<小于a < b
>=大于等于a >= b
<=小于等于a <= b

逻辑运算符

运算符描述示例
&&逻辑与a && b
||逻辑或a || b
!逻辑非!a

赋值运算符

运算符描述示例
=简单赋值a = b
+=加后赋值a += b
-=减后赋值a -= b
*=乘后赋值a *= b
/=除后赋值a /= b
%=取模后赋值a %= b

其他运算符

运算符描述示例
sizeof计算大小sizeof(int)
&取地址&a
*解引用*p
? :条件运算符a > b ? a : b
.成员访问obj.member
->指针成员访问ptr->member

4. 控制语句

条件语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// if语句
if (condition) {
// 条件为真时执行
} else if (anotherCondition) {
// 另一个条件为真时执行
} else {
// 所有条件都为假时执行
}

// switch语句
switch (expression) {
case value1:
// 处理value1
break;
case value2:
// 处理value2
break;
default:
// 处理其他情况
break;
}

循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// for循环
for (initialization; condition; update) {
// 循环体
}

// while循环
while (condition) {
// 循环体
}

// do-while循环
do {
// 循环体
} while (condition);

// 范围for循环(C++11+)
for (auto element : collection) {
// 循环体
}

跳转语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// break语句:跳出循环或switch语句
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
// 循环体
}

// continue语句:跳过本次循环的剩余部分,进入下一次循环
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue;
}
// 处理奇数
}

// return语句:从函数返回
int add(int a, int b) {
return a + b;
}

// goto语句:无条件跳转到标签(尽量避免使用)
if (error) {
goto errorHandler;
}
// 正常代码
errorHandler:
// 错误处理代码

输入和输出

标准输入输出

C++使用iostream库进行输入输出操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

int main() {
// 输出
std::cout << "Hello, " << "world!" << std::endl;

// 输入
int age;
std::cout << "Enter your age: ";
std::cin >> age;
std::cout << "Your age is: " << age << std::endl;

// 读取字符串
std::string name;
std::cout << "Enter your name: ";
std::cin >> name; // 读取到空格为止
std::cout << "Your name is: " << name << std::endl;

// 读取整行
std::string line;
std::cout << "Enter a line: ";
std::cin.ignore(); // 忽略之前的换行符
std::getline(std::cin, line); // 读取整行
std::cout << "You entered: " << line << std::endl;

return 0;
}

格式化输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <iomanip>

int main() {
// 设置宽度
std::cout << std::setw(10) << "Hello" << std::endl;

// 设置填充字符
std::cout << std::setfill('*') << std::setw(10) << "Hello" << std::endl;

// 设置精度
double pi = 3.1415926535;
std::cout << std::setprecision(5) << pi << std::endl;

// 科学计数法
std::cout << std::scientific << pi << std::endl;

// 十六进制输出
int num = 255;
std::cout << std::hex << num << std::endl;

// 八进制输出
std::cout << std::oct << num << std::endl;

// 重置格式
std::cout << std::dec << std::fixed;

return 0;
}

常见错误和调试

常见错误

1. 语法错误

  • 缺少分号:语句结束时忘记加分号
  • 括号不匹配:大括号、小括号、中括号不匹配
  • 变量未声明:使用未声明的变量
  • 类型不匹配:将一种类型的值赋给另一种不兼容的类型
  • 函数参数不匹配:调用函数时参数数量或类型不正确

2. 运行时错误

  • 除以零:尝试除以零
  • 数组越界:访问数组范围之外的元素
  • 空指针解引用:使用空指针
  • 内存泄漏:分配内存后未释放
  • 栈溢出:递归深度过大或局部变量过大

3. 逻辑错误

  • 算法错误:算法逻辑不正确
  • 条件判断错误:条件表达式逻辑错误
  • 循环错误:循环条件或循环体逻辑错误
  • 变量初始化错误:变量未正确初始化

调试技巧

1. 使用输出语句

1
std::cout << "Variable value: " << variable << std::endl;

2. 使用调试器

  • 设置断点:在代码的特定位置设置断点,程序执行到断点时暂停
  • 单步执行:逐行执行代码,观察变量变化
  • 查看变量:在调试过程中查看变量的值
  • 查看调用栈:了解函数调用关系

3. 使用断言

1
2
3
4
5
6
#include <cassert>

int divide(int a, int b) {
assert(b != 0 && "Division by zero");
return a / b;
}

4. 代码审查

  • 仔细检查代码逻辑
  • 检查边界条件
  • 检查变量初始化
  • 检查内存管理

编程规范

1. 命名规范

  • 变量和函数:使用小写字母和下划线(snake_case)
  • 类和结构体:使用大写字母开头的驼峰命名法(CamelCase)
  • 常量:使用全大写字母和下划线(UPPER_CASE)
  • 命名空间:使用小写字母

2. 代码格式

  • 缩进:使用4个空格或1个制表符进行缩进
  • 换行:每行代码长度不宜过长(通常不超过80-100个字符)
  • 空格:在运算符两侧、逗号后添加空格
  • 空行:在函数之间、逻辑块之间添加空行

3. 注释规范

  • 函数注释:描述函数的功能、参数、返回值
  • 复杂代码注释:解释复杂的算法或逻辑
  • 关键变量注释:解释关键变量的用途
  • 修改注释:记录代码修改的原因和时间

4. 最佳实践

  • 代码重用:将重复的代码提取为函数
  • 模块化:将代码组织为逻辑模块
  • 错误处理:适当处理错误情况
  • 资源管理:使用RAII(资源获取即初始化)原则管理资源
  • 性能优化:避免不必要的计算和内存操作
  • 安全性:避免缓冲区溢出、空指针解引用等安全问题

小结

本章介绍了C++编程的基础知识,包括环境搭建、第一个C++程序、基本语法、输入输出、常见错误和调试技巧等内容。通过本章的学习,你应该能够:

  1. 搭建C++开发环境,选择适合自己的编译器和IDE
  2. 编写简单的C++程序,理解程序的基本结构
  3. 掌握C++的基本语法,包括变量、常量、数据类型、运算符、控制语句等
  4. 使用标准输入输出流进行数据的输入和输出
  5. 识别和解决常见的编译错误和运行时错误
  6. 遵循基本的编程规范,编写清晰、可维护的代码

在接下来的章节中,我们将深入学习C++的更多特性,包括数据类型、控制语句、函数、数组、指针、字符串、类和对象等内容。通过系统学习这些内容,你将能够掌握C++的核心概念和编程技巧,为进一步学习高级特性打下坚实基础。

C++是一种强大而灵活的编程语言,学习它需要耐心和实践。通过不断地编写代码、解决问题,你将逐渐掌握C++的精髓,成为一名优秀的C++程序员。