从 C 到 C++:命名空间、引用、重载与基础输入输出
如果你已经能写基本 C 程序,进入 C++ 时不要只盯着 class。
C++ 在很早的位置就改变了名字管理、函数调用、对象传递和标准库使用方式。
命名空间解决名字冲突。
引用让函数参数表达“别名”。
函数重载让同名函数按参数类型区分。
标准输入输出流让 IO 变成对象协作。
C++ 源文件
C++ 源文件通常用 .cpp、.cc 或 .cxx。
#include <iostream>
int main() {
std::cout << "Hello, C++\n";
return 0;
}
编译:
clang++ -std=c++23 -Wall -Wextra -Wpedantic main.cpp -o app
使用 clang++ 或 g++,不要用 C 编译驱动硬编 C++。
std 命名空间
C++ 标准库名字大多放在 std 命名空间里。
std::cout
std::string
std::vector
命名空间像一个名字容器。
它防止不同库都定义 vector、string、sort 时互相冲突。
namespace app {
int version() {
return 1;
}
}
调用:
int v = app::version();
不要在头文件里 using namespace std
using namespace std;
这会把 std 里的大量名字引入当前作用域。
在小程序里看似方便,在头文件里会污染所有包含它的源文件。
工程代码更推荐显式写 std::。
std::string name;
std::vector<int> values;
显式名字更长,但边界清晰。
iostream 基础
输出:
std::cout << "count=" << count << '\n';
输入:
int value = 0;
std::cin >> value;
<< 和 >> 在这里是重载运算符。
它们让流对象可以按链式方式处理不同类型。
std::string 不是 C 字符数组
#include <string>
std::string name = "ZeroBug";
name += " Club";
std::string 自己管理内存。
它知道长度。
它可以包含 '\0'。
它离开作用域时自动释放内部资源。
这比裸 char* 更适合普通业务文本。
但跨 C ABI 或系统调用时,仍可能需要 name.c_str()。
引用是对象别名
引用必须初始化,并且之后不能重新绑定。
int x = 1;
int& ref = x;
ref = 2;
修改 ref 就是修改 x。
x 和 ref 是同一个对象的两个名字
引用常用于函数参数。
void increment(int& value) {
++value;
}
调用:
int n = 1;
increment(n);
不需要传 &n。
函数签名已经说明会修改调用方对象。
const 引用避免拷贝
大对象按值传递会复制。
const 引用可以只读借用。
void print_name(const std::string& name) {
std::cout << name << '\n';
}
这表达:
- 函数不拥有
name。 - 函数不修改
name。 - 调用时避免复制字符串。
const T& 是 C++ 接口设计的基础工具。
指针和引用的区别
| 维度 | 指针 | 引用 |
|---|---|---|
| 是否可为空 | 可以 | 通常不为空 |
| 是否可重新指向 | 可以 | 初始化后不可重绑定 |
| 访问语法 | *p、p->x |
直接使用 |
| 适合表达 | 可选对象、数组、所有权句柄 | 必须存在的借用 |
如果参数必须存在,用引用。 如果参数可以为空,用指针或更明确的可选类型。
函数重载
C++ 允许同名函数根据参数不同区分。
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
调用时编译器按参数类型选择。
add(1, 2);
add(1.0, 2.0);
重载不是随便复用名字。 同名函数应该表达同一概念的不同类型版本。
默认参数
void log(const std::string& message, bool verbose = false);
调用:
log("start");
log("start", true);
默认参数应放在声明处。 不要在多个声明里重复不同默认值。 这会制造接口混乱。
auto 自动推导
auto count = 10;
auto name = std::string{"ZeroBug"};
auto 让编译器根据初始化表达式推导类型。
它适合类型很长或明显的场景。
不要用它隐藏重要接口类型。
auto it = values.begin();
迭代器类型很长,使用 auto 合理。
范围 for
for (const auto& value : values) {
std::cout << value << '\n';
}
const auto& 表示只读借用每个元素,避免复制。
如果需要修改:
for (auto& value : values) {
value += 1;
}
值、引用、const 引用的选择会影响性能和语义。
nullptr
C++ 使用 nullptr 表示空指针。
int* p = nullptr;
它比 NULL 更类型安全。
新 C++ 代码应使用 nullptr。
工程风险
C++ 入门语法常见风险:
- 在头文件中写
using namespace std污染调用方。 - 引用绑定到生命周期不足的对象。
- 函数重载造成调用歧义。
- 默认参数在多个声明中不一致。
auto隐藏重要拷贝。- 范围 for 不小心按值复制大对象。
std::string::c_str()指针超过字符串生命周期。- C 和 C++ 编译器驱动混用。
这些风险都和边界表达有关。
小结
C++ 从一开始就引入了更强的名字、类型和对象表达。 命名空间控制名字边界。 引用表达必须存在的借用。 重载让同一操作适配不同类型。 标准库对象让资源释放更自动。 这些基础语法是进入类、RAII 和标准库之前必须掌握的桥梁。