【C++ 笔记】从 C 到 C++:核心过渡 (上)

【C++ 笔记】从 C 到 C++:核心过渡 (上)

前言:

        C++ 是一门高效、灵活且功能强大的通用编程语言,由 Bjarne Stroustrup 于 1979 年在贝尔实验室开发。

        它通常被视为 C 语言的延伸,在 C 语言的基础上增加了面向对象编程(OOP)和泛型编程的支持,同时C++ 是一门 “难学但上限极高”的语言,如果追求极致的程序运行效率,或者需要深入理解计算机底层运作原理,C++ 是必修课。

        

        

一、C与C++程序

        

C语言输出Hello World:   

include <stdio.h> int main() { printf("Hello World\n"); return 0; }

        

C++输出Hello World:

#include<iostream> using namespace std; int main() { cout<<"Hello World"<<endl; return 0; }

        

我们发现:C语言和C++虽然同宗同源,但即使是输出简单的 "Hello World",其背后的设计理念也有很大不同。

        

①C语言偏向“面向过程”,注重直接的函数调用;

        

②C++偏向“面向对象”,引入了流(Stream)和命名空间的概念。      
  

        

所以,想吃透 C++ 的第一个程序,我们需要先掌握必要的前置知识。

             

二、命名空间

        

 对于初学C++的帅观众,肯定会疑惑这句代码是什么意思,在C语言中从来没有看见过。       

using namespace std;

                

其实,命名空间(Namespace) 是 C++ 为了解决 C 语言中“命名冲突”这一痛点而引入的关键特性。

        

因为在C/C++中,变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全局作⽤域中,可能会导致很多冲突。
     

         

而使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

        

2.1 为什么会出现命名冲突

        

例如:在头文件<stdlib.h>中,包含一个名为rand的函数,其原型如下:

rand函数原型:

        

        int  rand  (void);

        

函数名:rand

        

返回值:int

        

参数:void,不需要传参。

        

功能:返回一个伪随机整数

        

#include <stdio.h> #include <stdlib.h> int rand = 10; int main() { // 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数” printf("%d\n", rand); return 0; }

        

编译报错原因:

        

在不知道该函数的前提下,当你编写一个程序时,定义了一个名字也为 “rand” 的变量,此时命名冲突就发生了,编译器会认为rand进行了重定义。

        

因为之前rand是一个函数,而现在变成了一个整形变量,这样就导致了因为命名冲突而出现报错。

        

C++祖师爷就针对这样的问题,引出了命名空间

        

2.2 namespace的简单定义

        

        定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对 { } 即可, { } 中即为命名空间的成员。

        命名空间中可以定义变量/函数/类型等。

           

代码示例:在命名空间mount中定义变量、函数、自定义类型。    

 //namespace --关键字 mount:为改命名空间的名字 namespace mount { //命名空间中可以定义变量 int rand = 10; //可以定义函数 int Add(int left, int right) { return left + right; } //可以定义结构体变量 struct Node { struct Node* next; int val; }; }

        

2.3 命名空间解决命名冲突

        

回顾冲突的原因: 同一个域里,不能有两个东西都叫 rand

        

在没有命名空间之前,整个代码就像在一个大广场(全局域/Global Scope)上。

当你包含了头文件 <stdlib.h><cstdlib> 时,标准库就把一个叫 rand 的函数放到了这个大广场上。

如果你接着在广场上又定义了一个变量叫 rand

#include <stdlib.h> // 引入了系统自带的 rand 函数 int rand = 10; // 报错!冲突了! int main() { // 编译器困惑:你这里的 rand 到底是指那个函数,还是这个整数? // Error: 'rand' redeclared as different kind of symbol } 

                

命名空间解决:编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。

        

命名空间(Namespace) 的本质就是在广场上盖了一个私有的房间(或者说围了一个院子)。

当你定义namespace mount时,你创造了一个独立的空间。

#include <stdio.h> #include <stdlib.h> // 系统 rand 函数 依然在“广场”上 // 我们建了一个叫 mount 的房间,把我们自己定义的 rand 变量关在里面 namespace mount { int rand = 10; } int main() { // 此时完全没有冲突: // 1. 系统 rand 函数,它在外面(全局域) // 2. 我们的 rand 变量,它在 mount 房间里(mount 域) return 0; }

        

2.4 访问命名空间

        

编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找,所以下⾯程序会编译报错。      

#include<stdio.h> namespace mount { int a = 0; int b = 1; } int main() { // 编译报错:error C2065: “a”: 未声明的标识符 printf("%d\n", a); return 0; } 

        

如果我们要使⽤命名空间中定义的 变量 / 函数,有三种⽅式:

        

① 指定命名空间访问,大型项⽬中推荐这种⽅式。



② using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种⽅式。

        

③  展开命名空间中全部成员,项⽬不推荐,冲突⻛险很⼤,⽇常⼩练习程序为了⽅便推荐使⽤。

                 

“   : :  ”   为域作用限定符,左边无命名空间名时,默认为全局

        

1. 当域作用限定符左边有命名空间名时,在该命名空间中寻找并调用

        

2. 当域作用限定符左边没有有命名空间名时,默认在全局中寻找并调用

        

2.4.1 指定命名空间访问

        

 代码示例:

#include<stdio.h> namespace mount { int a = 10; void f() { printf("func N\n"); } int add(int a,int b) { return a+b; } //可以定义结构体变量 struct Node { struct Node* next; int val; }; } int c=10; // 指定命名空间访问 int main() { //调用命名空间中的变量 printf("%d\n", mount::a); //调用命名空间中的无参函数 mount::f(); //调用命名空间中的有参函数 mount::add(1,2); //声明命名空间中的结构体变量 struct mount::Node n1; //调用全局域的变量 printf("%d\n",::c); return 0; }

        

        

2.4.2 展开命名空间中某个成员

        

#include<stdio.h> namespace mount { int a = 10; void f() { printf("func N\n"); } int add(int a,int b) { return a+b; } //可以定义结构体变量 struct Node { struct Node* next; int val; }; } using mount::a; using mount::f(); //展开命名空间中某个成员 int main() { //展开后无需使用域限定符进行访问 printf("%d",a); f(); return 0; }

        

        

2.4.3 展开命名空间中全部成员

        

#include<stdio.h> namespace mount { int a = 10; void f() { printf("func N\n"); } int add(int a,int b) { return a+b; } //可以定义结构体变量 struct Node { struct Node* next; int val; }; } using namespace mount; //展开命名空间中全体成员 int main() { //展开命名空间中全体成员后 //所有成员都无需使用域空间名进行访问 printf("%d",a); f(); add(1,2); struct Node n1; return 0; }

       

这样我们也就理解了为什么出现了 using namespace std; 

        

因为C++标准库都放在⼀个叫std(standard)的命名空间中,我们通过将其展开,无需通过加上前缀std::,就可以便捷地使用。

         

2.5namespace的嵌套定义

        

命名空间的嵌套就像电脑文件夹里的“子文件夹”。

        

当项目变得非常巨大时,仅仅一层命名空间可能不够用了。

        

比如:一个大型游戏引擎,可能所有的代码都在 Game 命名空间下,但里面又分“图形”、“音频”、“网络”等模块。

        

namespace Game { // Game 下的 Graphics 模块 namespace Graphics { void render() { printf("正在渲染画面...\n"); } } // Game 下的 Audio 模块 namespace Audio { void playSound() { printf("正在播放声音...\n"); } } }

            

2.6多文件下的命名空间

        

        多⽂件中可以定义同名namespace,他们会默认合并到⼀起,就像同⼀个namespace⼀样

        
代码实例演示:假设你在做一个电商系统,所有代码都归属于 Shop 命名空间,但你肯定不会把所有代码写在一个文件里。

        

文件 1: Cart.h (购物车模块)

// 第一次定义 Shop,编译器创建一个新域 namespace Shop { void add() { /*...*/ } }

        

文件 2: Order.h (订单模块)

// 再次遇到 Shop,编译器识别出已有该域,于是将 Order 加入其中 namespace Shop { void pay() { /*...*/ } }

        

文件 3: main.cpp (主程序)

#include "Cart.h" #include "Order.h" int main() { // 此时,在 main 函数看来,add函数 和 play函数 都在同一个 Shop 命名空间里 Shop::add(); Shop::play(); return 0; }

        

需要注意的“坑”: 虽然同一个命名空间可以分布在多文件中,但是同一个变量/函数不能重复定义(除非是声明)。

        

错误示范:命名空间合并了,意味着 x 都在同一个屋檐下了,在一个屋檐下,不能有两个重名的 x

        

FileA.cpp:

namespace A { int x = 10; // 定义 x }

        

FileB.cpp:

namespace A { int x = 20; // 错误!x 重定义了 }

        

        

三、输入与输出

        

在C++程序中,我们通常包含<iostream>头文件,是 Input Output Stream(输入输出流)的缩写。

        

它是 C++ 标准库中的核心头文件 —— 输入输出流库,专门定义了cout(输出)、cin(输入)等标准输入、输出对象,是实现控制台输入输出的基础。

        

C++ 的输入输出不再使用函数(如 scanf/printf),而是使用流对象配合运算符。

        

对象/符号所属类作用备注
std::cinistream标准输入(主要面向窄字符 char配合 >> 使用
std::coutostream标准输出(主要面向窄字符 char配合 << 使用
std::endl(函数)换行 + 刷新缓冲区比单纯的 \n 多了一个刷新缓冲区的动作
>>-流提取运算符 (Input)数据从流“流向”变量
<<-流插入运算符 (Output)数据从变量“流向”屏幕

        

3.1 C++中的标准输入

        

        C++的输入不再使用函数如scanf,而是通过定义在std命名空间中std::cin进行输入

        

代码示例:C++中的输入需要配合流插入运算符符号 “>>” 进行使用

#include<iostream> int main() { int a; std::cin>>a; return 0; }

      

代码示例:展开命名空间std标准库使用cin

#include<iostream> using namespace std; int main() { int a; cin>>a; return 0; }

           

3.2 C++中的标准输出

        

         C++的输出不再使用函数如printf,而是通过定义在std命名空间中std::cout进行输出

        

代码示例:C++中的输出需要配合流提取符号 “<<” 进行使用

#include<iostream> int main() { int a=10; std::cout<<a; return 0; }

        

代码示例:展开命名空间std标准库使用cout

#include<iostream> using namespace std; int main() { int a=10; cout<<10; return 0; }

        

3.3 C++中的换行符

        

std::endl 本质是函数,它的核心区别于单纯的换行符 \n。

前者既实现换行,又会刷新缓冲区;而后者仅完成换行,无刷新缓冲区的动作。

        

代码示例:配合流提取运算符“<<”使用换行符

#include<iostream> using namespace std; int main() { int a=10; cout<<10<<endl; return 0; }

        

3.4 C++的IO优势

        

相比于 C 语言的 printf / scanf,C++ 的方式更加“智能”:

        

①自动类型识别:不需要像 C 语言那样手动指定格式控制符(如 %d, %c, %f)。

        

原理:本质是利用了 函数重载(后续会深入讲解)。


        

②支持自定义类型:这是最强大的地方,未来学完类和对象后,你可以让 cin/cout 直接输出你自定义的结构体或类。

        

3.5 C++的IO注意事项

        

  • 命名空间:
    • 所有标准库内容都在 namespace std 中。
    • 练习时:可以直接 using namespace std; 图方便。
    • 项目中:不建议直接展开,应使用 std::coutusing std::cout; 以免污染命名空间。

        

  • 关于 C 语言混用:
    • C++ 代码中依然可以使用 printf/scanf
    • 头文件陷阱:虽然 VS 编译器在包含 <iostream> 时可能会间接包含 <stdio.h>(让你能直接用 printf),但这不符合标准。
    • 为了跨平台兼容,如果用 printf,最好显式包含 <stdio.h>

        

既然看到这里了,不妨关注+点赞+收藏,感谢大家,若有问题请指正。

                                                                     

Read more

卷积神经网络(CNN)进阶:经典架构解析与实战开发

卷积神经网络(CNN)进阶:经典架构解析与实战开发

卷积神经网络(CNN)进阶:经典架构解析与实战开发 💡 学习目标:掌握CNN的经典进阶架构设计思路,理解不同架构的核心创新点,能够基于经典架构开发定制化图像任务模型。 💡 学习重点:LeNet-5、AlexNet、VGGNet、ResNet的核心结构与改进逻辑,基于PyTorch实现ResNet-50并完成图像分类任务。 49.1 卷积神经网络进阶的核心驱动力 卷积神经网络从最初的简单结构发展到深度模型,核心驱动力是解决深层网络的性能瓶颈和提升特征提取的效率与精度。 在早期CNN的应用中,研究人员发现两个关键问题: 1. 网络深度增加到一定程度后,会出现梯度消失或梯度爆炸问题,导致模型无法收敛。 2. 简单堆叠卷积层的方式,会造成特征冗余和计算资源浪费,模型泛化能力受限。 ⚠️ 注意:CNN的进阶过程不是单纯的“堆层数”,而是通过结构创新、参数优化和训练技巧的结合,实现性能的突破。 ✅ 结论:经典CNN架构的每一次升级,都针对当时的技术痛点提出了创新性解决方案,掌握这些方案的设计思路,比记住网络结构更重要。 49.2 经典CNN架构深度解析 49.2.1

By Ne0inhk
2G 内存云服务器部署 Spring Boot + MySQL 实战:从踩坑到上线

2G 内存云服务器部署 Spring Boot + MySQL 实战:从踩坑到上线

2G 内存云服务器部署 Spring Boot + MySQL 实战:从踩坑到上线 前言 最近把自己的全栈博客项目部署到了腾讯云的入门级服务器(2核2G),过程中踩了不少坑。本文记录完整的部署过程和问题排查思路,希望对同样在小规格服务器上部署 Java 项目的同学有所帮助。 项目技术栈: * 后端:Java 17 + Spring Boot 3.2.3 + Spring Security + JPA * 数据库:MySQL 8.0 * 前端:Flutter Web * 反向代理:Nginx 1.26 * 容器:Docker 28.4 服务器配置: * 腾讯云轻量应用服务器 * 2 核 CPU / 2GB 内存 / 50GB

By Ne0inhk
SkyWalking - Spring Cloud Alibaba 全链路追踪实战

SkyWalking - Spring Cloud Alibaba 全链路追踪实战

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕SkyWalking这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * SkyWalking - Spring Cloud Alibaba 全链路追踪实战 🚀 * 1. 环境准备与核心概念 🧰 * 1.1 核心概念解析 * 1.2 环境准备 * 2. 构建 Spring Cloud Alibaba 微服务项目 🏗️ * 2.1 创建父工程 * 2.2 构建 `inventory-service`(库存服务) * 2.3 构建 `order-service`(订单服务) * 2.4 验证基础功能 * 3. 集成

By Ne0inhk
企业级web影院订票系统管理系统源码|SpringBoot+Vue+MyBatis架构+MySQL数据库【完整版】

企业级web影院订票系统管理系统源码|SpringBoot+Vue+MyBatis架构+MySQL数据库【完整版】

摘要 随着互联网技术的快速发展和数字化生活的普及,影院行业正面临着从传统线下售票向线上智能化转型的需求。观众对便捷、高效的购票体验要求日益提升,而影院管理方也需要一套功能完善、操作简便的系统来优化票务管理、排片安排和数据分析。基于此背景,开发一款企业级Web影院订票系统具有重要的现实意义。该系统能够整合线上线下资源,实现用户自助选座购票、影院动态排片、数据统计分析等功能,为影院运营提供全方位支持。关键词:影院订票系统、数字化转型、线上购票、智能化管理、企业级应用。 本系统采用SpringBoot+Vue+MyBatis架构,结合MySQL数据库,实现了前后端分离的高效开发模式。SpringBoot框架简化了后端服务的搭建,提供了稳定的RESTful API接口;Vue.js框架构建了响应式前端界面,提升了用户体验;MyBatis作为ORM工具,优化了数据库操作效率。系统功能涵盖用户注册登录、影片信息管理、场次排期、在线选座购票、订单支付、数据统计等模块,支持多角色权限管理,满足影院管理员和普通用户的不同需求。关键词:SpringBoot、Vue.js、MyBatis、MySQL、权

By Ne0inhk