前言
之前在更新C语言的博客,学校C++课程这学期已经开始了,当然,我在3月3号已经提前练习了基础代码Luminous/Luminousbegin,下面我也要开始更新C++的博客了,请大家多多支持。
这既是给自己的学习留个脚印,也希望能帮到和我一样,刚学完 C 语言、正在跟着学校课程入坑 C++ 的同学。
一、为什么我跟着学校课学,还要自己写练习?
这学期学校正式开了 C++ 程序设计课,但我发现:
- 学校课上的例子比较简单,光听不写很容易忘,必须自己动手敲一遍才能真正理解
- 最重要的是:我发现C++ 的核心其实就是 C 语言的升级版,之前写 C 语言通讯录、顺序表、链表打下的基础,完全可以无缝衔接过来
比如大家常说的 C++ 类class,在我看来本质就是 C 语言结构体struct的 "超级加强版"------C 的结构体只能存数据,而 C++ 的类既能存数据,又能把操作数据的函数也包进去,这不就是我们写数据结构时一直想做的事吗?
先把 C 语言的数据结构练熟了,再回头学 C++ 的面向对象,反而更容易理解它为什么要这么设计。
二、先搞懂:C++ 到底是什么?
2.1 C++ 的诞生与发展
C++ 是由本贾尼・斯特劳斯特卢普(Bjarne Stroustrup)在 1979 年于贝尔实验室设计的,最初叫 "带类的 C",1983 年正式命名为 C++。
它的设计初衷就是在 C 语言的基础上,增加面向对象编程的特性,解决 C 语言在大型复杂软件开发中表达能力、可维护性不足的问题。
2.2 C++ 的主要版本
C++ 的标准一直在更新,每个版本都带来了新的特性,我们现在学习主要用 C++11 及以上的版本:
| 版本 | 发布时间 | 核心特性 |
|---|---|---|
| C++98 | 1998 年 | 第一个官方标准,引入 STL 标准模板库 |
| C++11 | 2011 年 | 革命性更新,lambda、右值引用、智能指针、线程库 |
| C++14 | 2014 年 | 对 C++11 的完善和扩展 |
| C++17 | 2017 年 | 结构化绑定、文件系统库 |
| C++20 | 2020 年 | 协程、概念、模块化 |
这里插个有趣的瓜:C++23 本来计划加入网络库,结果因为标准委员会内部关于 senders/receivers 的争论,最终推迟到了 C++26,网上吐槽说 "等 C++ 网络库出来,我都退休了"。
2.3 C++ 和 C 的关系
- 完全兼容:绝大多数 C 代码可以直接在 C++ 编译器下运行
- 向下兼容:C 的所有语法在 C++ 中都能用
- 扩展增强:在 C 的基础上增加了面向对象、泛型编程、异常处理等特性
- 适用场景:C 更适合底层、性能要求极高的场景;C++ 更适合大型复杂项目的开发
三、我的第一批 C++ 练习
3.1 Cpp start:第一个 C++ 程序
cpp
#include<iostream>
using namespace std;
int main()
{
cout << "Hello world!" << endl;
return 0;
}
知识点详解:
#include<iostream>:引入 C++ 标准输入输出流头文件,对应 C 语言的stdio.husing namespace std;:展开标准命名空间,这样就不用写std::cout了cout:标准输出对象,对应 C 语言的printf<<:流插入运算符,自动识别变量类型,不用手动指定格式符endl:输出换行并刷新缓冲区,对应 C 语言的'\n'- C++ 程序的后缀是
.cpp,用 g++ 编译器编译,而不是 gcc
3.2 namespace_test:命名空间
cpp
namespace Luminous//单命名空间
{
void sayHello()
{
cout<<"Hello Luminous!"<<endl;
}
}
namespace working//命名空间的嵌套
{
namespace Luminous
{
void sayHello()
{
cout<<"Hello working Luminous!"<<endl;
}
}
int a = 10;
}
int main()
{
Luminous::sayHello();//调用单命名空间中的函数
working::Luminous::sayHello();//调用嵌套命名空间中的函数
cout << "a的值为:" << working::a << endl;
return 0;
}
知识点详解:
-
为什么需要命名空间?
- 解决 C 语言中全局变量、函数的命名冲突问题
- 比如 C 语言标准库有个
rand()函数,如果你自己也定义一个rand变量,就会编译报错 - 命名空间相当于给变量、函数套了个 "盒子",不同盒子里的同名变量不会冲突
-
命名空间的三种使用方式:
- 方式 1:
命名空间名::成员名(推荐,最安全) - 方式 2:
using 命名空间名::成员名(展开单个成员) - 方式 3:
using namespace 命名空间名(展开整个命名空间,小练习方便,大项目不推荐)
- 方式 1:
-
命名空间的特性:
- 可以嵌套定义
- 多个文件中定义的同名命名空间会自动合并
- C++ 标准库的所有内容都放在
std命名空间中
3.3 C++ 输入输出
cpp
#include<iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
cin >> a >> b >> c; // 连续输入多个变量,自动识别类型
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
return 0;
}
知识点详解:
cin:标准输入对象,对应 C 语言的scanf>>:流提取运算符,同样自动识别变量类型- 对比 C 语言的优势:
- 不用记
%d、%lf、%c等格式符,不容易写错 - 可以连续输入输出多个变量,代码更简洁
- 支持自定义类型的输入输出(后面学类的时候会用到)
- 不用记
3.4 缺省函数:
cpp
int add1(int a, int b = 10)//半缺省参数,b的默认值为10
{
return a + b;
}
int add2(int a = 50, int b = 20)//全缺省参数,a和b都有默认值
{
return a + b;
}
int main()
{
cout<<"sum1="<<add1(10) << endl; // 只传a,b用默认值10,输出20
cout << "sum2=" << add1(10, 20) << endl; // 传两个参数,用传入的值,输出30
cout << "sum3=" << add2() << endl; // 都不传,用默认值50+20=70
cout << "sum4=" << add2(5,10) << endl; // 传两个参数,输出15
return 0;
}
知识点详解:
-
什么是缺省参数?
- 声明或定义函数时,给参数指定一个默认值
- 调用函数时,如果没有传这个参数,就用默认值;如果传了,就用传入的值
-
缺省参数的规则:
- 半缺省参数必须从右往左依次连续缺省,不能间隔跳跃
- 函数调用时,实参必须从左往右依次传递,不能跳跃
- 函数声明和定义分离时,缺省参数只能写在声明中,不能同时写在声明和定义里
-
作用:
- 简化函数调用,不用为了不同的参数个数写多个重载函数
- 提高代码的灵活性
3.5 函数重载 test:函数重载
cpp
int Add(int a, int b)//函数重载1:int类型
{
return a + b;
}
double Add(double a, double b)//函数重载2:double类型
{
return a + b;
}
int main()
{
int a = 10, b = 20;
int sum1 = Add(a, b);
cout << "sum1 = " << sum1 << endl; // 输出30
double c = 15.5, d = 25.6;
double sum2 = Add(c, d);
cout << "sum2 = " << sum2 << endl; // 输出41.1
return 0;
}
知识点详解:
-
什么是函数重载?
- C++ 允许在同一作用域中定义多个同名函数
- 要求这些函数的形参列表不同(参数个数不同、参数类型不同、参数类型顺序不同)
- 编译器会根据实参的类型和个数,自动匹配对应的函数
-
注意:
- 返回值不同不能作为函数重载的条件(因为调用时无法区分)
- 缺省参数可能导致函数重载产生歧义(比如两个函数都能匹配同一个调用)
-
C 语言为什么不支持函数重载?
- C 语言编译后,函数名就是原来的名字,比如
Add编译后还是_Add - C++ 编译后,函数名会包含参数类型信息,比如
int Add(int,int)编译后变成_Add_int_int - 这样不同参数的同名函数,编译后的名字就不一样了,不会冲突
- C 语言编译后,函数名就是原来的名字,比如
3.6 引用 test:引用
cpp
// 传值:不能交换,因为传的是副本
void change1(int c, int d)
{
int a = c;
c = d;
d = a;
}
// 引用传参:可以交换,传的是变量本身
void change2(int& c, int& d)
{
int a = c;
c = d;
d = a;
}
// 指针传参:可以交换,传的是变量的地址
void change3(int* c, int* d)
{
int a = *c;
*c = *d;
*d = a;
}
int main()
{
int a = 10, b = 20;
change1(a, b);
cout << "change1后:a = " << a << ", b = " << b << endl; // 不变
int c = 10, d = 20;
change2(c, d);
cout << "change2后:c = " << c << ", d = " << d << endl; // 交换成功
int e = 10, f = 20;
change3(&e,&f);
cout << "change3后:e = " << e << ", f = " << f << endl; // 交换成功
return 0;
}
知识点详解:
-
什么是引用?
- 引用不是新定义一个变量,而是给已存在的变量取一个别名
- 编译器不会为引用变量开辟内存空间,它和引用的变量共用同一块内存空间
- 语法:
类型& 引用名 = 引用对象;
-
引用的特性:
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,就不能再引用其他实体(这是和指针最大的区别)
-
引用的使用场景:
- 引用传参:代替指针传参,代码更简洁,不容易出错
- 引用做返回值:减少拷贝,提高效率(后面学类的时候会详细讲)
-
引用和指针的区别:
- 引用是别名,不开空间;指针是存储地址的变量,开空间
- 引用必须初始化;指针可以不初始化(但不推荐)
- 引用不能改变指向;指针可以改变指向
- 引用没有空引用;指针有空指针
四、学习方法和资源推荐
4.1 我的学习方法
- 上课认真听:跟着学校老师的节奏走,先把基础概念搞懂
- 课后必写代码:每个知识点都自己写一遍测试用例,运行出错了再去查资料
- 写博客总结:每学完一个章节就写一篇博客,既能巩固知识,也能方便以后复习
- 结合数据结构:用 C++ 重写之前学过的 C 语言数据结构,既能练 C++,又能加深对数据结构的理解
4.2 资源推荐
- 参考文档 :
- C++ 参考手册 - cppreference.com:官方中文文档,信息最全
- Reference - C++ Reference:适合新手,内容易懂
- 书籍 :
- 《C++ Primer》:经典语法书,适合作为字典随时查阅
- 《STL 源码剖析》:中后期看,深入理解 STL 的实现原理
网上流传的 "21 天精通 C++" 只是个梗,C++ 是一门需要长期学习的语言,不用追求速成,一步一个脚印就好。
最后
从 C 到 C++,不是从零开始,而是站在之前的基础上更上一层楼。如果你也刚学完 C 语言,正在学 C++,不用害怕它的语法复杂,很多东西我们其实已经在 C 语言里接触过了,只是换了一种更优雅的写法。
接下来的日子,一起加油吧!