1. C++程序基础
1.1 C++程序结构
1.1.1 命名空间 namespace(与C语言全局作用域的区别)
命名空间是C++引入的关键特性,用于解决C语言中全局作用域污染的问题。在C语言中,所有变量和函数默认位于全局作用域,容易导致名称冲突。C++通过命名空间将标识符组织到独立的命名空间中,避免了不同模块间名称的直接冲突。
// C语言示例(全局作用域)intcount=0;voidprocess(){count++;}// C++命名空间示例namespaceutility{intcount=0;voidprocess(){count++;}}namespacelogging{intcount=0;voidprocess(){count++;}}// 使用命名空间utility::process();// 明确调用logging::process();// 明确调用1.1.2 输入输出流 iiostream(cin/cout/cerr)
C++使用流(stream)概念进行输入输出操作,提供了更灵活和面向对象的I/O系统。主要流对象包括:
cout:标准输出流(缓冲输出)cin:标准输入流cerr:标准错误流(非缓冲输出,常用于调试)
流对象通过运算符<<和>>进行数据传输,支持链式调用。
#include<iostream>usingnamespacestd;intmain(){inta,b;cout<<"Enter two numbers: ";cin>>a>>b;cout<<"Sum: "<<a+b<<endl;// 标准输出cerr<<"Error: Division by zero!"<<endl;// 错误输出return0;}1.1.3 头文件机制(.h与无后缀头文件,#include <…> vs #include “…”)
C++的头文件机制有两种包含方式:
#include <...>:用于系统头文件(如标准库),编译器在标准搜索路径中查找。#include "...":用于用户自定义头文件,编译器首先在源文件所在目录查找。
C++11标准引入无后缀头文件(如<vector>替代旧版<vector.h>),增强了类型安全和可维护性。同时,C++支持头文件保护机制:
// #pragma once(现代推荐)#pragmaonce// #ifndef头文件保护(传统方式)#ifndefMYHEADER_H#defineMYHEADER_HclassMyClass{// 类定义};#endif// MYHEADER_H1.2 词法与语法基础
1.2.1 注释(//单行注释,/* */块注释)
C++支持两种注释方式:
- 单行注释:
//从当前位置到行尾的内容被忽略。 - 块注释:
/*和*/之间的内容被忽略,支持多行注释。
// 单行注释intx=10;// 变量声明/* 块注释 多行内容 都将被忽略 */1.2.2 标识符与关键字(C++新增关键字如class/public/new/delete等)
C++在C语言基础上新增了面向对象编程的关键字,如:
class:定义类struct:定义结构体(C++中与class区别在于默认访问权限)union:定义联合体enum:定义枚举类型public/protected/private:访问控制修饰符virtual:声明虚函数或虚继承override(C++11):显式标记覆盖基类虚函数final(C++11):禁止类被继承或虚函数被覆盖this:指向当前对象的指针friend:声明友元(允许访问私有成员)new/delete:动态内存分配与释放
C与C++标识符的关键差异:
- C++支持
::作用域解析符,用于访问类、命名空间成员及全局作用域 - C++支持匿名命名空间(
namespace {}),提供文件内唯一作用域 - C++中结构体可以包含成员函数,而C语言中的结构体仅包含数据成员
1.2.3 字面量(整数、浮点、字符、字符串、布尔、指针字面量nullptr)
C++支持多种字面量类型,其中nullptr是C++11引入的强类型空指针:
intnum=10;// 整数字面量doublepi=3.14159;// 浮点字面量charch='A';// 字符字面量string str="Hello";// 字符串字面量boolflag=true;// 布尔字面量int*ptr=nullptr;// 空指针字面量(C++11)// C语言中NULL本质是0,可能导致歧义// void f(int x); void f(int* ptr);// f(NULL); // 会调用f(int),而非预期的f(int*)[(deep_research_source_group_web_4)]1.3 变量与常量
1.3.1 变量的定义与声明(extern链接属性)
变量声明与定义的区别:
- 定义:在内存中分配空间并可能初始化。
- 声明:告知编译器变量存在,但不分配空间。
extern关键字的作用:
- 用于声明外部变量/函数,避免重复定义。
- 允许跨文件访问其他模块定义的全局变量或函数。
- 与
const结合使用时,可实现跨文件共享只读常量。
// a.cppintglobal_var=100;// 定义constintMAX_SIZE=100;// 定义// b.cppexternintglobal_var;// 声明externconstintMAX_SIZE;// 声明// 跨文件使用global_var=200;1.3.2 常量定义
1.const常量(类型安全,替代宏常量)
- 提供类型安全,编译器会检查类型匹配。
- 可以是任何类型,包括指针和数组。
- 支持初始化后不可修改。
constintMAX=100;// 整型常量constchar*message="Hello";// 字符串常量constdoublePI=3.14159;// 浮点常量2.constexpr常量表达式(C++11编译期常量)
- 要求在编译期初始化,值必须在编译时确定。
- 隐含
const,但提供更严格的编译期约束。 - 支持更复杂的初始化表达式,可用于模板参数等场景。
constexprintsum=1+2;// 简单计算constexprdoublepower=2.0*2.0;// 浮点计算constexprcharstr[]="Hello";// 字符数组3. 宏常量 #define(预处理,不推荐)
- 通过预处理器进行文本替换,无类型安全。
- 可能导致歧义(如
NULL本质是0)和副作用。
#defineMAX100// 宏定义// 可能导致的问题:// void f(int x); void f(int* ptr);// f(MAX); // 正确调用f(int)// f(NULL); // 错误调用f(int)而非f(int*)[(deep_research_source_group_web_7)]4. 枚举常量 enum
- 定义命名整数常量集合。
- C++11引入
enum class(强类型枚举),避免与const常量冲突及隐式转换。
// 传统枚举(可隐式转换为int)enumColor{Red,Green,Blue};// C++11强类型枚举(需显式转换)enumclassDay{Monday,Tuesday,Wednesday};// 使用方式Color myColor=Red;Day today=Day::Monday;// 强类型枚举需指定作用域1.4 编译与构建
1.4.1 编译流程(预处理→编译→汇编→链接)
C++编译流程的四个阶段:
预处理阶段:
- 处理以
#开头的指令(如#include、#define)。 - 展开宏,替换预处理指令。
- 生成翻译单元(预处理后的源代码)。
- 处理以
编译阶段:
- 词法分析:将源代码字符流转换为标记序列。
- 语法分析:构建抽象语法树(AST)。
- 语义分析:类型检查、名称解析、访问控制验证等。
- 中间代码生成:转换为平台无关的中间表示(如LLVM IR)。
- 代码优化:常量折叠、死代码消除、循环优化等。
- 生成汇编代码文件(.s或.spp)。
汇编阶段:
- 将汇编代码转换为可执行的机器码目标文件(.o或.obj)。
- 生成重定位信息和符号表。
链接阶段:
- 解析符号引用(如函数和变量地址)。
- 合并多个目标文件和库文件。
- 生成最终的可执行文件或动态库。
链接器的关键操作:
- 符号解析:将函数/变量引用与定义绑定。
- 地址重定位:调整代码中的地址偏移。
- 合并节:将代码节、数据节等合并到最终文件。
- 处理静态库(.a或.lib)和动态库(.so或.dll)。
1.4.2 构建工具基础(Makefile, CMake简介)
Makefile:
- 基于依赖规则的构建工具。
- 使用
target: prerequisites语法定义构建规则。 - 通过时间戳判断是否需要重新编译。
- 维护复杂度高,跨平台支持差。
# Makefile示例 all: program program: main.o utility.o g++ main.o utility.o -o program main.o: main.cpp utility.h g++ -c main.cpp utility.o: utility.cpp utility.h g++ -c utility.cpp clean: rm -f *.o programCMake:
- 跨平台构建系统生成器。
- 使用
CMakeLists.txt定义构建配置。 - 支持多种生成器(如Makefile、Visual Studio项目文件)。
- 提供更好的可维护性和跨平台支持。
# CMakeLists.txt示例 cmake_minimum_required(VERSION 3.10) project(MyProject) add_executable(myapp main.cpp utility.cpp) target_link_libraries(myapp private mylib)1.5 预处理指令
1.5.1 条件编译(#if/#ifdef)
条件编译指令:
#if:根据常量表达式条件编译。#ifdef:如果宏已定义,则编译相关代码。#ifndef:如果宏未定义,则编译相关代码。#elif:多条件分支。#else:默认分支。#endif:结束条件编译。
平台特定代码示例:
#include<iostream>usingnamespacestd;#ifdef_WIN32// Windows平台特定代码#include<windows.h>voidplatformSpecificFunction(){cout<<"Windows platform"<<endl;}#elifdefined(__linux__)// Linux平台特定代码#include<unistd.h>voidplatformSpecificFunction(){cout<<"Linux platform"<<endl;}#else// 其他平台默认代码voidplatformSpecificFunction(){cout<<"Unknown platform"<<endl;}#endif1.5.2 其他指令(#pragma/#undef)
1. #pragma指令:
- 非标准但广泛支持的编译器指令。
- 常用于控制编译器行为。
// #pragma once(替代#ifndef头文件保护)#pragmaonce// #pragma pack调整结构体对齐(非标准但通用)#pragmapack(push,1)// 设置1字节对齐structPackedData{chara;// 1字节intb;// 4字节(无填充)doublec;// 8字节};#pragmapack(pop)// 恢复默认对齐2. #undef宏定义使用场景:
- 用于取消已定义的宏,解决宏冲突或临时禁用功能。
#defineDEBUG1// 调试代码#ifdefDEBUGcout<<"Debug mode enabled"<<endl;#endif// 取消DEBUG宏定义#undefDEBUG// 此时DEBUG不再被识别为已定义#ifdefDEBUGcout<<"This code will not be compiled"<<endl;#endif1.5.3 头文件保护(#pragma once/ifndef)
头文件保护的两种方式:
- #pragma once:
- 现代推荐方式,简洁高效。
- 非标准但被所有主流编译器支持。
#pragmaonce// 头文件内容classMyClass{// 类定义};- #ifndef头文件保护:
- 传统方式,提供更细粒度的控制。
- 标准且可移植。
#ifndefMYCLASS_H#defineMYCLASS_H// 头文件内容classMyClass{// 类定义};#endif// MYCLASS_H1.6 C++与C的关键差异
1. 面向对象编程支持:
- C++支持类、继承、多态,而C语言不支持。
2. 内存管理:
- C语言使用
malloc和free。 - C++使用
new和delete,并支持智能指针(如std::shared_ptr)。
3. 异常处理:
- C++支持
try、catch和throw进行异常处理。 - C语言通过返回值和错误码处理错误。
4. 函数重载:
- C++支持函数重载(同名不同参数)。
- C语言不支持函数重载。
5. 标准库:
- C++标准库(STL)包含丰富的容器和算法。
- C标准库功能相对有限。
6. 编程范式:
- C++支持多范式编程(过程式、面向对象、泛型等)。
- C语言主要支持过程式编程。
1.7 变量与常量的类型安全
1.const与constexpr的区别:
const:保证变量不可修改,但允许运行时初始化。constexpr:要求编译期初始化,提供更严格的类型安全。
intx=42;constinta=x;// 合法:a是const,但值在运行时确定constexprintb=a;// 错误:a不是编译期常量constexprintc=3+4;// 合法:纯字面量运算2. 指针与const的类型安全:
constintval=10;// 指向常量的指针(不可修改指向的值)constint*ptr1=&val;// 或 int const* ptr1// *ptr1 = 20; // 错误:不能修改指向的值// 常量指针(指针不可变,但指向的值可变)int*constptr2=&val;// ptr2 = &another_val; // 错误:指针不可修改// *ptr2 = 20; // 合法:修改指向的值// 指向常量的常量指针(指针和指向的值均不可变)constint*constptr3=&val;// ptr3 = &another_val; // 错误:指针不可修改// *ptr3 = 20; // 错误:不能修改指向的值3. 强类型枚举(enum class):
- C++11引入,避免传统枚举的隐式转换问题。
- 需显式指定作用域和类型转换。
// 传统枚举(可隐式转换为int)enumColor{Red,Green,Blue};Color c=Red;inti=c;// 合法:隐式转换为int// C++11强类型枚举(需显式转换)enumclassDay{Monday,Tuesday,Wednesday};Day d=Day::Monday;intj=d;// 错误:需显式转换intk=static_cast<int>(d);// 合法:显式转换1.8 总结
C++作为一种兼具高性能与复杂性的编程语言,其核心知识点涵盖了程序结构、词法语法、变量常量、编译构建和预处理指令等多个方面。理解这些基础概念对于掌握C++编程至关重要,特别是在类型安全和内存管理方面,C++提供了比C语言更强大的机制,如命名空间、const、constexpr、智能指针等。
在实际开发中,构建工具如CMake和Makefile的选择会影响项目的可维护性和跨平台能力;预处理指令如条件编译和头文件保护则有助于管理代码的可移植性和一致性。通过系统性地掌握这些知识点,开发者可以编写出更加安全、高效和可维护的C++代码。