C++ 入门

1. C++ 的第一个程序

学习任何编程语言,第一个程序通常都是输出"Hello World"。下面是一个最基础的 C++ 程序,它展示了 C++ 程序的基本框架:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

这个程序虽然简单,但包含了 C++ 程序的核心结构:

  • 头文件引入 :#include <iostream> 引入输入输出流库,是使用 cin、cout 的前提。
  • 命名空间 :using namespace std; 引入标准命名空间,之后才能直接使用 cout、endl 等。
  • 主函数 :int main() 是程序的入口,程序从这里开始执行。
  • 返回值 :return 0; 表示程序正常结束。

理解这个基础框架后,下面我们逐步深入 C++ 的核心特性。

2. 命名空间 namespace

命名空间是 C++ 中用于组织代码、避免命名冲突的重要机制。当多个库或模块中存在同名函数、变量或类时,命名空间可以将它们隔离在不同的作用域中。

2.1 namespace 的价值:解决命名冲突问题

在大型项目中,不同开发者或不同库之间很容易出现同名函数、同名变量。例如,两个库都定义了 print() 函数,如果没有命名空间,编译器就无法区分该调用哪一个,从而产生命名冲突。

cpp 复制代码
// 两个库都定义了同名函数,产生冲突
// 库A
void print() {
    cout << "Library A" << endl;
}

// 库B
void print() {
    cout << "Library B" << endl;
}

// 直接调用 print() 会报错:无法确定调用哪个版本

命名空间通过将代码划分到不同的作用域中,完美解决了这个问题。不同命名空间中的同名成员互不干扰,通过作用域限定符即可区分。

2.2 namespace 的定义:

自定义命名空间的语法非常简单,使用 namespace 关键字后跟命名空间名称,再用花括号包裹成员即可(注意花括号后面无分号):

cpp 复制代码
#include <iostream>

namespace LRT {
    int value = 100;
    }
}

int main() {
    // 使用命名空间中的成员
    std::cout << LRT::value << std::endl;
    return 0;
}

上述代码中,LRT 是一个自定义命名空间,通过 :: 作用域解析运算符来访问其中的成员。命名空间可以嵌套定义,也可以在不同文件中多次定义同一个命名空间,编译器会自动合并。

2.3 命名空间使用:三种使用方式

C++ 提供了三种使用命名空间成员的方式,各有适用场景:

方式一:域限定符(::)

通过 命名空间名::成员名 的方式访问,最安全、最明确,不会产生歧义:

cpp 复制代码
#include <iostream>

namespace LRT {
    int value = 100;
}

int main() {
    std::cout << LRT::value << std::endl;  // 通过 :: 访问
    return 0;
}

方式二:using 声明

使用 using 命名空间名::成员名; 只引入指定的单个成员,按需引入,既简化书写又避免引入过多名称:

cpp 复制代码
#include <iostream>
using std::cout;
using std::endl;

int main() {
    cout << "Hello, World!" << endl;  // 直接使用 cout 和 endl
    return 0;
}

方式三:using 编译指令

使用 using namespace 命名空间名; 一次性引入命名空间中的所有成员,书写最方便,但容易引入命名冲突:

cpp 复制代码
#include <iostream>
using namespace std;  // 引入 std 中所有成员

int main() {
    cout << "Hello, World!" << endl;  // 直接使用,无需 std::
    return 0;
}

三种方式对比总结:

使用方式 语法 优点 缺点
域限定符 std::cout 最安全,无歧义 书写繁琐
using 声明 using std::cout; 按需引入,较安全 每个成员都要单独声明
using 编译指令 using namespace std; 书写最方便 易引入命名冲突

在实际开发中,建议优先使用域限定符或 using 声明,在大型项目中避免使用 using 编译指令,防止命名冲突。

3. C++ 输入 & 输出

C++ 使用 cin 和 cout 进行标准输入输出,它们定义在 <iostream> 头文件中,属于 std 命名空间。

cout 输出

cout 配合插入运算符 << 向屏幕输出内容,endl 表示换行并刷新缓冲区:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int age = 20;
    cout << "我的年龄是:" << age << endl;
    cout << "两行输出" << endl;
    cout << "第二行" << endl;
    return 0;
}

cin 输入

cin 配合提取运算符 >> 从键盘读取数据,可以连续读取多个变量:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a, b;
    cout << "请输入两个整数:";
    cin >> a >> b;  // 连续输入,用空格或回车分隔
    cout << "a + b = " << a + b << endl;
    return 0;
}

<<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)

字符串输入

读取字符串时,cin 遇到空格会停止读取,如果需要读取包含空格的整行,使用 getline 函数:

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

int main() {
    string name;
    cout << "请输入姓名:";
    getline(cin, name);  // 读取整行,包含空格
    cout << "你好," << name << endl;
    return 0;
}

IO 流是 C++ 程序与外界交互的基础,几乎所有程序都需要用到输入输出功能。

4. 缺省参数

缺省参数(默认参数)允许在函数声明时为形参指定默认值,调用函数时可以省略该参数,编译器会自动使用默认值。

cpp 复制代码
#include <iostream>
using namespace std;

// 带缺省参数的函数
void greet(string name = "游客") {
    cout << "你好," << name << endl;
}

int main() {
    greet();           // 省略参数,使用默认值"游客"
    greet("小明");      // 传入参数,使用"小明"
    return 0;
}

缺省参数的使用规则:

  • 从右向左连续设置 :缺省参数必须从右往左连续设置,不能跳跃。例如 void f(int a, int b = 10, int c = 20); 合法,而 void f(int a = 1, int b, int c = 3); 不合法。
  • 声明和定义不能同时设置:缺省参数通常在函数声明中给出,定义中不要再重复设置,否则会报错。
  • 调用时从左向右传参:调用时省略参数只能从右往左省略,不能跳过中间的参数。
cpp 复制代码
#include <iostream>
using namespace std;

// 缺省参数从右向左连续设置
void printInfo(string name, int age = 18, string city = "北京") {
    cout << name << "," << age << "岁,来自" << city << endl;
}

int main() {
    printInfo("张三");                    // 全部使用默认值
    printInfo("李四", 25);                // city 使用默认值
    printInfo("王五", 30, "上海");         // 全部传入
    return 0;
}

缺省参数让函数调用更加灵活,减少了重复代码,是 C++ 中非常实用的特性。

5. 函数重载

函数重载允许在同一作用域 中定义多个函数名相同 但参数列表不同 的函数。编译器根据调用时传入的参数类型和个数,自动选择匹配的函数版本。

注意:返回值不同的函数不算是函数重载。

cpp 复制代码
#include <iostream>
using namespace std;

// 三个同名函数,参数列表不同
int add(int a, int b) {
    return a + b;
}

double add(double a, double b) {
    return a + b;
}

int add(int a, int b, int c) {
    return a + b + c;
}

int main() {
    cout << add(1, 2) << endl;          // 调用 int add(int, int),输出3
    cout << add(1.5, 2.5) << endl;     // 调用 double add(double, double),输出4
    cout << add(1, 2, 3) << endl;      // 调用 int add(int, int, int),输出6
    return 0;
}

函数重载的规则:

  • 参数列表必须不同:参数的类型、个数或顺序至少有一项不同。
  • 返回值类型不能作为区分依据:仅返回值不同不能构成重载。
  • 同一作用域:重载函数必须在同一作用域中定义。

函数重载让代码更加直观,同一个函数名可以处理不同类型的数据,提高了代码的可读性和复用性。

6. 引用(C++ 核心重点)

引用是 C++ 中非常重要的特性,它相当于给变量起了一个别名,操作引用就是操作原变量本身。

6.1 引用的概念和定义

引用使用 & 符号定义,语法为 类型& 引用名 = 原变量;:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int& b = a;  // b 是 a 的引用(别名)

    cout << "a = " << a << endl;  // 10
    cout << "b = " << b << endl;  // 10

    b = 20;  // 修改 b 就是修改 a
    cout << "a = " << a << endl;  // 20
    return 0;
}

引用和被引用变量指向同一块内存空间,修改任何一个,另一个也会同步变化。

6.2 引用的特性

引用具有以下几个重要特性:

  • 必须初始化:引用在定义时必须绑定一个变量,不能先声明后绑定。
  • 不能更改引用目标:引用一旦绑定某个变量,就不能再改为引用其他变量。
  • 引用不是独立变量:引用不占用独立的内存空间,它只是原变量的别名。
cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int b = 20;

    int& ref = a;  // 必须初始化
    // int& ref2;   // 错误:引用必须初始化

    ref = b;  // 注意:这不是让 ref 引用 b,而是把 b 的值赋给 a
    cout << "a = " << a << endl;  // 20
    cout << "ref = " << ref << endl;  // 20
    cout << "b = " << b << endl;  // 20
    return 0;
}

上述代码中,ref = b 只是把 b 的值赋给了 a,ref 仍然引用 a,并没有改变引用目标。

6.3 引用的使用:做函数参数、做函数返回值

引用做函数参数

引用传参可以让函数直接操作实参本身,避免拷贝开销,同时可以在函数内部修改实参的值:

cpp 复制代码
#include <iostream>
using namespace std;

void swapByValue(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 引用传递:直接修改实参
void swapByRef(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10, y = 20;

    swapByValue(&x, &y);
    cout << "取地址传递后:x = " << x << ", y = " << y << endl;  

    swapByRef(x, y);
    cout << "引用传递后:x = " << x << ", y = " << y << endl;  
    return 0;
}

引用做函数返回值

引用作为返回值时,返回的是变量的别名,可以避免拷贝,并且可以对返回值进行赋值操作:

cpp 复制代码
#include <iostream>
using namespace std;

int globalValue = 100;

// 返回引用
int& getGlobal() {
    return globalValue;
}

int main() {
    cout << "初始值:" << globalValue << endl;  // 100

    getGlobal() = 200;  // 通过引用返回值直接修改全局变量
    cout << "修改后:" << globalValue << endl;  // 200
    return 0;
}

需要注意的是,不能返回局部变量的引用,因为局部变量在函数结束后就被销毁了,返回的引用会变成悬空引用。

6.4 const 引用:常引用

const 引用(常引用)使用 const 类型& 定义,它不能通过引用修改原变量的值,主要用于保护数据不被修改,同时可以接收常量:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    const int& ref = a;  // 常引用

    // ref = 20;  // 错误:不能通过常引用修改值
    a = 20;  // 可以直接修改原变量
    cout << "ref = " << ref << endl;  // 20

    // 常引用可以接收常量
    const int& c = 100;
    cout << "c = " << c << endl;  // 100
    return 0;
}

常引用在函数参数中非常常用,既能避免拷贝开销,又能保证数据不被意外修改:

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 常引用做参数,保护数据不被修改
void printInfo(const string& name) {
    // name = "修改";  // 错误:不能修改
    cout << "姓名:" << name << endl;
}

int main() {
    string name = "张三";
    printInfo(name);
    printInfo("李四");  // 常引用可以接收常量
    return 0;
}

注意:在有临时变量拷贝的情况下,C++规定临时对象具有常性,引用都需要有 const 来修饰

这里有涉及权限的放大缩小问题,详见主页讲解。

6.5 指针和引用的关系

指针和引用都可以间接访问变量,但它们在语法和使用上有明显区别:

对比项 引用 指针
定义 变量的别名 存储变量地址的变量
初始化 必须初始化 可以不初始化
是否可改变指向 不能改变引用目标 可以改变指向
占用空间 不单独占用空间 占用独立空间
访问方式 直接使用,无需解引用 需要 * 解引用
空值 不能为空 可以为 NULL
cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;

    // 引用
    int& ref = a;
    ref = 20;  // 直接使用

    // 指针
    int* ptr = &a;
    *ptr = 30;  // 需要解引用

    cout << "a = " << a << endl;  // 30
    cout << "ref = " << ref << endl;  // 30
    cout << "*ptr = " << *ptr << endl;  // 30
    return 0;
}

从底层来看,引用在编译后本质上也是通过指针实现的,但引用在语法层面更安全、更简洁。在 C++ 中,优先使用引用,只有在需要改变指向或可能为空时才使用指针。

7. inline 内联函数

内联函数用于解决小函数频繁调用带来的性能开销。普通函数调用需要压栈、跳转、返回等操作,而内联函数在编译时直接将函数体代码展开到调用处,避免了函数调用的开销。

cpp 复制代码
#include <iostream>
using namespace std;

// 内联函数:编译时直接展开
inline int square(int x) {
    return x * x;
}

int main() {
    // 调用 square(5) 时,编译器直接展开为 5 * 5
    cout << "5的平方 = " << square(5) << endl;
    cout << "8的平方 = " << square(8) << endl;
    return 0;
}

使用内联函数的注意事项:

  • 适用于小函数:函数体很小(通常几行)时使用内联才有意义。
  • inline 只是建议 :inline 关键字只是向编译器提出建议,编译器会根据函数复杂度决定是否真正内联。
  • 避免复杂函数:包含循环、递归或大量代码的函数不适合内联,编译器通常会忽略 inline 建议。
  • 定义放在头文件:内联函数通常在头文件中定义,以便每个编译单元都能看到完整定义。

内联函数以空间换时间,适合频繁调用的小函数,如简单的 getter、setter 或数学计算函数。

8. nullptr 空指针

nullptr 是 C++11 引入的空指针常量,用于替代传统的 NULL。它类型安全,可以避免 NULL 在重载场景下产生的歧义问题。

cpp 复制代码
#include <iostream>
using namespace std;

void func(int x) {
    cout << "调用 int 版本:" << x << endl;
}

void func(char* p) {
    cout << "调用 char* 版本" << endl;
}

int main() {
    // NULL 在 C++ 中通常被定义为 0,会产生歧义
    // func(NULL);  // 错误:NULL 是整数 0,无法确定调用哪个版本

    func(nullptr);  // 明确调用 char* 版本,类型安全
    return 0;
}

NULL 与 nullptr 的区别:

  • NULL :在 C++ 中通常被定义为整数 0,本质是整型常量,在函数重载场景下会产生歧义。
  • nullptr :是真正的空指针类型 std::nullptr_t,可以隐式转换为任何指针类型,但不能转换为整数类型,类型安全。
cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int* p1 = nullptr;  // 正确:空指针
    int* p2 = NULL;     // 正确:NULL 等价于 0

    if (p1 == nullptr) {
        cout << "p1 是空指针" << endl;
    }

    // nullptr 不能赋值给整数类型
    // int x = nullptr;  // 错误:无法转换

    // NULL 可以赋值给整数类型(因为 NULL 就是 0)
    int y = NULL;  // 合法但不推荐
    cout << "y = " << y << endl;  // 0
    return 0;
}

在现代 C++ 编程中,建议统一使用 nullptr 表示空指针,避免 NULL 带来的类型歧义问题。

9. 综合示例:综合运用本节知识点

下面通过一个综合示例,将本节学习的命名空间、输入输出、缺省参数、函数重载、引用、内联函数和 nullptr 结合起来使用:

cpp 复制代码
#include <iostream>
using namespace std;

// 内联函数:计算平方
inline int square(int x) {
    return x * x;
}

// 函数重载:计算面积
int area(int w, int h) {
    return w * h;
}

double area(double r) {
    return 3.14159 * r * r;
}

// 引用做参数:交换两个数
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

// 缺省参数
void showInfo(string name, int age = 18) {
    cout << name << "," << age << "岁" << endl;
}

int main() {
    // 输入输出
    int x, y;
    cout << "请输入两个整数:";
    cin >> x >> y;

    // 引用交换
    swap(x, y);
    cout << "交换后:x = " << x << ", y = " << y << endl;

    // 内联函数
    cout << "x的平方 = " << square(x) << endl;

    // 函数重载
    cout << "矩形面积 = " << area(3, 4) << endl;
    cout << "圆面积 = " << area(2.0) << endl;

    // 缺省参数
    showInfo("张三");
    showInfo("李四", 25);

    // nullptr
    int* ptr = nullptr;
    if (ptr == nullptr) {
        cout << "ptr 是空指针" << endl;
    }
    return 0;
}

C++ 是一门功能强大、应用广泛的编程语言,虽然学习曲线相对陡峭,但掌握这些核心基础后,就能逐步应对更复杂的开发需求。希望本文能帮助你扎实掌握 C++ 的这些关键知识点,后续可以结合官方文档和实际项目不断深入。

相关推荐
ZhengEnCi1 小时前
2026年10月30日AI早报:Gemini 4 Carbon登顶、DeepSeek融资1055亿、智元机器人上岗实测
人工智能·算法
强哥聊算力1 小时前
《算力江湖·Intel帝国往事》第三集:安迪·格鲁夫的铁血时代:Intel为什么能把对手逼到墙角?
运维·人工智能·科技·算法·ai·gpu算力
步行cgn2 小时前
JSON.stringify 详解:把 JavaScript 对象变成 JSON 字符串
开发语言·javascript·json
Shaoshing2 小时前
LeetCode100 11. 盛最多水的容器
算法·leetcode
raindayinrain2 小时前
c++新特性--实用新特性
c++
Soneswy2 小时前
C++基础篇——类与对象(中)
开发语言·c++
CoderYanger2 小时前
Java EE 进阶:5. Spring IoC&DI(第二弹):Spring IoC
java·开发语言·spring boot·spring·面试·职场和发展·java-ee
骑着蜗牛撵大象3272 小时前
558 万美元训出 671B 模型:DeepSeek-V3 算法与系统的协同拆解
分布式·算法·moe·deepseek-v3·mla·fp8训练
Eyfcom2 小时前
〖阶段性回顾〗物流系统架构与算法的 Day 1-5 知识图谱与实战答疑
算法·系统架构·知识图谱·物流管理系统·快递管理系统