命名空间
名字空间本质上是自定义作用域,由于C++设计的初衷是开发大规模软件,大量的软件库必然会加剧全局符号(变量、函数)的冲突,因此名字空间最基本的作用就是给不同的库和模块拥有自己的独特的作用域,处于不同名字空间中的重名符号相安无事,互不冲突,以此来大大提高编程的便利性。
示例代码:
// 定义一个名字空间,叫ns
namespace ns
{
// 在 ns 中定义变量
int a = 1;
// 在 ns 中定义函数
float f(int x)
{
return x/2;
}}在头文件中使用extern关键字进行变量和函数的声明
namespace ns
{
// 对名字空间 ns 中的符号进行声明
extern int a;
extern float f(int x); }在需要调用的cpp文件中用ns::a或者ns::f(8)来进行变量或者函数的调用。
// main.cpp
#include "ns.h"
//using namespace ns; 也可以一次性全部导入,不推荐此做法。
//using ns::a; 可以这样引入
int main()
{
// 调用名字空间中的符号,使用全称
cout << ns::a << endl;
cout << ns::f(8) << endl;这个双冒号的操作符,称为作用域引用符(成员引用符 – 类对象),很显然,双冒号前面必须是一个作用域。
由于在上述例子中,名字空间 ns 仅仅包含极少量符号,因此不管采用哪种形式的 using 语句都没有什么区别,但如果某个名字空间包含大量符号(比如标准名字空间std),而程序中仅需用到其中的少量符号,那么导入整个名字空间的所有符号的做法也许是不明智的,因为这会使得大量未被使用的
符号成为潜在的符号冲突候选人,这种情形被称为名字空间污染,因此,实际编码中我们应在追求便利的同时,尽量避免引入不使用的符号。
C++允许嵌套定义名字空间,即一个名字空间内部再出现另一个名字空间,这其实是作用域的常规特性,早在C语言时代就可以有嵌套的作用域的概念,只不过C语言中的作用域都是匿名的,而C++给这些作用域赋予了特定的名字。
当程序在多处定义了相同的名字空间时,它们将会融合成一个统一的作用域。
全局作用域是从C语言就开始有的一种作用域,在C++中,有时为了强调某符号的全局特性,或为
了避免与导入的名字空间中的重名符号冲突,会在使用全局符号的时候加上
作用域解析符
注意:全局作用域的名字空间是匿名的,引用全局作用域符号只需加 :: 即可。
名字空间的本质就是作用域,遵守C语言关于作用域的基本原则,如内层作用域重名符号会掩盖外层作用域的重名符号。
Lambda表达式
Lambda函数,亦称为Lambda表达式、匿名函数等,是一种函数对象,Lambda函数可以让函数像普通变量一样进行赋值、传递、函数返回等操作。C++中的Lambda函数经常用来解决如下问题
使得程序更加简洁,尤其对于一次性使用的函数。
有些库函数要求传递的参数是一个函数,但是你又不想写一个函数出来使得函数可以自由流动,就像变量一样,这给函数式编程模式奠定了语法基础。
使得函数可以自由流动,就像变量一样,这给函数式编程模式奠定了语法基础。
语义规定:
1 [capture list](params list)mutable exception → return type{functionbody}
捕获列表是 Lambda 和普通函数最大的不同点。它允许你在 Lambda 内部访问外部函数的局部变量:
[](不捕获):不能使用任何外部局部变量。[=](按值捕获所有):把外部所有局部变量拷贝一份进 Lambda 内部使用(只读副本)。[&](按引用捕获所有):以引用方式使用外部所有变量(内部修改会直接影响外部!)。[a, &b](混合捕获):变量a按值拷贝,变量b按引用传递。
#include <iostream>
int main() {
int factor = 2;
int sum = 0;
// 按值捕获 factor,按引用捕获 sum
auto multiply = [factor, &sum](int x) {
sum += x * factor; // 可以修改 sum,但不能修改 factor
};
multiply(5);
std::cout << "sum = " << sum << std::endl; // 输出 10
}结合我们前面聊过的“类和面向对象”,Lambda 表达式在底层其实一点都不神秘。
当你在代码里写下一个 Lambda 时,编译器在后台默默帮你做成了这样:
- 偷偷为你生成了一个匿名的
struct / class。 - 捕获列表里的变量,变成了这个类的 成员变量。
- 把你的
{ 函数体 }自动重载成了这个类的operator()运算符(即仿函数)。
所以说,Lambda 只是 C++ 官方提供给你的又一个语法糖,本质依然是类和对象!
注意:没有捕获参数的lambda可以存入函数指针变量(函数指针),有的不能存入!
一般来讲直接用auto存放
int factor = 2;
// 用 auto 自动识别类型,管你有没有捕获都能存!
auto multiply = [factor](int x) { return x * factor; };
std::cout << multiply(5) << std::endl; // 输出 10说白话,这玩意就是个临时函数,如果目标函数写死了就可以把这个传进去执行表达式里面的逻辑,例如:
#include <iostream>
#include <vector>
#include <functional>
// 1. 【写死/固定框架的函数】:只负责遍历,怎么过滤交给 condition
void filterAndPrint(const std::vector<int>& numbers, std::function<bool(int)> condition) {
for (int num : numbers) {
if (condition(num)) { // 💡 传进来的 Lambda 在这里被执行!
std::cout << num << " ";
}
}
std::cout << std::endl;
}
int main() {
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 2. 想要打印偶数?直接临时传个判断偶数的 Lambda 进去:
std::cout << "偶数: ";
filterAndPrint(data, [](int x) { return x % 2 == 0; });
// 3. 突然又想打印大于 5 的奇数?不用改原函数,直接换个临时 Lambda:
std::cout << "大于 5 的奇数: ";
filterAndPrint(data, [](int x) { return x > 5 && x % 2 != 0; });
}类
类(class)是C++有别于C语言的最重要的概念之一,正是因为有了类的概念,C++才拥有了所谓面向对象(OOP,即Object OriginalProgramming)的编程模式,那么类究竟是什么呢?单从语法层面讲,类其实就是增加了函数的结构体。而从逻辑层面讲,类是增加了数据行为的结构体。类跟结构体一样,先要自定义类类型,然后再使用类类型去定义变量(对象)。以下是示例代码:
class A //class换成struct就是结构体
{ //不同?struct定义的类属性和方法都默认为pubilc,都 //可以被外部访问。class需要public修饰才能被外部访问
public: // 成员限定符 【公有的 、 公开的】
int x;
float y;
// 类方法
void showInfo()
{
cout << "x:" << x << endl;
cout << "y:" << y << endl;
}};
int main()
{
A test; //实例化一个对象 test;
test.x = 1; //通过.访问类中的属性
test.y = 2;
test.showInfo()//同理调用类中的方法
return 0;
};在上述代码中,test 本质上就是一个类类型的变量,但在类的语境中,我们一般称类变量为对象(Object),称对象中的函数接口为类方法:
对象:类变量
类方法:对象中的函数接口
将类变量称为对象的原因,是因为它们体现的是对现实世界的抽象,现实世界中的一个对象不仅仅是数据,还包括了对象本身的行为属性。比如下面这些都可以被设计为典型的对象实例:
小猫:不仅包含毛色、体重等数据,还有进食、睡觉、卖萌等行为。
文件:不仅包含类型、权限等数据,还有被打开、关闭、读写等行为。
进度条:不仅包含尺寸、百分比等数据,还会有进度的变化、弹窗等行为。
类方法
所谓类方法,就是类里面的函数接口,这是与结构体有重大区别的地方之一。类方法实际上表达的是一个对象的行为属性,比如小猫类 Kitty 应该要提供如下类方法,来表达一只小猫的正常的行为:
class Kitty
{
public:
void eat(){/*进食*/}
void sleep(){/*睡觉*/}
void actCute(){/*卖萌*/}7 };类成员权限限定符
我们可以用3种限定符来修饰类里面的属性或方法
public:任何地方都可以访问
protected:只能在本类或者后代类中访问
private:只能在本类访问
如果没有加任何限定符,默认private;
一般来讲,类的属性都是private或protected的,仅能通过外部调用方法或构造函数初始化方式进行修改。例如有一个dog类,年龄,公母都不能随意更改,那会乱套。
方法一般会用public修饰,因为要被外部调用。


