C++ 入门核心知识点(从 C 过渡到 C++ 基础)

一、C++ 第一个程序:兼容 C 与标准 C++ 版本

C++ 完全兼容 C 语言语法,同时提供了更简洁的标准库用法,两个经典 "Hello World" 实现如下:

1. 兼容 C 语言版本

直接使用 C 语言的stdio.h库,写法与 C 完全一致:

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

int main()
{
	printf("Hello World!\n");
	return 0;
}

2. 标准 C++ 版本

使用 C++ 标准输入输出库iostream,通过cout输出(需配合命名空间std):

cpp 复制代码
#include <iostream>
using namespace std; // 展开std命名空间(后续详解)

int main()
{
	cout << "Hello World" << endl; // 流插入运算符<<,endl=换行+刷新缓冲区
	return 0;
}

二、命名空间

C 语言中没有命名空间概念,当多个文件或库定义同名变量 / 函数时,会出现命名冲突。C++ 的namespace正是为解决此问题而生。

1. C 语言的命名冲突问题

cpp 复制代码
#include <stdio.h>
#include <stdlib.h> // 该库中定义了rand()函数

int rand = 10; // 错误:与stdlib.h中的rand函数重定义冲突
int main()
{
	printf("%d\n", rand);
	return 0;
}

编译报错:error C2365: "rand": 重定义;以前的定义是"函数"

2. 命名空间的定义与本质

  • 定义语法namespace 命名空间名 { 变量/函数/类型定义 }
  • 本质:创建一个独立的作用域(域),与全局域相互独立,同名标识符在不同域中可共存
  • 特性
    • 命名空间只能定义在全局域,支持嵌套定义
    • 多文件中同名的命名空间会被合并为一个(不会冲突)
    • 不影响变量生命周期(仅影响访问范围)
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 定义命名空间a,内部可定义变量、函数、结构体
namespace a
{
	int rand = 10; // 与stdlib.h的rand无冲突
	
	int Add(int left, int right) 
	{ 
		return left + right; 
	} 
	
	struct Node 
	{ 
		struct Node* next; 
		int val; 
	};
}

// 嵌套命名空间
namespace a
{
	namespace x
	{
		int rand = 1;
	}
	namespace y
	{
		int rand = 2;
	}
}

int main()
{
	// 访问命名空间中的成员需用域作用限定符::
	printf("%d\n", a::rand);	    // 10(访问a域的rand)
	printf("%d\n", a::x::rand);	// 1(访问a::x域的rand)
	printf("%d\n", a::Add(1, 1));	// 2(访问a域的Add函数)
	
	struct a::Node p1; // 定义a域的Node类型变量
	p1.val = 6;
	printf("%d\n", p1.val); // 6
	return 0;
}

3. 域作用限定符::与命名空间使用方式

  • 域作用限定符 :指定访问的作用域,::变量名访问全局域命名空间名::变量名访问指定命名空间域
  • 命名空间 3 种使用方式
    1. 指定命名空间访问(推荐):s::a
    2. 展开单个成员:using s::b
    3. 展开所有成员(仅小练习使用):using namespace s
cpp 复制代码
int m = 0; // 全局变量m

int main()
{
	int m = 1;
	printf("%d ", m);	// 1(局部m)
	printf("%d ", ::m);	// 0(全局m,显式加::)
	
	namespace s { int a=0, b=1; }
	using s::b; // 展开单个成员
	printf("%d\n", b); // 1
	return 0;
}

注意:C++ 标准库(如coutcin)都放在std命名空间中,日常练习可using namespace std简化代码,实际项目建议用指定命名空间访问(如std::cout)。

三、C++ 输入输出

C++ 提供iostream库,通过cin(输入)和cout(输出)实现 IO 操作,相比 C 语言的scanf/printf更灵活:

  • 无需手动指定格式(如%d%f),自动识别变量类型(本质是函数重载)
  • 支持链式操作(cout << a << bcin >> a >> b
  • endl:等价于\n+ 刷新缓冲区
  • 需通过std命名空间访问
cpp 复制代码
#include <iostream>
using namespace std;

int main()
{
	// 竞赛级IO优化(大量输入输出场景使用)
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	// 输出
	int i = 1;
	double j = 3.14;
	cout << "i = " << i << endl;	   // i = 1
	cout << "j = " << j << endl;	   // j = 3.14

	// 输入
	int a;
	char c;
	cin >> a >> c; // 链式输入
	cout << "a = " << a << ", c = " << c << endl;

	return 0;
}

四、缺省参数(默认参数)

缺省参数是声明或定义函数时为参数指定默认值,调用时若未传实参则使用默认值,否则使用实参。

  • 全缺省参数:所有参数都指定默认值
  • 半缺省参数:部分参数指定默认值(必须从右往左连续缺省,不能跳跃)
  • 声明与定义分离:缺省参数只能在声明中指定
cpp 复制代码
#include <iostream>
using namespace std;

// 全缺省参数
void Func1(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
}

// 半缺省参数(从右往左连续缺省)
void Func2(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
}

int main()
{
	Func1();		  // a=10, b=20, c=30
	Func1(1, 2);	  // a=1, b=2, c=30
	Func2(100);		  // a=100, b=10, c=20
	Func2(100, 200, 300); // a=100, b=200, c=300

	return 0;
}

五、函数重载

C++ 支持同一作用域中定义同名函数,只要形参不同(个数、类型、顺序不同),称为函数重载,实现多态行为。

  • 重载条件:形参不同(个数、类型、顺序),与返回值无关
  • 避免歧义:调用时需让编译器能明确区分同名函数
cpp 复制代码
#include <iostream>
using namespace std;

// 1. 形参类型不同
int Add(int left, int right) { cout << "int Add\n"; return left+right; }
double Add(double left, double right) { cout << "double Add\n"; return left+right; }

// 2. 形参个数不同
void f() { cout << "f()\n"; }
void f(int a) { cout << "f(int a)\n"; }

// 3. 形参顺序不同
void f(int a, char b) { cout << "f(int, char)\n"; }
void f(char b, int a) { cout << "f(char, int)\n"; }

int main()
{
	Add(10, 20);		  // int版本
	Add(10.1, 20.2);	  // double版本
	f(10, 'a');			  // int+char版本
	f('a', 10);			  // char+int版本
	return 0;
}

错误示例:

cpp 复制代码
// 错误1:仅返回值不同,不构成重载
// void f() {}
// int f() { return 0; }

// 错误2:调用歧义
void f1() {}
void f1(int a = 10) {}
// f1(); // 报错:无法区分

六、引用

引用是给已存在变量取的别名,编译器不会为引用开辟内存空间,与原变量共用同一块内存。

1. 定义语法与特性

cpp 复制代码
类型& 引用别名 = 原变量;
  • 必须初始化
  • 一个变量可以有多个引用
  • 一旦绑定某个变量,就不能再绑定其他变量
cpp 复制代码
#include <iostream>
using namespace std;

// 引用传参(替代指针传参)
void Swap(int& rx, int& ry)
{
	int tmp = rx;
	rx = ry;
	ry = tmp;
}

int main()
{
	int a = 0;
	int& b = a; // b是a的别名
	++b;
	cout << a << endl; // 1(a被修改)

	int x = 0, y = 1;
	Swap(x, y); // 直接传变量,无需取地址
	cout << x << " " << y << endl; // 1 0

	return 0;
}

2. const 引用(关键用法)

const 引用用于:传参时不拷贝原变量、不修改原变量,或引用临时对象(临时对象具有常性)。

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

int main()
{
	const int a = 10;
	const int& ra = a; // 正确:const引用引用const变量

	int b = 20;
	const int& rb = b; // 正确:权限缩小(b可修改,rb只读)
	// rb++; // 错误:const引用不可修改

	// 引用临时对象(必须用const引用)
	const int& rc = 30; // 正确
	double d = 12.34;
	const int& rd = d; // 正确(类型转换产生临时对象)

	return 0;
}

3.引用与指针的核心区别

|------|-----------------|------------------|
| 特性 | 引用 | 指针 |
| 内存空间 | 不开辟空间(共用原变量空间) | 开辟空间(存储变量地址) |
| 初始化 | 必须初始化 | 建议初始化(可置空) |
| 指向修改 | 一旦绑定,不能修改指向 | 可随时修改指向 |
| 访问方式 | 直接访问(无需解引用) | 需解引用(*)访问目标变量 |
| 安全性 | 无空引用 / 野引用(更安全) | 存在空指针 / 野指针(风险高) |

七、inline:内联函数(替代 C 语言宏函数)

inline修饰的函数叫做内联函数,编译时编译器会在调用处展开函数体,避免函数调用的栈帧开销,提高效率。

  • inline是编译器的建议,长函数、递归函数会被编译器忽略
  • 替代 C 语言宏函数(宏函数易出错、不便调试)
  • 不建议声明和定义分离(会导致链接错误)
cpp 复制代码
#include <iostream>
using namespace std;

// 内联函数(短函数,频繁调用场景)
inline int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int ret = Add(1, 2); // 编译时展开为:int ret = 1 + 2;
	cout << ret << endl; // 3

	return 0;
}

与 C 语言宏函数的对比:C 语言宏函数需注意优先级和分号问题,而 inline 函数更安全。

cpp 复制代码
// C语言宏函数(需加多层括号,避免优先级问题)
#define ADD(a,b) ((a)+(b))
// C++ inline函数(无需担心优先级,支持调试)
inline int Add(int a, int b) { return a + b; }

八、nullptr

nullptrC++11 引入的关键字 ,用于替代 C 语言中的NULL,解决了NULL在类型转换和函数重载中的歧义问题。

1. C 语言中 NULL 的缺陷

C 语言中NULL是宏定义,C++ 中常定义为0,导致函数重载歧义:

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

void f(int x) { cout << "f(int x)\n"; }
void f(int* ptr) { cout << "f(int* ptr)\n"; }

int main()
{
	f(NULL); // 输出:f(int x)(歧义!本意是调用指针版本)
	return 0;
}

2. nullptr 的核心特性与使用

  • 类型为std::nullptr_t,可隐式转换为任意指针类型,但不能转换为整数类型
  • 精准匹配指针类型的重载函数,无歧义
cpp 复制代码
#include <iostream>
using namespace std;

void f(int x) { cout << "f(int x)\n"; }
void f(int* ptr) { cout << "f(int* ptr)\n"; }

int main()
{
	f(nullptr); // 输出:f(int* ptr)(精准匹配)

	// 类型安全:不能转换为整数
	// int x = nullptr; // 编译报错

	// 可隐式转换为任意指针类型
	int* p1 = nullptr;
	char* p2 = nullptr;

	return 0;
}

3. NULL 与 nullptr 对比

|--------|---------------------|-------------------------|
| 特性 | NULL | nullptr |
| 本质 | 宏定义(可能为 0 或 void*) | 关键字(类型为 std::nullptr_t) |
| 函数重载匹配 | 可能匹配 int 版本(歧义) | 精准匹配指针版本(无歧义) |
| 类型转换 | 可转换为 int 或指针 | 仅可转换为指针(类型安全) |

如果有疑问或补充,欢迎在评论区交流~

相关推荐
7***374540 分钟前
Java设计模式之工厂
java·开发语言·设计模式
jiushun_suanli1 小时前
量子纠缠:颠覆认知的宇宙密码
经验分享·学习·量子计算
上不如老下不如小1 小时前
2025年第七届全国高校计算机能力挑战赛初赛 Python组 编程题汇总
开发语言·python·算法
程序员小白条1 小时前
你面试时吹过最大的牛是什么?
java·开发语言·数据库·阿里云·面试·职场和发展·毕设
xlq223221 小时前
19.模版进阶(上)
c++
yuuki2332331 小时前
【C++】初识C++基础
c语言·c++·后端
小年糕是糕手1 小时前
【C++】类和对象(二) -- 构造函数、析构函数
java·c语言·开发语言·数据结构·c++·算法·leetcode
豐儀麟阁贵1 小时前
8.2异常的抛出与捕捉
java·开发语言·python
权泽谦1 小时前
PHP 版羊了个羊完整开发实战:逻辑解析 + 三消算法 + 全套接口(附源码)
开发语言·php