C++入门语法
你好,这里是新人 Sunfor
这篇是我最近对于C++入门语法的学习心得和错题整理
有任何错误欢迎指正,欢迎交流!
会持续更新,希望对你有所帮助,我们一起学习,一起进步
📌前言
“真正优秀的C++程序员,往往能把基础语法玩出花来”
本文将带大家深入C++核心语法细节,这些知识不仅是面试的高频考点,更是项目实践中提升代码质量的利器
一、命名空间(namespace) ----你的代码领域宣言
1. 为什么需要命名空间?
- “命名空间是代码世界的国界线,避免符号战争的终极方案”
解决全局作用域下的标识符污染问题
经常运用于解决 多人协作开发 时的符号冲突
2.命名空间的定义
- 通过 namespace 关键字声明独立作用域,不同的域可以定义同名变量
C++中的域有函数局部域,全局域,命名空间域,类域
域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑,所以有了
域的隔离,名字冲突就解决了
注意: 局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期
命名空间域和类域不会影响变量生命周期
- namespace只能定义在全局,当然也可以嵌套定义
- 项目工程中多文件定义的同名namespace会认为是一个namespace,不会冲突
- C++标准库都放在一个std(standard)的命名空间中
3.使用示范
//your first C++ program
//c语言版本
#include<stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
//c++版本
#include<iostream>
using namespace std;
int main()
{
cout << "Hello World!\n" << endl;
return 0;
}
//命名空间的嵌套
namespace yw
{
namespace sun
{
int rand = 1;
int Add(int left, int right)
{
return left + right;
}
}
namespace flower
{
int rand = 2;
int Add(int left, int right)
{
return (left + right) * 10;
}
}
}
int main()
{
printf("%d\n", yw::sun::rand);
printf("%d\n", yw::flower::rand);
printf("%d\n", yw::sun::Add(1, 2));
printf("%d\n", yw::flower::Add(1, 2));
return 0;
}
//全局域vs局部域
int x = 0;
namespace yw
{
int x = 1;
}
void func()
{
int x = 2;//局部域的只能在局部访问
}
int main()
{
int x = 3;
printf("%d\n", x);//访问自身的 默认从局部到全局 不会去命名空间找
printf("%d\n", yw::x);//访问命名空间的 主要为了与全局隔离
printf("%d\n",::x);//访问全局的
}
💡 行业规范
大型项目必须使用命名空间,禁止在头文件中使用 using namespace
二、缺省参数 ----智能的函数参数设计
1.为什么要有缺省参数?
“让函数接口具备弹性,向后兼容的巧妙设计”
简化函数调用复杂度,支持接口升级时不破坏旧代码
2.缺省参数的定义
缺省参数是声明和定义函数时为函数的参数指定一个缺省值,在调用该函数时,如果没有指定实参则采用该形式的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省
全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值,半缺省参数必须从右往左连续缺省,不能间隔跳跃给缺省值注意:C++规定必须从左到右依次给实参,不能跳跃给实参
函数声明和定义分离时,缺省参数不能在函数声明和定义同时出现,规定必须函数声明给缺省值
3.缺省参数的使用
void Func(int a = 10)
{
cout << a << endl;
}
int main()
{
Func();
Func(1);
return 0;
}
//全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
//半缺省(必须从右往左给值)
void Func2(int a, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
int main()
{
Func1();
Func1(1);
Func1(1, 2);
Func1(1, 2, 3);
//Func1(,2,)不允许越级传,只能从左到右
Func2(100);//至少给一个参数
Func2(100, 200);
Func2(100, 200, 300);
}
//不知道用缺省 知道就显示传递 就与缺省值无关就不会开辟额外的空间
//缺省参数不能在声明和定义处同时出现 在声明地方给 定义不能给
三、函数重载 ----优雅的多面手
1.为什么要有函数重载?
“函数重载是C++给程序员的语义画笔,让相同操作的不同形态拥有统一的名字”
允许同名函数根据参数列表差异执行不同逻辑,提升代码可读性,实现自然语义化编程
2.函数重载的定义
函数名相同,参数列表必须满足以下差异之一
1.参数类型不同
2.参数个数不同
3.参数顺序不同不构成重载的情况:仅返回值不同‘
3.函数重载的使用
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()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
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;
}
//参数不同可以构成重载 返回值不同不能区分
int main()
{
Add(1, 2);
Add(1.1, 2.2);
f();
f(10);
f(19, 'a');
f('a', 19);
return 0;
}
四、引用 ----指针的安全马甲
1.为什么要有引用?
“安全的指针替身,函数式编程的基石”
提供变量别名,避免指针的复杂操作,实现高效安全的数据传递
2.引用的定义
引用不是定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和引用的变量共用同一块内存空间
类型& 引用别名 = 引用对象
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,再不能引用其他实体
3.引用的使用
// 1. 函数参数传递(避免拷贝)
void ProcessBigData(const BigClass& obj);
// 2. 函数返回值(延长生命周期)
const string& GetName() {
static string name = "test";
return name; // 必须返回静态或全局变量
}
// 3. 范围for循环
for (auto& num : nums) {
num *= 2; // 直接修改容器元素
}
注意
引用和指针在实践中相辅相成,功能具有重叠性,但各有特点,互相不可替代
C++的引用跟其他语言的引用有很大区别,引用定义后不改变指向
4.const引用
可以引用const对象,但必须使用const引用。同时,const引用也能引用普通对象,因为在引用过程中可以缩小对象的访问权限,但无法扩大权限。
举个例子
int main()
{
const int a = 10;
//权限不能放大
//int& ra = a;
const int& ra = a;
//权限可以缩小
int b = 1;
const int& rb = b;
return 0;
}
五、inline函数 ----性能优化的秘密武器
1.为什么要有inline函数?
”性能于可维护性的平衡艺术“
消除函数调用开销,替代宏函数的同时保持类型安全
替代C的宏定义
2.inline函数的定义
用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就需要建立栈帧,就可以提高效率
inline关键字对编译器而言仅是一种建议,这意味着即使你为函数添加了inline,编译器也可能选择不在调用处展开。由于C++标准并未对此作出明确规定,不同编译器在何种情况下展开inline函数的行为各有差异。一般来说,inline适用于频繁调用且代码较短小的函数,而对于递归函数或代码较为复杂的函数,即使标注了inline,编译器通常也会忽略该建议。
inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地 址,链接时会出现报错。
3.inline函数的使用
//正确的宏的写法
#define ADD(a,b)((a) + (b))
//不能加; 本质是替换
//要加外面的括号 要考虑符号的优先级
//& | 这种情况也需要考虑运算优先级
//宏函数缺点很多,但是替换机制不用建立栈帧
int main()
{
int ret = ADD(1, 2);
cout << ret << endl;
cout << ADD(1, 2) << endl;
return 0;
}
inline int Add(int x, int y)
{
int ret = x + y;
return x + y;
}
int main()
{
int x = 1, y = 2;
int re = Add(x & y, x | y);
cout << re << endl;
return 0;
}
六、nullptr ----C++11的类型安全革命
1.为什么要有nullptr?
”类型安全的空指针革命“
解决C语言NULL的二义问题(0 vs (void*)0),明确指针空值语义
2.nullptr的定义
nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换 成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被 隐式地转换为指针类型,⽽不能被转换为整数类型
void func(int) { cout << "int version" << endl; }
void func(int*) { cout << "pointer version" << endl; }
int main() {
func(NULL); // 可能调用int版本!
func(nullptr); // 明确调用指针版本
}
3.nullpter的使用
#include<iostream>
using namespace std;
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
int main()
{
f(0);
// 本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),
// 因此与程序的初衷相悖
f(NULL);
f((int*)NULL);
// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型
// f((void*)NULL);
f(nullptr);
return 0;
}
💡 避坑指南
- 避免跨作用域的缺省参数
- 不要返回局部对象的引用
- inline函数不宜超过10行
- 优先使用const引用传递大型对象
结语
真正掌握这些语法特性,需要在实际项目中反复锤炼。建议读者将本文代码示例手敲三遍,体会每个语法细节的设计哲学。下期我们将深入探讨C++面向对象编程的精髓,敬请期待!
👉 互动环节:大家在项目中遇到过哪些因基础语法使用不当引发的Bug?欢迎评论区分享讨论!
碎碎念time
消失了快3个月啦,实在对关注我的朋友们感到抱歉
这三个月里确实没有系统性的学习编程知识,也确实发现生活没有编程真的很无趣无味
不知道大家是否也会向我一样间歇性摆烂,在清醒中沉沦呢?
其实我们所有的道理都明白,但是到我们自己需要做的时候就不像脑海中幻想的那么简单
学习的路上更是最明显的体现,我们知道一切收获都需要付出相应的时间与精力
但每当遇到瓶颈和阻拦的时候,就很难坚持下去
多的也不多说,希望大家都能保持初心,久久做功!