C++入门基础复习:从命名空间到引用与nullptr,一篇重新捡回C++基础

C++入门基础复习:从命名空间到引用与nullptr,一篇重新捡回C++基础

本文用于 C++ 基础知识复习

适合已经学过 C++,但隔了一段时间需要重新回忆知识点的人

本文重点放在实际编程中会使用到的知识,C++发展历史等非实用内容不做总结


文章目录

一、C++程序的基本认识

C++兼容 C 语言绝大多数语法,因此很多 C 语言代码在 C++ 中依然可以使用

例如 C 语言中的:

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

int main()
{
    printf("hello world\n");

    return 0;
}

放到 C++ 中依然可以正常运行

不过 C++ 有自己的一套输入输出方式,因此更加标准的 C++ 写法通常是:

cpp 复制代码
#include <iostream>

using namespace std;

int main()
{
    cout << "hello world" << endl;

    return 0;
}

这里暂时可以先记住:

cpp 复制代码
cout
cin
endl

后面会详细解释


二、C++文件为什么使用.cpp

C++源文件通常使用:

text 复制代码
.cpp

作为后缀

例如:

text 复制代码
test.cpp
main.cpp
Stack.cpp
SeqList.cpp

编译器看到 .cpp 文件后,会按照 C++ 的规则进行编译

如果是在 Linux 环境下:

bash 复制代码
g++ test.cpp

而 C 语言通常使用:

bash 复制代码
gcc test.c

因此:

text 复制代码
.c   → C语言
.cpp → C++语言

这是学习 C/C++ 时非常基础但非常重要的一个区别


三、命名空间 namespace

为什么需要命名空间

C++程序越来越大以后,会出现一个非常现实的问题:

名字冲突

例如:

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

int rand = 10;

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

    return 0;
}

这里自己定义了一个:

cpp 复制代码
int rand = 10;

但是标准库中本身就存在 rand

于是就产生了名字冲突

这就是命名空间存在的主要原因

namespace 的作用就是对标识符进行隔离,避免命名冲突和名字污染


四、namespace的基本使用

定义一个命名空间:

cpp 复制代码
namespace bit
{
    int rand = 10;

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

可以把它理解成:

text 复制代码
bit
 ├── rand
 └── Add

这些东西都属于 bit 这个命名空间

访问的时候使用:

cpp 复制代码
bit::rand

或者:

cpp 复制代码
bit::Add(10, 20)

这里的:

cpp 复制代码
::

叫做作用域解析运算符


五、命名空间中可以放什么

namespace 中可以定义:

  • 变量
  • 函数
  • 类型
  • 结构体
  • 类
  • 其他命名空间

例如:

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

    int Add(int x, int y)
    {
        return x + y;
    }

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

使用:

cpp 复制代码
bit::a;

bit::Add(1, 2);

bit::Node node;

六、命名空间可以嵌套

namespace 可以继续嵌套 namespace

例如:

cpp 复制代码
namespace bit
{
    namespace pg
    {
        int rand = 1;
    }

    namespace hg
    {
        int rand = 2;
    }
}

访问:

cpp 复制代码
bit::pg::rand

结果:

text 复制代码
1

访问:

cpp 复制代码
bit::hg::rand

结果:

text 复制代码
2

可以把它理解成目录结构:

text 复制代码
bit
├── pg
│   └── rand
│
└── hg
    └── rand

所以:

cpp 复制代码
bit::pg::rand

就是:

text 复制代码
bit → pg → rand

七、多文件中的namespace

这是以后写项目时非常重要的一个知识

假设:

Stack.h

cpp 复制代码
namespace bit
{
    struct Stack
    {
        int* a;
        int top;
        int capacity;
    };
}

Stack.cpp

cpp 复制代码
namespace bit
{
    void STInit(Stack* ps)
    {
        // ...
    }
}

虽然两个文件分别定义了 namespace bit

但是它们实际上属于同一个命名空间

也就是说:

cpp 复制代码
namespace bit
{
    // Stack.h
}

和:

cpp 复制代码
namespace bit
{
    // Stack.cpp
}

会被认为是同一个 bit

这一点在以后进行多文件项目开发时非常常见


八、std命名空间

C++标准库中的大量内容都放在:

cpp 复制代码
std

命名空间中

例如:

cpp 复制代码
std::cout
std::cin
std::endl
std::string
std::vector

因此严格来说:

cpp 复制代码
cout

实际上是:

cpp 复制代码
std::cout

而:

cpp 复制代码
endl

实际上是:

cpp 复制代码
std::endl

九、namespace的三种使用方式

假设:

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

直接指定命名空间

最推荐的方式:

cpp 复制代码
cout << bit::a << endl;
cout << bit::b << endl;

这种方式最明确

看到:

cpp 复制代码
bit::a

就知道 a 来自 bit


十、using展开单个成员

可以:

cpp 复制代码
using bit::a;

之后就可以直接:

cpp 复制代码
cout << a << endl;

但是:

cpp 复制代码
b

仍然需要:

cpp 复制代码
bit::b

这种方式适合:

经常使用,而且确定不会产生冲突的成员


十一、using namespace

还可以直接:

cpp 复制代码
using namespace bit;

这样:

cpp 复制代码
a
b

都可以直接使用

例如:

cpp 复制代码
#include <iostream>

using namespace std;

int main()
{
    cout << "hello" << endl;

    return 0;
}

这里的:

cpp 复制代码
using namespace std;

就是把 std 命名空间中的成员展开到当前使用环境中


十二、三种方式怎么选

可以简单记:

text 复制代码
bit::a

最明确

cpp 复制代码
using bit::a;

只展开一个成员

cpp 复制代码
using namespace bit;

全部展开

在项目开发中,一般推荐:

cpp 复制代码
std::cout
std::vector
std::string

这种明确指定的方式

而学习阶段的小程序中:

cpp 复制代码
using namespace std;

会比较方便


十三、C++输入输出

C++最常见的输入输出:

cpp 复制代码
cin
cout

需要:

cpp 复制代码
#include <iostream>

十四、cout输出

例如:

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

cout << a << endl;
cout << b << endl;
cout << c << endl;

输出:

text 复制代码
10
3.14
a

C语言:

c 复制代码
printf("%d", a);
printf("%lf", b);
printf("%c", c);

而 C++:

cpp 复制代码
cout << a;
cout << b;
cout << c;

不需要手动指定 %d、%lf、%c

因为 C++ IO 可以根据变量类型选择对应的处理方式,本质上和后续要学习的函数重载有关


十五、<<到底是什么

C++中:

cpp 复制代码
cout << a;

这里的:

cpp 复制代码
<<

叫做:

流插入运算符

意思可以简单理解成:

text 复制代码
把a插入到cout输出流中

例如:

cpp 复制代码
cout << a << b << c << endl;

可以连续输出多个内容


十六、cin输入

例如:

cpp 复制代码
int a;
double b;
char c;

cin >> a;
cin >> b;
cin >> c;

也可以写成:

cpp 复制代码
cin >> a >> b >> c;

这里的:

cpp 复制代码
>>

叫做:

流提取运算符

可以理解为:

text 复制代码
从输入流中提取数据

十七、cout和cin的基本记忆

这一组建议直接记住:

cpp 复制代码
cout << 数据;

表示:

输出

cpp 复制代码
cin >> 变量;

表示:

输入

也就是:

text 复制代码
cout <<   输出
cin  >>   输入

千万不要记反


十八、endl

例如:

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

endl 的作用不只是换行

它相当于:

text 复制代码
换行 + 刷新缓冲区

所以:

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

和:

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

都可以实现换行

但 endl 还会刷新缓冲区

因此不要无脑大量使用 endl


十九、C++ IO性能优化

在输入输出量非常大的场景,例如某些竞赛题中,可以使用:

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

例如:

cpp 复制代码
#include <iostream>

using namespace std;

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

    return 0;
}

它们主要用于提高 C++ IO 的效率

普通的小程序一般不用太在意


二十、缺省参数

缺省参数也叫:

默认参数

例如:

cpp 复制代码
void Func(int a = 10)
{
    cout << a << endl;
}

调用:

cpp 复制代码
Func();

相当于:

cpp 复制代码
Func(10);

调用:

cpp 复制代码
Func(20);

那么就使用传入的:

text 复制代码
20

因此:

cpp 复制代码
Func();

使用默认值

而:

cpp 复制代码
Func(20);

使用指定值


二十一、全缺省参数

例如:

cpp 复制代码
void Func(int a = 10, int b = 20, int c = 30)
{
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
}

可以这样调用:

cpp 复制代码
Func();

此时:

text 复制代码
a = 10
b = 20
c = 30

也可以:

cpp 复制代码
Func(1);

那么:

text 复制代码
a = 1
b = 20
c = 30

也可以:

cpp 复制代码
Func(1, 2);

那么:

text 复制代码
a = 1
b = 2
c = 30

也可以:

cpp 复制代码
Func(1, 2, 3);

二十二、半缺省参数

例如:

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

这是合法的

但是:

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

不合法

C++规定:

半缺省参数必须从右向左连续缺省

也就是说:

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

可以

但是不能:

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

二十三、缺省参数的调用规则

例如:

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

可以:

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

但是不能跳着传:

cpp 复制代码
Func(100, , 300);

因为函数调用时必须:

从左到右依次传递实参


二十四、缺省参数声明和定义的问题

如果函数声明和定义分离:

Stack.h

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

Stack.cpp

cpp 复制代码
void STInit(ST* ps, int n)
{
    // ...
}

默认参数应该写在:

函数声明中

不要声明和定义同时写:

cpp 复制代码
void STInit(ST* ps, int n = 4)
{
}

二十五、函数重载

函数重载是 C++ 非常重要的一个特性

C语言不允许在同一作用域中定义同名函数

但是 C++ 可以

前提是:

参数不同


二十六、参数类型不同

例如:

cpp 复制代码
int Add(int left, int right)
{
    return left + right;
}

又定义:

cpp 复制代码
double Add(double left, double right)
{
    return left + right;
}

两个函数名字一样:

cpp 复制代码
Add

但是参数类型不同

所以构成函数重载

调用:

cpp 复制代码
Add(10, 20);

匹配:

cpp 复制代码
int Add(int, int)

调用:

cpp 复制代码
Add(10.1, 20.2);

匹配:

cpp 复制代码
double Add(double, double)

二十七、参数个数不同

例如:

cpp 复制代码
void f()
{
}

void f(int a)
{
}

两个函数参数个数不同

所以构成重载


二十八、参数类型顺序不同

例如:

cpp 复制代码
void f(int a, char b)
{
}

void f(char b, int a)
{
}

参数类型顺序不同

也构成重载


二十九、返回值不同不能构成重载

例如:

cpp 复制代码
int f()
{
    return 1;
}

和:

cpp 复制代码
double f()
{
    return 1.0;
}

这样是不行的

因为调用:

cpp 复制代码
f();

的时候,编译器无法仅仅根据返回值判断到底调用哪个函数

所以:

返回值不同不能作为函数重载的条件

这是非常经典的面试和考试知识点


三十、函数重载与默认参数容易产生歧义

例如:

cpp 复制代码
void f()
{
}

void f(int a = 10)
{
}

现在调用:

cpp 复制代码
f();

就产生了歧义

因为:

cpp 复制代码
f();

既可以调用:

cpp 复制代码
void f()

也可以调用:

cpp 复制代码
void f(int a = 10)

编译器不知道到底选哪个

所以:

函数重载和缺省参数同时使用时,要特别注意二义性


三十一、引用

这是 C++ 非常核心的知识

引用的本质:

给已经存在的变量取一个别名

基本语法:

cpp 复制代码
类型& 引用名 = 变量;

例如:

cpp 复制代码
int a = 10;

int& b = a;

此时:

text 复制代码
a
b

实际上指向同一个对象

可以简单理解成:

text 复制代码
a
 ↓
同一块内存
 ↑
b

三十二、引用不是创建新变量

例如:

cpp 复制代码
int a = 10;
int& b = a;

不是创建两个独立的变量

而是:

text 复制代码
a ------┐
    ├── 同一个对象
b ------┘

所以:

cpp 复制代码
b = 20;

实际上修改的是:

cpp 复制代码
a

因此:

cpp 复制代码
cout << a << endl;

结果:

text 复制代码
20

三十三、引用的三个重要特性

引用有三个必须记住的特点

1. 定义时必须初始化

错误:

cpp 复制代码
int& a;

正确:

cpp 复制代码
int x = 10;
int& a = x;

2. 一个变量可以有多个引用

例如:

cpp 复制代码
int a = 10;

int& b = a;
int& c = a;
int& d = a;

此时:

text 复制代码
a
b
c
d

都是同一个对象的不同名字


3. 引用一旦绑定,不能更换对象

例如:

cpp 复制代码
int a = 10;
int b = 20;

int& ra = a;

ra = b;

很多初学者会认为:

cpp 复制代码
ra

从 a 改成引用 b

实际上不是

真正发生的是:

cpp 复制代码
a = b;

也就是:

text 复制代码
a变成20

ra 依旧引用:

cpp 复制代码
a

所以一定要区分:

cpp 复制代码
int& ra = a;

这是:

建立引用关系

而:

cpp 复制代码
ra = b;

这是:

给引用的对象赋值


三十四、引用为什么可以用于函数传参

例如交换两个变量:

cpp 复制代码
void Swap(int& x, int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}

调用:

cpp 复制代码
int a = 10;
int b = 20;

Swap(a, b);

最终:

text 复制代码
a = 20
b = 10

因为:

cpp 复制代码
x

是:

cpp 复制代码
a

的别名

而:

cpp 复制代码
y

是:

cpp 复制代码
b

的别名

所以函数内部修改:

cpp 复制代码
x
y

实际上就是修改:

cpp 复制代码
a
b

三十五、引用传参和指针传参

C语言中通常使用指针:

cpp 复制代码
void Swap(int* x, int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

调用:

cpp 复制代码
Swap(&a, &b);

C++可以使用引用:

cpp 复制代码
void Swap(int& x, int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}

调用:

cpp 复制代码
Swap(a, b);

所以引用传参的一个重要优势就是:

代码更加简洁,使用起来更加方便


三十六、引用和指针的核心区别

这一部分非常重要

对比 引用 指针
本质 别名 存储地址
是否需要初始化 必须 语法上不必须
是否可以改变指向 不可以 可以
访问对象 直接访问 需要解引用
空指针问题 较少 容易出现
野指针问题 较少 容易出现

例如指针:

cpp 复制代码
int a = 10;

int* p = &a;

*p = 20;

引用:

cpp 复制代码
int a = 10;

int& r = a;

r = 20;

三十七、引用和指针的sizeof区别

假设:

cpp 复制代码
int a = 10;

int& r = a;
int* p = &a;

引用在语言层面不是一个独立变量

而指针本身是一个变量,需要存储地址

在常见平台下:

text 复制代码
32位平台指针通常为4字节
64位平台指针通常为8字节

所以不要把:

cpp 复制代码
int&

和:

cpp 复制代码
int*

简单认为是一回事

它们的语义不同


三十八、const引用

普通引用:

cpp 复制代码
int& r = a;

可以修改 a

而:

cpp 复制代码
const int& r = a;

表示通过 r:

不能修改a

例如:

cpp 复制代码
int a = 10;

const int& r = a;

// r = 20; // 错误

三十九、const对象必须使用const引用

例如:

cpp 复制代码
const int a = 10;

不能:

cpp 复制代码
int& r = a;

因为这样相当于把:

text 复制代码
只读权限

扩大成:

text 复制代码
可读可写权限

这是不允许的

应该:

cpp 复制代码
const int& r = a;

四十、普通对象可以使用const引用

例如:

cpp 复制代码
int a = 10;

const int& r = a;

这是允许的

因为:

text 复制代码
普通对象
↓
只读引用

相当于:

缩小访问权限

这是安全的

所以记住:

text 复制代码
权限不能放大
权限可以缩小

四十一、const引用可以引用临时对象

例如:

cpp 复制代码
const int& r = 30;

这是合法的

但是:

cpp 复制代码
int& r = 30;

不合法

为什么?

因为:

cpp 复制代码
30

是一个临时对象

C++规定临时对象具有常性

所以只能使用:

cpp 复制代码
const int&

来引用它


四十二、什么是临时对象

例如:

cpp 复制代码
int a = 10;

const int& r = a * 3;

表达式:

cpp 复制代码
a * 3

会产生一个计算结果

编译器需要一个临时空间保存这个结果

这个没有名字的对象就可以理解为:

临时对象

因此:

cpp 复制代码
const int& r = a * 3;

可以成立


四十三、引用返回值

引用不仅可以用于参数

还可以用于返回值

例如:

cpp 复制代码
int& Top()
{
    return a[index];
}

这样调用:

cpp 复制代码
Top() += 10;

就可以直接修改原来的对象

例如:

cpp 复制代码
int& STTop(ST& rs)
{
    assert(rs.top > 0);

    return rs.a[rs.top - 1];
}

然后:

cpp 复制代码
STTop(st) += 10;

实际上修改的是栈顶元素本身

这一部分以后学习类和对象、运算符重载、STL时会经常遇到


四十四、引用简化二级指针

C语言链表中,如果需要修改头指针,经常会使用:

cpp 复制代码
void ListPushBack(Node** pphead);

调用:

cpp 复制代码
ListPushBack(&head, 1);

C++中可以使用:

cpp 复制代码
void ListPushBack(Node*& phead);

这里:

cpp 复制代码
Node*&

可以理解成:

给指针变量取别名

因此:

cpp 复制代码
Node* phead = NULL;

ListPushBack(phead, 1);

函数内部修改:

cpp 复制代码
phead

实际上修改的是外面的:

cpp 复制代码
phead

所以可以用引用来简化一些原本需要二级指针的场景


四十五、inline内联函数

C++提供:

cpp 复制代码
inline

关键字

例如:

cpp 复制代码
inline int Add(int x, int y)
{
    return x + y;
}

编译器在满足条件时,可以把函数调用位置展开

例如:

cpp 复制代码
int ret = Add(1, 2);

可能类似于直接把函数代码放到调用位置

这样可以减少函数调用相关的开销


四十六、inline不是强制命令

非常重要:

cpp 复制代码
inline

只是给编译器的一个建议

不是:

text 复制代码
加了inline → 编译器一定展开

而是:

text 复制代码
加了inline → 建议编译器考虑展开

最终是否展开由编译器决定

例如复杂函数:

cpp 复制代码
inline int Func(...)
{
    // 大量复杂代码
}

编译器完全可以选择不展开

所以不要把:

cpp 复制代码
inline

理解成:

强制内联


四十七、inline适合什么函数

一般适合:

短小、频繁调用的函数

例如:

cpp 复制代码
inline int Add(int x, int y)
{
    return x + y;
}

而特别复杂的函数、递归函数等,即使写了:

cpp 复制代码
inline

编译器也可能不进行展开


四十八、inline与宏函数

C语言中经常使用宏:

cpp 复制代码
#define ADD(a, b) ((a) + (b))

但是宏存在很多问题

例如:

  • 不进行类型检查
  • 优先级容易出问题
  • 调试困难
  • 宏替换规则比较容易产生坑

例如:

cpp 复制代码
#define ADD(a, b) a + b

然后:

cpp 复制代码
ADD(1, 2) * 5

宏展开后可能变成:

cpp 复制代码
1 + 2 * 5

结果就不是我们想要的:

text 复制代码
(1 + 2) * 5

所以宏通常需要大量括号:

cpp 复制代码
#define ADD(a, b) ((a) + (b))

而 C++ 可以通过:

cpp 复制代码
inline

函数来实现更加安全的函数式封装


四十九、inline为什么一般写在头文件

假设:

F.h

cpp 复制代码
inline void f(int i);

F.cpp

cpp 复制代码
void f(int i)
{
    cout << i << endl;
}

这种把 inline 声明和定义分开的方式容易导致链接问题

因为 inline 函数需要让调用它的地方看到函数定义,才能进行相应处理

因此实际开发中通常直接:

cpp 复制代码
// F.h

inline void f(int i)
{
    cout << i << endl;
}

也就是:

inline函数通常声明和定义一起放在头文件中


五十、nullptr

这是 C++11 引入的一个重要特性

以前我们经常使用:

cpp 复制代码
NULL

表示空指针

但是:

cpp 复制代码
NULL

本质上可能被定义为:

cpp 复制代码
0

这样就会产生一个问题


五十一、NULL为什么会产生歧义

例如:

cpp 复制代码
void f(int x)
{
    cout << "f(int x)" << endl;
}

void f(int* ptr)
{
    cout << "f(int* ptr)" << endl;
}

现在:

cpp 复制代码
f(0);

显然调用:

cpp 复制代码
f(int)

但是:

cpp 复制代码
f(NULL);

如果 NULL 是:

cpp 复制代码
0

那么它依然可能匹配:

cpp 复制代码
f(int)

而不是我们希望的:

cpp 复制代码
f(int*)

这就是 NULL 的一个问题


五十二、nullptr解决了这个问题

C++11引入:

cpp 复制代码
nullptr

它专门用于表示:

空指针

例如:

cpp 复制代码
int* p = nullptr;

这样比:

cpp 复制代码
int* p = NULL;

更加明确


五十三、nullptr和0的区别

nullptr:

可以转换成指针类型

但:

不能隐式转换成整数类型

因此:

cpp 复制代码
void f(int x)
{
}

void f(int* p)
{
}

调用:

cpp 复制代码
f(nullptr);

会选择:

cpp 复制代码
f(int* p)

这就是 nullptr 相比 NULL 的一个重要优势


五十四、NULL、0、nullptr总结

可以直接记住:

text 复制代码
0
↓
整数0
text 复制代码
NULL
↓
传统空指针表示
↓
可能本质是0
text 复制代码
nullptr
↓
C++11
↓
专门表示空指针

现代 C++ 中:

cpp 复制代码
int* p = nullptr;

通常比:

cpp 复制代码
int* p = NULL;

更加推荐


五十五、这一篇最重要的知识点

如果你现在是在复习,而不是第一次学习,那么这一篇不需要每个知识点都死记

真正应该重点重新建立下面这张知识网络:

text 复制代码
C++基础
│
├── namespace
│   ├── 解决命名冲突
│   ├── namespace
│   ├── ::
│   ├── using
│   └── std
│
├── C++ IO
│   ├── cin
│   ├── cout
│   ├── endl
│   ├── <<
│   └── >>
│
├── 缺省参数
│   ├── 全缺省
│   ├── 半缺省
│   └── 从右向左连续缺省
│
├── 函数重载
│   ├── 参数类型不同
│   ├── 参数个数不同
│   ├── 参数顺序不同
│   └── 返回值不同不能构成重载
│
├── 引用
│   ├── 本质是别名
│   ├── 必须初始化
│   ├── 不能改变指向
│   ├── 引用传参
│   ├── 引用返回值
│   └── const引用
│
├── 指针与引用
│   ├── 指针存地址
│   ├── 引用是别名
│   ├── 指针可以改变指向
│   └── 引用不能改变指向
│
├── inline
│   ├── 内联函数
│   ├── 编译器建议
│   ├── 短小高频函数
│   └── 通常放头文件
│
└── nullptr
    ├── C++11
    ├── 空指针
    └── 避免NULL带来的重载歧义

五十六、复习时最容易混淆的几个地方

1. &到底是什么

在不同场景中含义不同

cpp 复制代码
int& r = a;

这里:

cpp 复制代码
&

表示:

引用

而:

cpp 复制代码
int* p = &a;

这里:

cpp 复制代码
&

表示:

取地址

一定要根据语法位置判断


2. <<和>>

C语言中:

cpp 复制代码
<<
>>

可以表示位移运算

C++ IO中:

cpp 复制代码
cout << a;
cin >> a;

又有了流插入和流提取的含义

所以以后看到它们不要死记一个含义,要根据上下文判断


3. 引用不是指针

不要简单理解成:

text 复制代码
引用 = 指针

更准确的是:

text 复制代码
引用和指针具有部分相似功能
但语义不同

引用更像:

text 复制代码
变量的别名

指针更像:

text 复制代码
保存地址的变量

4. ra = b不会改变引用

例如:

cpp 复制代码
int a = 10;
int b = 20;

int& ra = a;

ra = b;

不是:

text 复制代码
ra → b

而是:

text 复制代码
a = b

最终:

text 复制代码
a = 20
b = 20
ra 仍然引用a

这个知识点非常重要


5. 返回值不能构成重载

错误理解:

cpp 复制代码
int f();
double f();

认为可以重载

实际上不行

因为:

cpp 复制代码
f();

无法通过返回值决定调用哪一个


6. 默认参数和函数重载可能产生二义性

例如:

cpp 复制代码
void f();

void f(int x = 10);

调用:

cpp 复制代码
f();

就不知道调用谁

所以写代码的时候要特别注意


总结

这一部分看起来知识点很多,但实际上可以归纳成几条主线:

namespace → 解决名字冲突

cin/cout → C++标准输入输出

缺省参数 → 减少函数调用时必须传递的参数

函数重载 → 同名函数根据参数进行区分

引用 → 给已有对象取别名

const引用 → 只读访问,同时可以引用临时对象

inline → 给编译器的内联建议

nullptr → C++11更加规范的空指针表示

其中真正需要重点掌握的是:

text 复制代码
函数重载
引用
const引用
指针与引用的区别
缺省参数
nullptr

尤其是引用、指针、const引用,后面学习 STL、类和对象、数据结构以及各种高级 C++ 特性时都会不断出现

所以这一部分不要只停留在"看懂代码"的程度,而应该能够自己解释:

这个语法为什么存在

它解决了什么问题

它和 C 语言中的什么东西类似

它和什么东西容易混淆

实际开发中什么时候使用

这样才算真正把以前学过的东西重新捡起来

相关推荐
(Charon)43 分钟前
【C++面试】手写线程池:任务队列、工作线程与优雅退出
开发语言·c++
言乐61 小时前
Python概括后端原理
开发语言·python·django·virtualenv·pygame
无名猿1 小时前
拷贝构造与拷贝赋值:调用时机、深浅拷贝与复制消除
c++·内存管理·现代c++·踩坑记录
@大迁世界1 小时前
JEV RAG:更高效的 RAG 系统解决方案?
开发语言·rust
大侠归来2 小时前
C语言素数判断:从入门到进阶
java·c语言·算法
谢亮_vipxieliang2 小时前
Spring Boot 自动配置原理:从 @SpringBootApplication 到自定义 Starter
java·spring boot·后端
朝朝辞暮i2 小时前
C++ 第一阶段总结:从零基础到 ROS2 前置语法
开发语言·c++·算法
朝朝辞暮i2 小时前
C++ 第 25 课:getter / setter + this
java·javascript·c++
2601_962885722 小时前
如何用 Python 回测定投策略(定期定额 DCA)?(和一次性买入对比)
开发语言·人工智能·python