C++ 入门核心知识:从第一个程序到引用

作为编程新手,踏入 C++ 的世界,这些基础又关键的知识是绕不开的“必经之路”。从最基础的第一个程序写起,逐步深入命名空间、输入输出、函数特性等内容,帮你搭建起 C++ 入门的知识框架,让你能清晰理解和上手写代码~

一、C++ 的第一个程序

C++ 兼容 C 语言大部分语法,所以用 C 语言实现的 hello world 程序在 C++ 里也能跑,但 C++ 有自己的代码规范,通常把代码后缀改为 .cpp 。不同平台编译器也有差异:

  • Windows(VS 编译器):识别 .cpp 文件会调用 C++ 编译器编译。
  • Linux:要用 g++ 编译,而不是 gccgcc 更偏向 C 语言编译,g++ 对 C++ 特性支持更完善 )。

代码示例(test.cpp)

// 包含 C 语言标准输入输出头文件,C++ 也能兼容使用
#include <stdio.h> 
int main() {
    // 输出 hello world,和 C 语言用法一样
    printf("hello world\n"); 
    return 0;
}

不过,严格意义上 C++ 版本的 hello world 会用 C++ 专属的输入输出方式,后面会讲到~

二、命名空间(namespace)

在 C++ 编程里,随着代码量变大,很容易出现命名冲突(比如不同模块里的变量、函数重名)。命名空间就是用来解决这个问题的“大杀器”,它能把代码划分到不同的“域”里,让同名内容和平共处。

(一)命名空间的定义

namespace 关键字定义命名空间,格式如下:

namespace 命名空间名 {
    // 可以定义变量、函数、类型等
    int num; 
    void func() { /* ... */ }
}

本质:就是定义出一个独立的“域”,和全局域相互隔离。比如不同命名空间里可以定义同名变量 rand ,不会冲突。

C++ 里的“域”:C++ 中有函数局部域、全局域、命名空间域、类域。这些“域”会影响编译时语法查找变量/函数/类型的逻辑

  • 局部域和全局域:除了影响编译查找逻辑,还会影响变量的生命周期(比如局部变量进入作用域创建,离开销毁 )。
  • 命名空间域和类域:主要影响编译查找逻辑,不影响变量生命周期。

举个简单例子:

// 定义命名空间 N1
namespace N1 { 
    int x = 10;
    void show() {
        // 这里访问的是 N1 命名空间里的 x
        printf("%d\n", x); 
    }
}
namespace N2 {
    int x = 20;
    void show() {
        // 这里访问的是 N2 命名空间里的 x
        printf("%d\n", x); 
    }
}
int main() {
    // 调用 N1 的 show 函数,输出 10
    N1::show(); 
    // 调用 N2 的 show 函数,输出 20
    N2::show(); 
    return 0;
}

(二)命名空间的其他特性

  1. 定义位置namespace 只能定义在全局作用域,不过它支持嵌套定义 。比如:
namespace Outer {
    namespace Inner {
        int y = 30;
    }
}
int main() {
    // 访问嵌套命名空间里的变量,输出 30
    printf("%d\n", Outer::Inner::y); 
    return 0;
}
  1. 多文件同名合并:项目工程中,多个 .cpp 文件里定义的同名 namespace ,会被编译器认为是同一个命名空间 。比如 file1.cppfile2.cpp 都有 namespace MySpace ,它们里面的内容会合并到一起。
  2. 标准库的命名空间:C++ 标准库的内容,都放在 std(standard 的缩写)这个命名空间里。后面用 coutcin 等输入输出功能时,就和它密切相关。

(三)命名空间的使用方式

编译器默认只会在局部域或全局域查找变量/函数的声明/定义,不会主动去命名空间里找。所以想用命名空间里的内容,得用下面几种方式:

1. 指定命名空间访问(项目推荐)

格式:命名空间名::成员 ,明确告诉编译器去哪个命名空间找。

namespace MySpace {
    int data = 100;
    void print() {
        printf("%d\n", data);
    }
}
int main() {
    // 明确访问 MySpace 里的 data,输出 100
    printf("%d\n", MySpace::data); 
    // 明确访问 MySpace 里的 print 函数
    MySpace::print(); 
    return 0;
}

这种方式最清晰,能避免命名冲突,项目开发里常用。

2. using 展开命名空间某个成员(常用且安全)

如果经常访问命名空间里的某个成员,又不想每次都写 命名空间名:: ,可以用 using 展开单个成员:

namespace MySpace {
    int data = 100;
    void print() {
        printf("%d\n", data);
    }
}
// 展开 MySpace 里的 data,之后直接用 data 就代表 MySpace::data
using MySpace::data; 
int main() {
    // 直接用 data,等价于 MySpace::data,输出 100
    printf("%d\n", data); 
    // 但 print 函数还得用命名空间访问,因为没展开它
    MySpace::print(); 
    return 0;
}

适合频繁使用某个成员,且成员名不会和其他域里的名字冲突的场景。

3. using namespace 展开全部成员(日常练习可用,项目慎用)

using namespace 命名空间名; 可以一次性展开命名空间里所有成员,之后直接用成员名就行。但冲突风险很高,项目里不推荐,容易和其他域的同名内容打架。

namespace MySpace {
    int data = 100;
    void print() {
        printf("%d\n", data);
    }
}
// 展开 MySpace 所有成员
using namespace MySpace; 
int main() {
    // 直接用 data 和 print,等价于 MySpace::data、MySpace::print
    printf("%d\n", data); 
    print(); 
    return 0;
}

日常写小练习代码图方便可以用,但正式项目为了代码安全和可读性,尽量别这么干~

三、C++ 输入 & 输出

C++ 有自己专属的输入输出方式,比 C 语言的 printfscanf 更方便,不需要手动指定格式,还能更好支持自定义类型。

(一)核心头文件和对象

  • <iostream>:是 Input Output Stream 的缩写,包含了标准输入、输出流的相关定义,要使用 C++ 输入输出,必须包含这个头文件。
  • std::cin:是 istream 类的对象,用于标准输入(从键盘等设备读数据 ),面向窄字符(char 类型 )。
  • std::cout:是 ostream 类的对象,用于标准输出(往控制台打印数据 ),面向窄字符。
  • std::endl:是一个特殊的“操作符”,作用是插入换行符并刷新缓冲区,让输出立马显示在控制台。
  • <<>>
    • <<流插入运算符,配合 cout 用,把数据输出到控制台。
    • >>流提取运算符,配合 cin 用,从输入设备读数据。在 C 语言里,这两个符号是位运算的左移、右移,C++ 里属于运算符重载(后面会深入讲,新手先记住用法 )。

(二)基础用法示例

#include <iostream>
// 展开 std 命名空间(小练习常用,项目看情况)
using namespace std; 
int main() {
    int num;
    // 输出提示信息,endl 换行
    cout << "请输入一个数字:" << endl; 
    // 从键盘读入一个整数,存到 num
    cin >> num; 
    // 输出 num 的值,自动识别类型
    cout << "你输入的数字是:" << num << endl; 
    return 0;
}

运行后,程序会等待你从键盘输入数字,然后输出你输入的内容。和 printfscanf 相比,不用关心格式符(%d%f 这些 ),C++ 会自动识别变量类型,是不是方便很多~

(三)提升 IO 效率的小技巧(竞赛场景常用)

在一些大量输入输出的场景(比如算法竞赛 ),默认的 IO 速度可能不够快。可以加这几行代码提升效率:

#include <iostream>
using namespace std;
int main() {
    // 关闭 stdio 和 iostream 的同步,提升效率
    ios_base::sync_with_stdio(false); 
    // 解除 cin 和 cout 的绑定(默认绑定在一起,会有额外开销)
    cin.tie(nullptr); 
    cout.tie(nullptr); 

    // 之后正常用 cin、cout 就行,速度会更快
    int a, b;
    cin >> a >> b;
    cout << a + b << endl;
    return 0;
}

原理是减少 IO 底层的一些“同步开销”,让数据读写更高效,竞赛里处理大数据输入输出时很有用~

四、缺省参数(默认参数)

缺省参数是 C++ 函数的一个实用特性,让函数调用更灵活。简单说,就是给函数参数预先设置一个“默认值”,调用函数时,没传对应参数就用默认值,传了就用传入的值

(一)分类和规则

  1. 全缺省参数:函数所有参数都有默认值。
// 全缺省,a 默认 10,b 默认 20,c 默认 30
int add(int a = 10, int b = 20, int c = 30) { 
    return a + b + c;
}
int main() {
    // 传 0 个参数,用全部默认值:10+20+30=60
    cout << add() << endl; 
    // 传 1 个参数,a=1,b=20,c=30 → 51
    cout << add(1) << endl; 
    // 传 2 个参数,a=1,b=2,c=30 → 33
    cout << add(1, 2) << endl; 
    // 传 3 个参数,a=1,b=2,c=3 → 6
    cout << add(1, 2, 3) << endl; 
    return 0;
}
  1. 半缺省参数:只有部分参数有默认值,且必须从右往左连续缺省,不能跳着给默认值。
// 半缺省,从右往左,b 有默认值,a 没有(错误示例,不符合连续缺省规则)
// int add(int a, int b = 20, int c); 
// 正确示例:c 有默认值,b 没有的话不行;应该保证从右往左连续
int add(int a, int b, int c = 30) { 
    return a + b + c;
}
int main() {
    // 传 2 个参数,a=1,b=2,c=30 → 33
    cout << add(1, 2) << endl; 
    // 传 3 个参数,a=1,b=2,c=3 → 6
    cout << add(1, 2, 3) << endl; 
    return 0;
}

调用规则:传参时必须从左到右依次给实参,不能跳着传。比如函数 void func(int a = 1, int b = 2, int c = 3); ,不能只传 ac ,必须按顺序传。

(二)声明和定义分离时的注意点

如果函数的声明和定义分开写(比如头文件声明,cpp 文件定义 ),缺省参数只能在声明里给,定义里不能重复给 。否则编译器会报错,因为默认参数只能有一份“定义”。

示例(正确写法 ):
test.h

#ifndef TEST_H
#define TEST_H
// 声明时给缺省参数
int add(int a = 10, int b = 20, int c = 30); 
#endif

test.cpp

#include "test.h"
// 定义时不能再给缺省参数,否则冲突
int add(int a, int b, int c) { 
    return a + b + c;
}

main.cpp

#include "test.h"
#include <iostream>
using namespace std;
int main() {
    cout << add() << endl; // 用默认值,输出 60
    return 0;
}

五、函数重载

C++ 支持同一作用域下定义同名函数,但这些函数的形参必须不同(参数个数、类型、顺序不同 )。这就是函数重载,能让函数调用更灵活,体现“多态”特性(C 语言不支持同一作用域同名函数,会报错 )。

(一)示例说明

#include <iostream>
using namespace std;
// 1. 参数个数不同
int add(int a, int b) {
    return a + b;
}
int add(int a, int b, int c) {
    return a + b + c;
}
// 2. 参数类型不同
double add(double a, double b) {
    return a + b;
}
// 3. 参数顺序不同(虽然少见,但符合规则)
int add(int a, double b) {
    return a + static_cast<int>(b);
}
int add(double a, int b) {
    return static_cast<int>(a) + b;
}
int main() {
    // 调用 add(int, int) → 3
    cout << add(1, 2) << endl; 
    // 调用 add(int, int, int) → 6
    cout << add(1, 2, 3) << endl; 
    // 调用 add(double, double) → 3.3
    cout << add(1.1, 2.2) << endl; 
    // 调用 add(int, double) → 1 + 2 = 3
    cout << add(1, 2.0) << endl; 
    // 调用 add(double, int) → 1 + 2 = 3
    cout << add(1.0, 2) << endl; 
    return 0;
}

编译器会根据你传入的参数类型、个数、顺序,自动匹配对应的函数,是不是很智能~

(二)注意事项

  • 作用域限制:函数重载必须在同一作用域(比如全局作用域、同一个命名空间、同一个类 )。不同作用域的同名函数不算重载,会引发命名冲突。
  • 返回值不参与重载判断:不能仅靠返回值不同来实现重载。比如 int func();double func(); ,编译器无法区分调用哪个,会报错。

六、引用

引用是 C++ 里很重要的特性,常用来替代指针,让代码更简洁、可读性更高。

(一)引用的概念和定义

引用不是新定义变量,而是给已存在的变量取一个“别名” 。编译器不会为引用开辟新内存空间,它和原变量共用同一块内存

形象点说:就像《水浒传》里,李逵有“铁牛”“黑旋风”这些别名;林冲外号“豹子头”。变量的引用,就相当于给它起了个新外号,操作引用和操作原变量,效果一样。

语法格式类型& 引用名 = 原变量;

示例:

#include <iostream>
using namespace std;
int main() {
    int num = 10;
    // ref 是 num 的引用(别名)
    int& ref = num; 
    // 操作 ref,等价于操作 num → num 变成 20
    ref = 20; 
    // 输出 num 的值,结果是 20
    cout << num << endl; 
    return 0;
}

这里 refnum 指向同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值