C++入门(一):第一个 C++ 程序、命名空间、输入输出和缺省参数

C++入门(一):第一个 C++ 程序、命名空间、输入输出和缺省参数


🔥 星恒随风: 个人主页 ❄️ 个人专栏: 《指针合集》 《C语言基础》 《数据结构》 《机器学习导论》 《前端基础》 《python基础》 ✨ 数据即知识,压缩即智能


目录


前言

如果你已经学过 C 语言,再来看 C++,第一感觉可能是:

这不就是 C 语言加了一些新语法吗?

C++ 确实兼容 C 语言的大多数语法,所以很多 C 语言程序放到 C++ 文件中依然可以运行。但是 C++ 不只是"更大的 C",毕竟它的名字叫 "c plus plus" ,它在 C 的基础上引入了命名空间、函数重载、引用、类和对象、模板、STL 等一系列机制。

这一篇先从 C++ 入门最基础的几个点开始讲起


一、第一个 C++ 程序

这就是我们c++版本的hellow world。

这里出现了几个新东西:

  • #include <iostream>
  • std::cout
  • <<
  • endl
  • using namespace std

先别着急,接下来我们会详细展开。


二、.c 文件和 .cpp 文件有什么区别?

在 C 语言中,源文件通常以 .c 结尾。

在 C++ 中,源文件通常以 .cpp 结尾。

比如:

txt 复制代码
test.c      // 通常按 C 语言规则编译
test.cpp    // 通常按 C++ 语言规则编译

在 Linux 下,如果编译 C 程序,常用:

bash 复制代码
gcc test.c -o test

如果编译 C++ 程序,常用:

bash 复制代码
g++ test.cpp -o test

三、C++ 的输入输出库:iostream

C 语言中常用:

c 复制代码
printf()
scanf()

C++ 中更常用:

cpp 复制代码
cout
cin

要使用它们,需要包含头文件:

cpp 复制代码
#include <iostream>

iostream 可以理解为:

input output stream,也就是输入输出流。

其中:

对象 作用
std::cout 标准输出对象,用来向控制台输出
std::cin 标准输入对象,用来从控制台读取输入
std::endl 输出换行,并刷新缓冲区

示例:

cpp 复制代码
#include <iostream>

int main()
{
    int a = 10;
    double b = 3.14;
    char c = 'x';

    std::cout << a << " " << b << " " << c << std::endl;

    return 0;
}

输出:

txt 复制代码
10 3.14 x

四、<<>> 在 C++ 输入输出中的作用

在 C 语言中,<<>> 是位运算符:

c 复制代码
a << 1
a >> 1

但在 C++ 的输入输出中,它们被赋予了新的含义。

在输出中:

cpp 复制代码
std::cout << a;

这里的 << 叫流插入运算符。

可以理解为:

把右边的数据插入到左边的输出流中。

在输入中:

cpp 复制代码
std::cin >> a;

这里的 >> 叫流提取运算符。

可以理解为:

从输入流中提取数据,放入变量 a 中。

示例:

cpp 复制代码
#include <iostream>

int main()
{
    int age = 0;
    double score = 0.0;

    std::cout << "请输入年龄和成绩:";
    std::cin >> age >> score;

    std::cout << "年龄:" << age << "\n";
    std::cout << "成绩:" << score << "\n";

    return 0;
}

如果输入:

txt 复制代码
18 95.5

输出:

txt 复制代码
年龄:18
成绩:95.5

五、C++ 输入输出相比 printf / scanf 的优势

C 语言中,使用 printfscanf 时要手动写格式控制符。

例如:

c 复制代码
int a = 10;
double b = 3.14;

printf("%d %lf\n", a, b);

其中:

  • %d 对应 int
  • %lf 对应 double

如果格式写错,可能会出现错误输出甚至未定义行为。

而 C++ 的 coutcin 可以自动识别变量类型:

cpp 复制代码
int a = 10;
double b = 3.14;
char c = 'x';

std::cout << a << " " << b << " " << c << "\n";

不需要你手动写 %d%lf%c

这对初学者非常友好。

不过,C++ IO 流底层涉及类、对象和运算符重载,这些内容后面再深入理解。现在先会用即可。


六、endl\n 有什么区别?

很多时候我们会写:

cpp 复制代码
cout << "hello" << endl;

也可以写:

cpp 复制代码
cout << "hello\n";

它们都能换行,但不完全一样。

endl 做了两件事:

  1. 输出一个换行;
  2. 刷新输出缓冲区。

\n 只是换行。

所以在普通输出场景中,尤其是大量输出时,通常更推荐使用:

cpp 复制代码
cout << '\n';

而不是频繁使用:

cpp 复制代码
cout << endl;

因为频繁刷新缓冲区可能降低效率。

不过这里更具体的内容无法过多展开(没事之后会填坑的,可以期待一下)


七、C++ 输入输出的效率

在一些竞赛题或大量输入输出场景中,C++ 的 cin/cout 默认可能比 scanf/printf 慢。

可以在 main 函数开头加入:

cpp 复制代码
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

完整示例:

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

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n = 0;
    cin >> n;

    cout << n << '\n';

    return 0;
}

这几行代码的作用可以简单理解为:

  • 关闭 C++ IO 和 C IO 的同步;
  • 解除 cincout 的绑定;
  • 减少不必要的刷新操作。

注意:

使用这几行加速后,不建议混用 cin/coutscanf/printf

否则可能出现输入输出顺序异常。


八、为什么需要命名空间?

C++ 项目中会有大量变量、函数、结构体、类。

如果所有名字都放在全局作用域中,很容易发生命名冲突。

比如 C 语言中有一个标准库函数叫 rand

如果你又定义了一个全局变量:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int rand = 10;

int main()
{
    printf("%d\n", rand);
    return 0;
}

这就可能和库函数 rand 发生冲突。

C++ 引入 namespace,就是为了解决这种名字冲突问题。

可以把命名空间理解成:

给一批名字套了一个"外壳"或"作用域"。

不同命名空间中可以有同名变量、同名函数,但不会冲突。


九、namespace 的基本定义

定义命名空间的语法:

cpp 复制代码
namespace 命名空间名
{
    // 变量
    // 函数
    // 类型
}

示例:

cpp 复制代码
#include <iostream>

namespace my_space
{
    int rand = 10;

    int Add(int left, int right)
    {
        return left + right;
    }

    struct Node
    {
        int val;
        Node* next;
    };
}

int main()
{
    std::cout <<  my_space::rand << '\n';
    std::cout <<  my_space::Add(1, 2) << '\n';

    return 0;
}

这里:

cpp 复制代码
 my_space::rand
 my_space::Add(1, 2)

中的 :: 叫作用域解析运算符。

它的意思是:

到 bit 这个命名空间中寻找 rand 或 Add。


十、命名空间可以嵌套

命名空间中还可以继续定义命名空间。

cpp 复制代码
#include <iostream>

namespace  my_space
{
    namespace Luna
    {
        int rand = 1;

        int Add(int left, int right)
        {
            return left + right;
        }
    }

    namespace Lucia
    {
        int rand = 2;

        int Add(int left, int right)
        {
            return (left + right) * 10;
        }
    }
}

int main()
{
    std::cout << my_space::Luna::rand << '\n';
    std::cout << my_space::Lucia::rand << '\n';

    std::cout << my_space::Luna::Add(1, 2) << '\n';
    std::cout << my_space::Lucia::Add(1, 2) << '\n';

    return 0;
}

输出:

txt 复制代码
1
2
3
30

这里 LunaLucia 中都定义了 randAdd,但因为它们处于不同命名空间,所以不会冲突。


十一、同名 namespace 会合并

C++ 中,同一个命名空间可以在多个地方定义。

例如:

cpp 复制代码
namespace my_space
{
    int a = 10;
}

namespace my_space
{
    int b = 20;
}

这两个 bit 不会冲突,而是会合并成同一个命名空间。

可以理解为:

cpp 复制代码
namespace my_space
{
    int a = 10;
    int b = 20;
}

这个特性在多文件工程中很常见。

比如一个项目中,栈相关代码放在 Stack.h / Stack.cpp,队列相关代码放在 Queue.h / Queue.cpp,但它们都可以放在同一个项目命名空间中:

cpp 复制代码
namespace my_space
{
    // Stack 相关声明
}

namespace my_space
{
    // Queue 相关声明
}

十二、std 命名空间是什么?

C++ 标准库中的很多内容都放在 std 命名空间中。

所以如果你写:

cpp 复制代码
cout << "hello";

编译器可能找不到 cout

因为它真正的名字在 std 命名空间中:

cpp 复制代码
std::cout

这也是为什么最标准的写法是:

cpp 复制代码
#include <iostream>

int main()
{
    std::cout << "hello world\n";
    return 0;
}

十三、命名空间的三种使用方式

1. 指定命名空间访问

cpp 复制代码
std::cout << "hello\n";

或者:

cpp 复制代码
my_space::Add(1, 2);

这是最推荐的方式。

优点是非常清晰:

这个名字来自哪个命名空间,一眼就能看出来。

实际项目中尤其推荐这种写法。


2. using 展开某个成员

如果某个成员经常用,也可以单独展开:

cpp 复制代码
#include <iostream>

using std::cout;
using std::cin;

int main()
{
    int a = 0;
    cin >> a;
    cout << a << '\n';

    return 0;
}

这种方式比 using namespace std; 更安全。

因为它只展开你指定的名字。


3. using namespace 展开整个命名空间

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

int main()
{
    cout << "hello\n";
    return 0;
}

这种写法在初学阶段很常见,因为方便。

但是在大型项目中不推荐在头文件或全局范围中随便写:

cpp 复制代码
using namespace std;

原因是它会把 std 中大量名字引入当前作用域,增加命名冲突风险。

简单记:

场景 建议
日常小练习 可以使用 using namespace std;
正式项目源文件 谨慎使用
头文件 不建议使用
推荐写法 std::coutusing std::cout

十四、缺省参数是什么?

缺省参数也叫默认参数。

它的意思是:

在函数声明或定义时,给参数指定默认值。调用函数时,如果没有传这个参数,就使用默认值。

示例:

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

void Print(int n = 1)
{
    cout << "n = " << n << '\n';
}

int main()
{
    Print();     // 没有传参,使用默认值 1
    Print(10);   // 传参,使用 10

    return 0;
}

输出:

txt 复制代码
n = 1
n = 10

十五、全缺省参数和半缺省参数

1. 全缺省参数

所有参数都有默认值,叫全缺省。

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

void Func(int a = 10, int b = 20, int c = 30)
{
    cout << "a = " << a << '\n';
    cout << "b = " << b << '\n';
    cout << "c = " << c << "\n\n";
}

int main()
{
    Func();
    Func(1);
    Func(1, 2);
    Func(1, 2, 3);

    return 0;
}

调用效果:

cpp 复制代码
Func();         // a=10, b=20, c=30
Func(1);        // a=1,  b=20, c=30
Func(1, 2);     // a=1,  b=2,  c=30
Func(1, 2, 3);  // a=1,  b=2,  c=3

2. 半缺省参数

部分参数有默认值,叫半缺省。

cpp 复制代码
void Func(int a, int b = 10, int c = 20)
{
    cout << a << " " << b << " " << c << '\n';
}

调用:

cpp 复制代码
Func(100);           // 100 10 20
Func(100, 200);      // 100 200 20
Func(100, 200, 300); // 100 200 300

十六、缺省参数必须从右往左给

下面这种写法是正确的:

cpp 复制代码
void Func(int a, int b = 10, int c = 20);

因为默认值从右往左连续给。

但是下面这种写法是错误的:

cpp 复制代码
void Func(int a = 10, int b, int c = 20);

原因是:

C++ 调用函数时,实参是从左往右匹配形参的。

如果中间跳过一个参数不给默认值,编译器就无法判断你传入的参数到底应该匹配谁。

当然各位肯定想问到底为何要这么划定呢?这个具体就得问当年c++的创始人了,大家可以理解为这是一种规范。


十七、声明和定义分离时,默认值写在哪里?

如果函数声明和定义分离,默认参数一般写在函数声明处,不要在声明和定义中重复写。

例如:

Stack.h

cpp 复制代码
#pragma once

typedef int STDataType;

struct Stack
{
    STDataType* a;
    int top;
    int capacity;
};

void STInit(Stack* ps, int n = 4);

Stack.cpp

cpp 复制代码
#include "Stack.h"
#include <cassert>
#include <cstdlib>

void STInit(Stack* ps, int n)
{
    assert(ps);
    ps->a = (STDataType*)malloc(sizeof(STDataType) * n);
    ps->top = 0;
    ps->capacity = n;
}

test.cpp

cpp 复制代码
#include "Stack.h"

int main()
{
    Stack s1;
    STInit(&s1);       // 默认开 4 个空间

    Stack s2;
    STInit(&s2, 1000); // 明确开 1000 个空间

    return 0;
}

注意:

cpp 复制代码
void STInit(Stack* ps, int n = 4);

默认值写在声明中。

定义时写:

cpp 复制代码
void STInit(Stack* ps, int n)

不要重复写:

cpp 复制代码
void STInit(Stack* ps, int n = 4) // 不建议

十八、缺省参数适合什么场景?

缺省参数适合:

  • 某个参数大多数时候使用固定值;
  • 但少数情况下又希望调用者可以自定义;
  • 不想写太多重载函数。

例如初始化栈:

cpp 复制代码
void STInit(Stack* ps, int n = 4);

大多数情况下默认开 4 个空间即可。

如果用户明确知道后面要插入大量数据,也可以直接传入更大的初始容量:

cpp 复制代码
STInit(&s, 1000);

这就比强制每次都传参数更灵活。


总结

这一篇从 C++ 第一个程序讲到了命名空间、输入输出和缺省参数。

重点如下:

  1. C++ 源文件通常使用 .cpp 后缀。
  2. C++ 中可以使用 iostream 进行输入输出。
  3. std::cout 用于输出,std::cin 用于输入。
  4. << 在输出流中表示流插入,>> 在输入流中表示流提取。
  5. endl 会换行并刷新缓冲区,普通换行更推荐使用 '\n'
  6. 命名空间用于解决命名冲突和名字污染。
  7. std 是 C++ 标准库使用的命名空间。
  8. 项目中更推荐使用 std::coutusing std::cout,不建议在头文件中使用 using namespace std;
  9. 缺省参数可以让函数调用更灵活。
  10. 半缺省参数必须从右往左连续给默认值。
  11. 函数声明和定义分离时,默认值一般只写在声明处。

相关推荐
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第94题】【Mysql篇】第24题:什么是单路排序?什么是双路排序??
java·开发语言·数据库·mysql·面试·排序算法
于先生吖1 小时前
Java分账体系设计,网约车行程计费与到店线下结账一体化后端开发实战
java·开发语言
数据皮皮侠AI1 小时前
中国土地利用驱动因子数据集(9种驱动因子/裁剪到省市/Tif)
大数据·人工智能·笔记·能源·1024程序员节
thisiszdy1 小时前
<C++&C#> lambda表达式
java·c++·c#
晚风叙码1 小时前
C++类和对象(中)| 深挖四大默认成员函数:构造/析构/拷贝/赋值重载原理全解
c++
Cloud_Shy6181 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第三章 Item 17 - 20)
开发语言·笔记·python
三品吉他手会点灯1 小时前
C语言学习笔记 - 42.数据类型 - scanf函数深度解析
c语言·开发语言·笔记·学习
来生硬件工程师1 小时前
【51单片机学习笔记】:P01 STC89C52RC
笔记·学习·51单片机
混迹中的咸鱼1 小时前
游戏开发核心架构指南
c++·游戏·架构