第一章— C++的基础知识


前言

C语言作为基础语言,带我们走进了编程世界,而今天我们将遇见一座蜿蜒挺拔的大山:C++
本系列运用的是C++ 11 及以上的知识,主要为11版


一、认识C++

C++的定义

C++是一种静态类型、编译式、通用的高级编程语言,由Bjarne Stroustrup于1979年在AT&T贝尔实验室开发。C++是C语言的扩展,支持过程化编程、面向对象编程和泛型编程。

C++的特点

  • 高性能‌:直接操作内存,运行效率接近硬件层面

  • 面向对象‌:支持类、封装、继承和多态,提升代码可维护性‌

  • 泛型编程‌:通过模板实现通用数据结构,增强代码复用性

  • 跨平台兼容‌:代码可编译为Windows、Linux、macOS等多个系统的可执行文件‌

  • ‌与C语言兼容‌:作为C的超集,可直接使用C语言的库和语法‌

二、C++的基础知识

1.命名空间

对于C语言的命名重复是否感到厌烦?C++的namespace恰好能够解决你的问题。

定义:C++命名空间是一种解决命名冲突的重要机制,通过将全局作用域划分为不同区域来组织代码‌

  • 创建
    使用namespace关键字
namespace SXR
{
	int num = 10;
}
int main()
{
	int num = 5;
	std::cout << num << std::endl;
	std::cout << SXR::num << std::endl;
}

运行结果为:5 10
同时也可以命名的嵌套

namespace SXR
{
	namespace num1
	{
		int num = 20;
	}
	
	namespace num2
	{
		int num = 30;
	}
}
int main()
{
	int num = 5;
	std::cout << num << std::endl;
	std::cout << SXR::num1::num << std::endl;
	std::cout << SXR::num2::num << std::endl;
}

运行结果为:5 20 30
当然命名空间中还可以包含函数和结构体等。

  • 使用
    分为局部与全局使用
    全局:using namespace std(通常练习中使用)
    局部:std::

2.IO流 input\output stream(初步认识)

  • 定义: C++ IO 流是基于流对象实现的输入输出机制,分为标准 IO(控制台)、文件 IO(磁盘文件)、字符串 IO(内存字符串)三大类,本质是数据在 “源 / 目标” 与程序间的字节流传输。(该处主要与标准IO有关)

  • 包含函数
    1.cin: 即为char input,是IO流中的输入函数。实现数据的输入,既可以单个赋值,也可多个同时赋值。(可以自动识别类型)
    2.cout: 即为char output,是IO流中的输出函数。实现数据的输出,既可以单个输出,也可以多个同时输出。(可以自动识别类型)
    3.endl:即为end line,是IO流中实现换行的函数(机制复杂后面章节解释),相当于\n。

//cin函数
int num_1;
float num_2;
double num_3;
std::cin >> num_1;
std::cin >> num_2 >> num_3;

//cout函数
std::cout << SXR::num1::num << '\n' << num << ' ';
std::cout << SXR::num2::num << std::endl;

在竞赛中可能会需要提高输入输出的效率,那么就可以用这个代码
C++中提高IO流效率方法:

std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);

3.缺省函数

  • 定义: C++ 中的缺省函数(也叫 “默认参数函数”)是指在声明 / 定义函数时,为参数指定默认值,调用函数时若未传递该参数,则自动使用默认值。
  • 类别: 根据参数的完全度分为全缺省与半缺省。全缺省即函数参数全赋值,半缺省则为函数参数部分赋值(关键:必须从左到右连续缺省)
//全缺省
void Fun(int a = 0,int b = 5)
{
	cout << a << " ";
	cout << b<< endl;
}
Fun(1020);
Fun(5);
Fun();

//半缺省
void Sad(int a, int b = 10)
{
	cout << a << " " << b << endl;
}
Sad(4);
Sad(6, 8);

运行结果为:10 20 ;5 20;0 5 ; 4 10 ; 6 8
注意: 声明与定义分离时,只需要声明处给出默认值,否则编译错误

4.重载函数

  • 定义: 重载函数是C++中的核心特性,属于多态的一种(编译时多态 / 静态多态),指:在同一作用域内,定义多个同名函数,但参数列表(参数个数、类型、顺序)不同,编译器会根据调用时传入的实参匹配对应的函数。
  • 形成关键: 1. 参数个数,2. 参数类型 ,3.参数顺序 (无关 1.返回值类型 2.参数名不同 3.const修饰)
//参数个数
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}

//参数类型
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}

//参数顺序
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}

Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);

注意:在构成重载函数时,缺省函数调用会编译报错

void f()
{
  cout << abc << endl;
}

void f(int a = 10)
{
 cout << a << endl;
}

此处会因为 f() 既可以指向上面的函数,又可以指向下面的缺省函数,编译报错。

5.引用(本节重点)

  • 定义: 引用是 C++ 特有的语法特性,本质是变量的 “别名”(Alias) —— 它并非新开辟内存空间,而是对已有变量的内存地址做 “绑定”,操作引用等价于直接操作被绑定的原变量。
    类型& 引⽤别名=引⽤对象;
int num;
int& a = num;

甚至可以给一个变量起多个别名,并且变量与别名的地址一致,修改其中一个值,全部都会改变。

int num = 10;
int& a = num;
int& b = a;
cout << num << " " << a << " " << b <<endl;
a = 15;
cout << num << " " << a << " " << b << endl;
  • 特性:

    1. 定义时必须初始化
    2. 一个变量可以有多个引用
    3. 引用一旦引用一个实体,就不能引用其他实体
  • 使用:
    主要用于引用传参和引用做返回值

这里为引用传参

void Swap(int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
}

int main()
{
int x = 0, y = 1;
cout << x <<" " << y << endl;
Swap(x, y);
cout << x << " " << y << endl;
return 0;
}

可以看到这里实现了通过Swap函数交换下x,y的值。

这里为引用做返回值

int& STTop(ST& rs)
{
	assert(rs.top > 0);
	return rs.a[rs.top];
}
int main()
{
// 调⽤全局的
	ST st1;
	STInit(st1);
	STPush(st1, 1);
	STPush(st1, 2);
	cout << STTop(st1) << endl;
	STTop(st1) += 10;
	cout << STTop(st1) << endl;
	return 0;
}

返回的是变量的别名,在main中接受后,可以直接对其存储的数据进行修改
而在C语言中必须返回的是地址,不然只是修改副本而不能对数据本身进行处理

int* STTop(ST* rs)
 {
    assert(rs->top > 0);
    return &rs->a[rs->top]}

*STTop(&st1) += 10;

这样看来C++的引用确实有助于提高效率

  • const引用:
    const 引用(const 类型&)是 C++ 中引用的重要变种,核心作用是禁止通过引用修改绑定的变量 / 值,同时支持绑定常量、临时值等只读场景,是 C++ 中保证数据只读、提升代码安全性和灵活性的关键语法。
    在此知识点中必须提前了解两个知识点——读写权限和临时对象:
    1. 读写权限
      在C++中,const是经典的只读关键字,本质是”通过该标识符不能改变值“,通过const使得数据分为了两种权限——可读可写与只读,而读写权限的核心规则:可收缩,不可扩张,就有了权限的收缩与扩张。

    2. 临时对象
      C++中有这一种由编译器自动创建的未命名对象:临时对象,它通常用来暂存表达式的求值结果,临时对象具有常性,即是右值,因此只能由const修饰的左值引用
      它的生命周期极短,默认在创建它完整表达式结束时销毁,而const能够延长它的生命周期(到main函数结束)。(完整表达式就是分号前的语句)
      产生场景:表达式计算;类型隐式转换;函数返回值;匿名对象构造
      易错示例:

             1. 类型隐式转换
      
int main() {
    double b = 3.14;
    const int& ref = b; // 类型不匹配,发生整型转换,编译器创建临时int对象(值为3)
    
    cout << ref << endl; // 输出3
    b = 5.67; // 修改原double变量
    cout << ref << endl; // 仍输出3(ref绑定的是临时对象,而非b)
    return 0;
}
               2. 函数返回值
string getStr() 
{
    return "hello"; // 返回临时string对象
}

int main() {
    // 场景1:值接收 → 临时对象赋值后销毁(C++17后优化为“返回值优化RVO”,可能跳过临时对象)
    string s1 = getStr();
    // 场景2:const引用接收 → 延长临时对象生命周期
    const string& s2 = getStr();
    cout << s2 << endl; // 输出hello(临时对象未销毁)
    return 0;
}
   场景2的本质为const延长了临时变量的生命周期,使得引用&能够锁定getStr函数返回的临时对象。

   虽然前提知识复杂庞大,但是在接下来const讲解会很有用。 **^ _ ^**

1. const与引用

int main()
{
const int a = 10;
int& ra = a; 错误
// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &” 
// 这⾥的引⽤是对a访问权限的放⼤ 
// 这样才可以
 const int& ra = a; 正确
 //ra++; 错误
// 编译报错:error C3892: “ra”: 不能给常量赋值 
int b = 20;
const int& rb = b;
// 这⾥的引⽤是对b访问权限的缩⼩ 
// rb++;
// 编译报错:error C3892: “rb”: 不能给常量赋值
return 0;
}

第一例:读写权限放大,编译错误
第二例:ra仅为只读权限,不能进行修改
第三例:rb为只读权限,引用对象b为读写,权限收缩正确

并且const有着核心优势,const 加引用可以绑定变量

int main() {
    // 1. 绑定常量
    const int& ref1 = 5; 
    cout << ref1 << endl; // 输出5

    // 2. 绑定表达式临时值
    int a = 1, b = 2;
    const int& ref2 = a + b;
    cout << ref2 << endl; // 输出3

    // 3. 绑定函数返回的临时对象
    string getStr() { return "hello"; }
    const string& ref3 = getStr();
    cout << ref3 << endl; // 输出hello(临时对象生命周期被延长)

    // 以下均报错:非const引用无法绑定临时值
    // int& ref4 = 5;
    // int& ref5 = a + b;
    return 0;
}

这里来分析函数参数在:
const 类型&、类型&、 类型的区别与优势
首先,const引用类型可以支持函数参数为常量与表达式
其次,const引用类型为只读权限,而后两种能够改变值,但引用改变的是传入的值,最后一种改变的是副本的值。

区分:可见指针与引用是相似的,但两则其实有着本质区别。
• 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间

• 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。

• 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象

• 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。

• sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占个数(32位平台下占4个字节,64位下是8byte)

• 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更全⼀些。

6.内联函数

  • 定义: 内联函数是 C++ 中用于消除函数调用开销的优化手段,本质是编译器将内联函数的代码 “直接嵌入” 到调用处(而非执行传统的函数调用指令),以空间换时间,提升程序执行效率。(在函数前加上inline)
  • 作用: 内联函数可以消除函数调用的开销(如:1. 保存当前函数栈帧 2. 跳转到函数地址执行 3. 执行完毕后返回原地址)
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
// 链接错误:⽆法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z) 
f(10);
return 0;
}
  • 特色: inline是编译器的建议,编译器可以忽略建议,会自行判断是否合适,一般当涉及循环、分支、递归等就会忽略建议
  • 注意: 内联函数需要声明与定义在同一处,避免连接错误
    w

7. nullptr

使用原因:常规的C/C++中NULL实际是一个宏,在stddef.h文件中可以看到定义

#ifndef NULL
   #ifdef __cplusplus
       #define NULL    0
   #else
       #define NULL    ((void *)0)
   #endif
#endif

可见NULL通常被定义为0或(void*)0,这种情况下会出现:重载函数匹配函数错误和赋值给非指针类型(如int),从而错误。

#include <iostream>
using namespace std;

// 重载函数:一个接收int,一个接收指针
void func(int x) { cout << "调用int版本:" << x << endl; }
void func(char* p) { cout << "调用指针版本" << endl; }

int main() {
    func(NULL); // 本意是调用指针版本,但NULL=0,实际调用int版本!
    return 0;
}

C++的nullptr则可以解决问题

  • 定义: nullptr 是 C++11 引入的一个关键字,用于表示空指针。与传统的 NULL 不同,nullptr 是一个类型安全的空指针常量,能够避免类型不匹配的问题

  • 特性:

     		1.类型:nullptr_t,本质为“指针空值类型”,可转为任意指针类型  
     		2.不可转为非指针类型:无法赋值给非指针类型,编译报错
     		3.重载匹配:优先匹配指针类型的重载函数
     		4.可替代使用NULL场景:如 if (p == nullptr) 等价于 if (p == NULL)
    

总结

C++的新的知识提升了开发效率,实现了更加方便的开发与学习,是学习编程中必不可少的语言学习知识,对以后其他语言的学习将有所帮助。本篇的基础知识讲解完毕
,如有错误和补充,请在评论区发言。谢谢

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐