首页 > 其他分享 >从零开始 使用OMNET++结合VEINS,INET和SUMO的联合仿真

从零开始 使用OMNET++结合VEINS,INET和SUMO的联合仿真

时间:2024-03-31 23:33:15浏览次数:33  
标签:xml 仿真 ++ SUMO VEINS veins sumo inet

背景知识

当我们探索未来的交通系统和智能交通解决方案时,车辆到一切(Vehicle-to-Everything, V2X)通信技术显得尤为重要。V2X是指在车辆与车辆(V2V)、车辆与基础设施(V2I)、车辆与行人(V2P)以及车辆与网络(V2N)之间进行的通信。这种技术能够提高道路安全,优化交通流量,减少拥堵,提升驾驶体验,并为自动驾驶汽车的实现打下基础。

为了准确模拟和分析V2X通信中的复杂交互,需要使用一些专用的仿真工具:

  1. OMNeT++ 是一个公开源码的网络仿真框架,提供了广泛的工具集和功能,用于构建复杂的网络和其他分布式系统。OMNeT++的灵活性和模块化使其成为研究和模拟通信网络,特别是V2X通信网络的理想选择。

  2. INET 是OMNeT++的一个扩展模型库,专注于互联网协议和网络技术的仿真。INET提供了大量的网络协议模型,如TCP/IP、路由协议等,允许研究者构建和测试各种网络架构和服务。

  3. SUMO (Simulation of Urban MObility) 是一个开源的交通仿真软件,用于模拟城市的车辆流动。通过SUMO,研究人员可以创建详细的城市交通场景,包括道路网络、交通信号灯、车辆行为等,来分析不同交通策略和管理措施的效果。

  4. VEINS 是一个允许OMNeT++和SUMO之间进行联合仿真的框架,专门用于车辆通信系统的研究。它使得OMNeT++模拟的通信网络和SUMO模拟的移动车辆能够实时交互,从而实现对V2X通信场景的全面仿真。

使用OMNeT++结合VEINS、INET和SUMO进行联合仿真,能够在复杂的城市交通环境中准确模拟V2X通信。这种仿真可以帮助研究者评估V2X技术在实际应用中的表现,如通信延迟、系统可靠性和安全性等。此外,仿真结果还可以指导政策制定者和工程师设计更有效的交通管理策略和智能交通系统,推动智能交通技术的发展,为实现无缝、安全和高效的未来交通网络提供支持。

在这篇博文中,我将尝试:

  • 搭建仿真的环境,导入VEINS和INET开源库
  • 使用INET库中提供的网络协议和功能模型来模拟通信协议和网络功能
  • 使用SUMO创建道路网络和车辆轨迹,并导出到OMNeT++中
  • 在OMNeT++中使用VEINS通过socket和SUMO连接,借助VEINS实现的TRACI接口来设置车辆相关的行为和场景
  • 在OMNET++中结合VEINS,SUMO和INET运行联合仿真实验,并分析车辆在遇到紧急情况下的通信状况和具体行为

环境搭建

OMNet++ / Veins / INET / SUMO下载

关于这四个软件的下载安装,以下两篇博文是很好的参考:

https://www.cnblogs.com/Xylona/p/17779621.html

(veins5.0+sumo1.2.0+OMNeT++5.5.1)车载自组织网络仿真环境安装配置教程(一步一步)_sumo1.3.0+veins5.2+omnet++5.5.1-CSDN博客

 我最终使用的版本:

  • OMNeT++ 5.6.2
  • Veins 5.2
  • SUMO 1.10.0
  • INET 4.2.5

环境配置

  • 修改configure.user:

在分别下载好这四个版本的软件后,进入OMNet++文件夹,打开configure.user,修改PREFER_CLANG的值为no

  • sumo环境变量配置:

共配置四条:

  • 解压&编译OMNET++:

打开OMNet++中的“mingwenv.cmd”,并按下任意键开始解压;

解压完成后分别输入“./configure”和“make”进行编译,make的时间大概要一个小时

  • 打开OMNETT++:

在“mingwenv.cmd”中输入“omnetpp”打开IDE界面

  • 导入VEINS & INET:

点击左上角File -> Import:

导入inet和veins:

注意!对于veins要勾选search for nested projects!!

  • 点击左上角Project -> Build All
  • 运行INET的示例仿真:

点击inet -> examples -> aodv ->  omnetpp.ini,然后右键Run as Omnet ++ simulation就可以看到示例仿真:

  •  运行VEINS的示例仿真:

在“mingwenv.cmd”中输入以下指令使veins连接上sumo:

C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/veins-veins-5.2/bin/veins_launchd -vv -c C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/sumo-1.10.0/bin/sumo.exe

点击左侧veins -> examples -> veins -> omnetpp.ini,然后右键Run as Omnet ++ simulation就可以看到示例仿真:

点击Run后:


现在,成功完成了环境搭建,接下来就要开始创建一个属于我的项目

关于接下来的大体步骤,我参考了以下油管的视频,但是如果仿真软件的版本和我相差不大,建议参考我的版本,因为我按照我下载的这一套VEINS,SUMO,INET,OMNET++版本完全按照视频操作会出现一些问题,所以建议有什么不会再去参考油管的视频:

How to Create a New OMNET++ Project That Works with INET and Veins (youtube.com)

How to Simulate a V2V Network using OMNET++, INET, and Veins - YouTube

How to Add A Custom Sumo Simulation to OMNET++ (youtube.com)


创建工程

  • 左上角file -> new -> OMNET++ project创建project:

  • 右击新建的project -> Properties -> Project References,链接INET和VEINS和veins_inet

  • 别关掉上面的窗口,按照以下步骤完善make规则:

 


现在,这个新的project已经成功的refer到了veins 和 inet,但是,问题来了,如何才能将二者结合起来?答案是:veins中提供了一个sub-project,名字是veins_inet,其中提供了大量现成的cc代码和一个简单的示例,这个sub-project提供了一个将二者结合的很好例子,这也是为什么刚刚import veins的时候要同时Import veins_inet这个子项目!


 

VEINS & INET 的结合

  • 将veins_inet的仿真例程迁移到我新创建的project:

找到刚刚import的veins_inet子项目, 找到 examples -> veins_inet 拷贝到新项目的simulation下:

出现了大量的红色叉叉,这是因为.ned文件中的package的名字不正确

② 修改.ned文件中的package name:

//在所有.ned文件中:(其实就两个)
//将开头的 
package XXXXXXX.veins_inet;
//修改为 
package _9038_project.simulations.veins_inet;

可以通过运行当前路径下的omnetpp.ini来测试是否成功:

由于此处包含veins,别忘了在OMNETT++根目录下的mingwenv.cmd中输入以下代码连接SUMO:

C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/veins-veins-5.2/bin/veins_launchd -vv -c C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/sumo-1.10.0/bin/sumo.exe

 

可见,成功在自己创建的项目中运行了veins_inet的例程仿真! 


现在,已经可以成功的在自己创建的project里运行结合veins_inet例子下的仿真,但是例子中的sumo地图并不是我想要的,下一步是根据自己的地图,实现veins,inet,sumo的联合仿真


VEINS & INET & SUMO 的联合仿真

  • 创建自己的仿真路径

在新建项目的simulations下再创建一个“my_veins_inet”的文件夹,把刚刚“veins_inet”的内容全部复制进来:

       (同样,记得修改两个.ned文件中的package名,此处不再展开!)

然后,将square.net.xml; square.poly.xml; square.rou.xml; obstacles.xml删除,剩下的留着待会修改

  • 自定义sumo地图导入:

此处我选择了留学所在城市的一家costco附近的地图

网站:https://www.openstreetmap.org/export#map=16/42.9852/-81.2900

选定区域后点击左侧的“Export”导出.osm文件

通过python脚本分别生成.net.xml;.rou.xml;.poly.xml:

请根据文件位置修改文件路径!

import numpy as np
import subprocess

#生成map.net.xml

# 设置你的.osm文件路径
osm_file_path = 'costco.osm' #根据https://www.openstreetmap.org 网站导出的

# 设置输出文件路径
output_file_path = 'costco.net.xml'

# 构建netconvert命令
netconvert_command = f'netconvert --osm-files {osm_file_path} -o {output_file_path}'

# 调用命令
subprocess.run(netconvert_command, shell=True)
print(".net.xml成功生成")

#生成map.poly.xml

osm_file_path = 'costco.osm' #根据https://www.openstreetmap.org 网站导出的
type_file_path = 'typemap.xml' #内容根据CSDN收藏

# 设置输出文件路径
output_file_path = 'costco.poly.xml'

# 构建netconvert命令
netconvert_command = f'polyconvert --net-file costco.net.xml --osm-files {osm_file_path} --type-file {type_file_path} -o {output_file_path}'

# 调用命令
subprocess.run(netconvert_command, shell=True)
print(".poly.xml成功生成")

# 生成map.rou.xml

# 设置SUMO环境变量(请根据你的SUMO安装路径进行调整)
sumo_tools_dir = "C:/Users/Majiaming/Desktop/WESTERN/9038_wireless_comm/project/project_new/sumo-1.10.0/tools"
sumo_network_file = "costco.net.xml"
output_route_file = "costco.rou.xml"

# 构建randomTrips.py脚本的完整命令
command = [
    "python",
    f"{sumo_tools_dir}/randomTrips.py",
    "-n", sumo_network_file,
    "-e", "100",
    "-l",
]

# 使用subprocess运行命令
result = subprocess.run(command, capture_output=True, text=True)

# 检查命令输出(可选)
if result.returncode == 0:
    print("执行成功1")
    print(result.stdout)  # 打印标准输出
else:
    print("执行出错1")
    print(result.stderr)  # 打印错误输出

####################################################

# 构建randomTrips.py脚本的完整命令
command = [
    "python",
    f"{sumo_tools_dir}/randomTrips.py",
    "-n", sumo_network_file,
    "-r", output_route_file,
    "-e", "50",
    "-l",
]

# 使用subprocess运行命令
result = subprocess.run(command, capture_output=True, text=True)

# 检查命令输出(可选)
if result.returncode == 0:
    print("执行成功2")
    print(".rou.xml成功生成")
    print(result.stdout)  # 打印标准输出
else:
    print("执行出错2")
    print(result.stderr)  # 打印错误输出

生成后,将这三个文件放到“my_veins_inet”下备用

  • 修改SUMO相关文件:

my_veins_inet”下已经有了sumo相关的各种文件,现在需要根据具体要求修改它们

将square.sumocfg和square.launchd.xml改名为costco.sumocfgcostco.launchd.xml,并根据新的rou, net和poly文件修改内容:

 

修改完成后,可以用sumo gui打开costco.sumocfg查看效果:

修改.rou.xml:

为了让仿真效果尽可能的清晰,将随机生成的50辆车的代码注释掉,并替换成如下的代码:

<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/routes_file.xsd">
   <vType id="vtype0" accel="2.6" decel="4.5" sigma="0.5" length="4.5" minGap="2.5" maxSpeed="14" color="1,1,0"/>
   <route id="route0" edges="-62088703#4 -62088703#3 -62088703#2 -62088703#1 474843229#0 474843229#1 474843229#2 474843229#3 474843229#4 474843229#5 474843229#6 466961753#1 466961753#2 466961753#3 466961753#4 466961753#5 466961753#6 466961753#7 466961753#8 -466961753#8"/>
   <flow id="flow0" type="vtype0" route="route0" begin="0" period="3" number="5" arrivalPos="0" />
</routes>
  • <vType>元素:
  1. 定义了一种车辆类型vtype0
  2. accel(加速度): 车辆的最大加速度,这里是2.6米/秒²
  3. decel(减速度): 车辆的最大减速度,这里是4.5米/秒²
  4. sigma(驾驶员不确定性): 描述了驾驶员行为的不确定性,这里是0.5,范围从0(完全确定行为)到1(非常不确定的行为)
  5. length(长度): 车辆的长度,这里是4.5米
  6. minGap(最小间隙): 车辆之间的最小间隙,这里是2.5米
  7. maxSpeed(最大速度): 车辆的最大速度,这里是14米/秒
  8. color(颜色): 车辆的颜色,这里定义为黄色(RGB颜色模式中的1,1,0)
  • <route>元素:
  1. 定义了一个路线route0
  2. id属性指定了路线的唯一标识符
  3. edges属性列出了构成该路线的边的序列。边是道路网中的基本元素,代表单向的道路。这里列出的边通过它们的唯一标识符来指定,负号表示逆向行驶
  • <flow>元素:
  1. 定义了一个流量flow0,表示一定数量的车辆将沿指定的路线移动
  2. id属性为流量提供了一个唯一标识
  3. type属性指定了车辆类型,这里引用了之前定义的vtype0
  4. route属性指定了车辆将遵循的路线,这里是route0
  5. begin属性定义了流量开始的时间(秒),这里是从仿真开始的0秒
  6. period属性定义了车辆生成的时间间隔(秒),这里是每3秒生成一次
  7. number属性定义了将被生成的车辆总数,这里是5辆
  8. arrivalPos属性定义了车辆到达目的地的位置,这里是0,通常表示车辆将尝试行驶整个路线

  • 修改.ned & .ini文件:

修改Scenario.ned

修改灰色区域范围大小,以适应新的sumo地图

修改omnetpp.ini

  • 运行仿真文件

右击omnetpp.ini,右击Runs as -> Run Configuration:

设置完成后点击右下角:Apply -> run:

进入页面后选择genreal:

结果展示

最终仿真效果:

  • 根据.rou.xml的设置,node[0]至node[4]五辆车预先定义要走的路线完全一致。
  • 然而,node[0]变红代表了模拟突发事故,事故发生后,车辆之间开始相互通信,得到消息的车辆会变成绿色。
  • node[3]和node[4]由于具备调头条件,直接掉头,更改了原有的路线并规划了新的路线。
  • node[1]和node[2]由于不具备调头条件只能按照原有路线继续往前,在被node[0]挡住后,根据sumo内部的算法换道并继续前进。

仿真结果简单分析

吞吐量(overall throughput value)

上图显示了4个节点随着时间的吞吐量变化,可以明显的看到,对于这4个节点,吞吐量都在事故发送时开始增加,在0.1秒左右达到最高,并在0.2秒后快速下降,可见通讯速度很快。

 

包发送量(packet sent count)

可见,node0为2次,其他三个节点都为1次,这也很好理解,因为node0是事故发送的节点,所以需要发送一次事故包一次转发包;而其他节点只需要发送一次转发包即可。

思路和代码解读

现在,已经实现OMNET++,SUMO,VEINS和INET的联合仿真,但是背后的原理是什么其实依然一知半解,于是我开始解读代码,尝试理解为什么仿真会这样进行。

注意,以下所有内容都属于自问自答,完全可能有错误,欢迎大家指正,本人纯小白

Q1在最后的仿真路径中,Scenario.ned中的manager,,radio medium,node和physical environmnet分别到底代表什么?在图形界面中拖动他们的位置或改变他们的大小有什么实际的意义?

A

  • manager通常用于仿真管理任务,如控制仿真流程、管理节点间的通信、协调事件等
  • radioMedium定义了无线电传播的模型,如何模拟信号的传播和衰减、如何处理节点间的无线通信等
  • node代表仿真网络中的一个参与者或设备,在车辆网络仿真中,这些节点通常是车辆
  • physicalEnvironment模块定义了仿真的物理环境,包括地形、建筑物等,这些因素可能会影响信号的传播和车辆的移动

在OMNeT++ IDE的图形界面中,拖动这些组件的位置或改变它们的大小主要是为了改善视觉布局,帮助仿真设计者更好地理解和组织仿真场景。这些操作不直接影响仿真逻辑或结果,仿真的逻辑和行为完全由NED文件中的参数定义和配置文件(.ini文件)中的设置决定

Q2根据网络原理,网络协议应该分为5层,应用层,传输层,网络层,链路层,物理层,这些体现在项目的哪里?

A

  • 在这个仿真中,每一个节点(汽车)都被注册为一个名为VeinsInetCar的对象:

  • 而通过跳转VeinsInetCar.nedf会发现,它继承自一个叫AdhocHost的对象:

  • 使用同样的方法不停的跳转,会得出这样一个继承的关系:

  • 而通过分析这些.ned文件,可以总结出以下内容:

  • 所以,每个汽车所代表的VeinsInetCar都是从NodeBase一路继承过来的,而观察继承的路线就可以发现,在一路的继承中,就分别实现了网络协议中5大层的接口定义!

 

Q3根据Q2,有个随之而来的问题:我理解了为什么VeinsInetCar具备了完整的协议栈,网络协议的5大层实现在了哪里。但是我依然不理解:从NodeBase一直继承到了VeinsInetCar,现在已经有了无数的接口用来配置完整的协议,但是使用哪些接口具体配置哪些值,比如在链路层具体配置为无线还是VLAN,网络层具体配置为IPV4还是IPV6,传输层具体配置为TCP还是UDP,这些具体的配置在哪里?

A

答案是:大部分在仿真文件omnetpp.ini中定义:

  • 链路层具体配置:

  • 网络层具体配置:

  •  应用层具体配置:

物理层没有定义很好理解,这都是最底层的设定。

但是,显然会有一个问题,为什么没有传输层的相关定义?到底使用TCP还是UDP还是什么?

chatgpt给出了三种回答:

  1. 默认配置:许多OMNeT++和INET模块,包括传输层协议模块,具有默认配置。例如,如果一个应用需要使用UDP或TCP,并且在其模块定义中正确指定了,那么即使在.ini文件中没有明确配置,这些传输层协议也会被自动实例化并使用默认设置。

  2. 应用层决定:在许多情况下,特别是在使用简单应用(如UdpBasicApp)或其他特定的应用模型时,所使用的传输协议(UDP、TCP等)可能已在应用层模块的实现中明确定义。例如,一些应用模型默认使用UDP进行通信,而无需在.ini文件中进行额外配置。

  3. 模块继承:由于VeinsInetCar继承自AdhocHost,进而继承自StandardHost,一直到NodeBase,这些基础模块可能已经包含了对传输层协议的支持。特别是在AdhocHostStandardHost级别,通常会包括对主要传输层协议(如TCP和UDP)的支持,而无需在每个仿真场景的.ini文件中进行单独配置。

我认为都有一定的道理,根据上面的截图可以知道,应用层是由VeinsInetSampleApplication来实现的,跳转到其对应的.ned文件,可以发现它继承自VeinsInetApplicationBase:

再次跳转,找到这个VeinsInetApplicationBase:

这个代码并没有明确的规定UDP还是TCP,但是根据socket门的注释,应该是使用的UDP。

  • 最后,为了验证,运行仿真并点击一个节点:

可见,虽然链路层出现了循环回路lo,传输层出现了tcp,但是真正实现从上到下沟通的协议还分别是:UDP,IPV4和WLAN

我的推测是:由于没有明确的规定lo和tcp不能使用,所以他们也可能存在,并在某种方面帮助消息传播?

Q4在最后的仿真结果中,汽车的行为是在哪里被定义的?比如,为什么node[0]会在20秒坏掉又在50秒重新启动?

A

这个问题的答案其实已经在仿真结果下的文字中回答一部分了,但是其行为的核心,其实还是在于应用层所定义的这个“VeinsInetSampleApplication”,核心的代码就定义在VeinsInetSampleApplication.cc中(veins_inet->src->veins_inet),以下是代码中最重要的两个函数:

//设置哪个节点什么时候速度为0,什么时候恢复的函数
bool VeinsInetSampleApplication::startApplication()
{
    // host[0] should stop at t=20s
    if (getParentModule()->getIndex() == 0) { //如果节点为0
        auto callback = [this]() { //这个函数在20秒倒计时结束后被运行
            getParentModule()->getDisplayString().setTagArg("i", 1, "red"); //车子变红

            traciVehicle->setSpeed(0); //车子不会立刻停下来,因为仿真软件中定义的物理限制的影响,例如车辆的减速能力,但车子肯定会在20秒后很短时间内停下

            //定义要发送的包的内容
            auto payload = makeShared<VeinsInetSampleMessage>();
            payload->setChunkLength(B(100));
            payload->setRoadId(traciVehicle->getRoadId().c_str());
            timestampPayload(payload);

            //定义包的名字并发送包
            auto packet = createPacket("accident");
            packet->insertAtBack(payload);
            sendPacket(std::move(packet));

            // host should continue after 30s
            auto callback = [this]() { //这个函数在30秒倒计时结束后被运行
                traciVehicle->setSpeed(-1); //车子恢复正常
            };
            timerManager.create(veins::TimerSpecification(callback).oneshotIn(SimTime(30, SIMTIME_S)));//30秒倒计时
        };
        timerManager.create(veins::TimerSpecification(callback).oneshotAt(SimTime(20, SIMTIME_S)));//20秒倒计时
    }

    return true;
}


//规定节点在收到消息之后如何反应的函数
void VeinsInetSampleApplication::processPacket(std::shared_ptr<inet::Packet> pk)
{
    auto payload = pk->peekAtFront<VeinsInetSampleMessage>();

    EV_INFO << "Received packet: " << payload << endl;

    getParentModule()->getDisplayString().setTagArg("i", 1, "green"); //接收到包,车子变绿

    traciVehicle->changeRoute(payload->getRoadId(), 999.9); //重新规划路径

    if (haveForwarded) return; //用于避免重复处理或发送相同的消息

    //如果是第一次收到消息
    auto packet = createPacket("relay"); //创建用于转发的包
    packet->insertAtBack(payload);
    sendPacket(std::move(packet)); //转发

    haveForwarded = true;
}

根据代码可以得出以下的逻辑:

当node[0]停止,即模拟事故发生时,会立刻装载一个大小为100Byte的包,并在其中添加当前所在路的ID,然后向通讯范围内的所有节点(汽车)发送这个包。每个收到这个包的节点(汽车)会立刻从包中获取事故路段的ID,并假设事故路段的通过时间为最大。这就导致所有会经过事故路段且拥有切换路径条件的汽车在收到这个包后会立刻切换路径。同时,向其通信范围的所有节点(汽车)转发一次包的内容。

 

Q5

A

 

标签:xml,仿真,++,SUMO,VEINS,veins,sumo,inet
From: https://blog.csdn.net/mjmmm/article/details/137094865

相关文章

  • 突破编程_C++_C++14新特性(变量模板)
    1变量模板在C++14中的引入与扩展在C++14中,变量模板的引入与扩展为编程带来了许多便利,特别是在泛型编程方面。这一特性允许我们直接定义模板变量,而不需要将其包装在模板类或模板函数中,从而使得代码更加直观和简洁。首先,我们来详细了解一下C++14之前模板的使用限制。......
  • 突破编程_C++_网络编程(OSI 七层模型(传输层))
    1传输层的功能与特点1.1传输层的功能传输层是OSI七层模型中的第四层,它位于网络层和应用层之间,起着承上启下的关键作用。以下是关于OSI传输层功能的详细讲解:一、提供可靠的数据传输服务传输层的主要任务是确保数据在源主机和目标主机之间可靠地传输。它通过一系列......
  • 【C++实验1】学生成绩信息管理系统题解
    【问题描述】编写一个基于结构体得学生成绩信息管理系统。主要功能如下:1.用结构体存放所有数据。2.每个功能都用函数实现。3.输入10个学生的学号和三门课程的成绩。4.计算每个学生的总分。5.按总分从高到低排序。6.加上名次一列。7.输出最后的二维表格样式的成......
  • C++--STL函数模板
    一.函数模板我们可以定义一个函数模板(functiontemplate),而不是为每个类型都定义一个函数。一个函数模板就是一个蓝图,可用来生成针对特定类型的函数。例如用于比较两个数字的大小compare()函数的模板如下:template<typenameT>intcompare(constT&v1,constT&v2){......
  • C++初阶篇----内存管理
    目录引言1.内存分布2.C动态内存管理方式:malloc/calloc/realloc/free3.C++动态内存管理:new和delete3.1内置类型3.2自定义类型4.operatornew与operatordelete函数4.1operatornew与operatordelete函数5.new和delete的实现底层5.1内置类型5.2自定义类型引......
  • C++ 引用传递 超级详细 小白也行
    一.引用的概念引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。(本名和小名共用一块地址)例如:就像是给你取小名,本名小名都是你,所有作用也都一样。 类型&引用变量名(对象名)=引用实体(如图下)......
  • 便民查询 - C++也能写H5应用
    这款应用是我和我的一位粉丝共创,他还在读大二,刚学了C++,他一直都想找个靠谱的项目练练手,刚好我准备做一款便民查询的应用,当然这个应用如果用C++来写后端,会有一种大炮打蚊子的感觉,C++也不适合做APP开发,开发效率和debug容易度都比其他的高级语言差一截。但是由于他想找个项目练手,我......
  • C++ vector
    文章目录vector的介绍vector介绍vector的定义vectoriterator的使用vector空间增长问题vector的增删查改vector的模拟实现创建vector类和成员变量iterator迭代器范围for构造函数赋值、析构空间容量resize和reserve的区别下标操作符重载插入删除交换vector深度剖析v......
  • C++中Switch穿透的妙用
    在C++中,Case穿透(fall-through)指的是在switch语句中,一个case标签没有显式地使用break语句来终止,而是直接执行下一个case标签中的代码。虽然Case穿透在编程中有时会被视为不良实践,因为它可能导致代码的可读性变差和潜在的错误,但有时也可以利用它来实现一些特定的目的。以下是一些利......
  • C++单例类和线程的一个结合
    一个C++的单例类,类里面定义了一个线程对象,线程对象会定时去计算一个时间,并返回这个计算出来的时间。 应用场景:比如,有些比较消耗时间的操作,就可以放在线程里面定时计算,我们在外部定时读取。这样就可以避免主线程被阻塞。 #include<iostream>#include<thread>#incl......