[Linux服务器] Linux C++快速入门

72 0
Honkers 2025-8-30 05:25:26 来自手机 | 显示全部楼层 |阅读模式

本文旨在以最小的篇幅,最少的信息,介绍最高频使用的内容,从而掌握C++编程开发的能力。

这种能力,只是语法层面,不涉及具体的函数库,基础库等内容。

能力准备:需要C语言基础。基本的if else, while,基础数据类型等等,不在本文涉及的范围之内。

环境准备

感谢微软的努力,让我们在 Windows环境下可以毫无障碍的进行Linux开发。

推荐使用Windows + wsl2 的环境开发。

打开Microsoft Store,搜索ubuntu,安装最新版本即可。

安装如有问题,请自行百度

使用 vscode + wsl插件的形式,编辑、编译代码。ctrl + ` 可以在命令行和文件编辑之间切换,非常的方便。

在ubuntu下,安装cmake, gcc, g++。安装方法自行百度。

代码的组织结构

  1. build : 编译目录
  2. docs : 文档目录,负责存放该代码相关的信息
  3. libs : 该项目依赖的外部的库及头文件
  4. libs/include 依赖库的头文件
  5. libs/[编译器名称] 平台相关的库文件。比如cc放x86-64位的库,arm-linux-gnu-gcc放该编译器编译出的相关的库文件
  6. source 项目源码目录
  7. test 测试目录,内含测试代码
复制代码

本文中所有涉及到的示例代码,可在此下载:

链接:https://pan.baidu.com/s/1f73k5uxYTRgtMEORbvgqvA?pwd=tnje

提取码:tnje

编译方法:

  1. cd build
  2. cmake ..
  3. make
复制代码

该代码展示了一个功能库的目录结构,编译方法。

如果想要做成可执行程序,参考test目录中的内容即可。

类的基本规则

通常定义一个类,我们会分为源文件.cpp,和头文件.h分开来用。

如下为头文件。其中的注释请仔细阅读

  1. // rtspc.h
  2. /**
  3. * @author
  4. * @brief rtsp客户端
  5. * @version 0.1
  6. * @date 2023-11-30
  7. *
  8. * @copyright Copyright (c) 2023
  9. *
  10. */
  11. // 使用pragma once 让头文件只引用一次。与下面的 _RTSPC_H_ 作用一致
  12. #pragma once
  13. // 头文件避免重复引用。与#pragma once 二选一
  14. #ifndef _RTSPC_H_
  15. #define _RTSPC_H_
  16. #include <string>
  17. #include <functional>
  18. // 注意,原则上禁止在头文件中使用using namespace xxx。避免命名空间失效
  19. // 实际上,不管源文件和头文件,都不建议using namespace的方式。而是直接写全。
  20. // using namespace std;
  21. // 这是命名空间。可以有效隔离类,函数,变量的重名问题。在定义库时都建议添加使用。
  22. namespace rtsp
  23. {
  24. class Rtspc
  25. {
  26. // 公共方法,类外部可访问
  27. public:
  28. // 回调函数新写法。对应lambda表达式使用
  29. using OnData = std::function<void(const char *data, int len)>;
  30. Rtspc(bool btcp, OnData onData);
  31. // 注意,如果该类会被继承,则务必将它写成虚函数。否则影响析构
  32. virtual ~Rtspc();
  33. // 建议安装doxygen插件,在函数上方输入 /// 或者 /** 自动生成注释模板。
  34. /// @brief 公共方法,大写开头。私有方法,小写开头(代码规范,自行约定)。
  35. /// @param url
  36. /// @param bTCP
  37. /// @return
  38. int Run(const std::string &url, bool bTCP);
  39. /// @brief 停止
  40. /// @return
  41. void Stop(){
  42. _running = false;
  43. }
  44. // 保护方法。类内及类的子类可访问。
  45. protected:
  46. // 对于不改变类的内容的方法,后面加const
  47. // 对于不希望被改变的返回的引用,前面加const
  48. const std::string &getValue() const {return _url;}
  49. // 私有方法。通常它和私有成员的private分开写,更清晰一些。
  50. private:
  51. void workthread();
  52. // 私有成员。成员变量通常为私有成员
  53. private:
  54. std::string _url; // 成员变量以 '_' 开头,以便代码中与局部变量,参数做区分。
  55. bool _btcp;
  56. bool _running;
  57. OnData _onData;
  58. };
  59. #endif //_RTSPC_H_
  60. } // namespace rtsp
复制代码

源文件长这样。

  1. // rtspc.cpp
  2. #include <iostream>
  3. #include <chrono>
  4. #include <thread>
  5. #include "rtspc.h"
  6. namespace rtsp
  7. {
  8. // 构造函数
  9. Rtspc::Rtspc(bool btcp, OnData onData)
  10. // 这下面是类成员初始化的写法。据说比写在大括号里效率要高
  11. :_btcp(btcp)
  12. ,_running(true)
  13. ,_onData(onData)
  14. {
  15. }
  16. Rtspc::~Rtspc()
  17. {
  18. }
  19. int Rtspc::Run(const std::string &url, bool bTCP)
  20. {
  21. std::cout << "Running " << url << std::endl;
  22. const std::string data = "haha, i am data";
  23. while (_running)
  24. {
  25. std::this_thread::sleep_for(std::chrono::milliseconds(400));
  26. _onData(data.c_str(), data.size());
  27. }
  28. return 0;
  29. }
  30. } // namespace rtsp
复制代码

继承与虚函数

干货都在代码中

  1. //rtp-pack.h 这是父类
  2. #pragma once
  3. #include <memory>
  4. #include <functional>
  5. namespace rtsp
  6. {
  7. class RtpPack
  8. {
  9. public:
  10. // 回调打包好的数据
  11. using OnRtpData = std::function<void(const std::string &rtp)>;
  12. using Ptr = std::shared_ptr<RtpPack>;
  13. RtpPack(OnRtpData onRtp)
  14. :_onRtp(onRtp)
  15. {}
  16. // 注意,如果该类会被继承,则务必将它写成虚函数。否则影响析构
  17. virtual ~RtpPack(){}
  18. virtual int Pack(const uint8_t *data, int len) = 0;
  19. /// @brief 创建打包器
  20. /// @param encode 编码方式
  21. /// @return
  22. static Ptr CreatePacker(const char *encode, OnRtpData onData);
  23. protected:
  24. OnRtpData _onRtp;
  25. };
  26. } // namespace rtsp
复制代码

  1. // rtp-pack-h264.h 这是子类
  2. #pragma once
  3. #include <rtp/rtp-pack.h>
  4. namespace rtsp
  5. {
  6. class RtpPackH264 : public RtpPack
  7. {
  8. private:
  9. /* data */
  10. public:
  11. RtpPackH264(RtpPack::OnRtpData onRtp)
  12. :RtpPack(onRtp)
  13. {}
  14. virtual ~RtpPackH264(){}
  15. virtual int Pack(const uint8_t *data, int len) override
  16. {
  17. std::string rtp;
  18. rtp.append("begin flag");
  19. rtp.append((const char *) data, len);
  20. _onRtp(rtp);
  21. return 0;
  22. }
  23. // 非虚函数
  24. int Demo()
  25. {
  26. return 0;
  27. }
  28. };
  29. } // namespace rtsp
复制代码

如代码所示,RtpPackH264为子类,它继承于RtpPack

虚函数

其中,Pack 在RtpPack中,被定义为纯虚函数。这意味着你无法将RtpPack实例化。

也就是说,RtpPack pack; 是非法的。

只有在子类中实现了 Pack方法,就像RtpPackH264 一样,它才能够被实例化。

std::function 与lambda表达式

它是C++11开始支持的好东西,它有两个作用:

1,替代回调函数

2,替代回调函数的 param回传参数。

最重要的是第二点,结合lambda函数使用,让我们的代码看起来是如此的与众不同。

请参看如下示例。

testrtp.cpp

  1. #include <rtp/rtp-pack-h264.h>
  2. #include <sys/types.h> /* See NOTES */
  3. #include <sys/socket.h>
  4. int main()
  5. {
  6. int sock = socket(AF_INET, SOCK_STREAM, 0);
  7. connect(sock, xxx);
  8. // std::function 的定义与 lambda的应用
  9. rtsp::RtpPackH264 rtp([sock](const std::string &rtp){
  10. send(sock, rtp.c_str(), rtp.size());
  11. });
  12. while (1)
  13. {
  14. rtp.Pack("123456", 6);
  15. }
  16. return 0;
  17. }
复制代码

可以看到,RtpPackH264 rtp 在实例化的时候,传的参数是一个奇怪的东西:[sock](const std::string &rtp){xxx

这个奇怪的东西,叫作lambda表达式。也叫匿名函数。

[]内部,就相当于我们注册回调函数时,注册进去的param,它通过回调函数再传回给我们。

而这里则不需要这么麻烦,你可以在[ ] 中加入任意多的变量,然后就如代码中的sock一样,在lambda体中使用。

需要注意的是,[sock]这是值传递的写法。它会记录sock的值。还可以这样写:[&sock],引用传递。此时需要注意,它相当于记录了sock的指针。

这里还有另一种写法,可以将lambda表达式写成一个变量:

  1. auto onRtp = [sock](const std::string &rtp){
  2. send(sock, rtp.c_str(), rtp.size());
  3. };
  4. rtsp::RtpPackH264 rtp(onRtp);
复制代码

小提示:

本节中的代码,没有源文件。类的定义与实现,可以都写在头文件中,只不过这要看实际情况而写。

它的缺点是编译、链接较慢,封装性差。

但有些时候,比如模板,必须写在头文件中。

类的本质是什么?

C++中的类,命名空间,虚实函数,本质上都可以用C来表达。或者换个说法,C++编译器最终会把它变成C语言那样的东西。

就拿RtpPackH264来讲,它在编译器处理后,变成了如下的东西。至于C++的各种特性,都是语法糖。

  1. rtp-pack-h264.c
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. typedef void (*rtpCallback_t)(void *userparam, const uint8_t *data, int len);
  5. struct rtpH264_class{
  6. // 类成员变量
  7. rtpCallback_t _onRtp;
  8. void *_userparam;
  9. // 虚函数表
  10. struct rtpH264VirtualFunctionTable{
  11. int (*Pack)(struct rtpH264_class *thiz, const uint8_t *data, int len);
  12. }functionTable;
  13. };
  14. // 虚函数的实现,对应rtsp::RtpPackH264::Pack
  15. // 注意这奇怪的名字,param之后,列出了参数类型。这就是为什么C++允许重名但参数不同的函数。
  16. int rtsp_RtpPackH264_Pack_param_u8_i32(struct rtpH264_class *thiz, const uint8_t *data, int len)
  17. {
  18. return 0;
  19. }
  20. // 构造函数,生成对象时自动调用。无论是new,还是局部变量
  21. struct rtpH264_class *rtsp_RtpPackH264_RtpPackH264_param_rtpCallback_t_void(rtpCallback_t onRtp, void *userparam){
  22. // 分配内存
  23. struct rtpH264_class *rtp = malloc(sizeof(struct rtpH264_class));
  24. // 构造虚函数表
  25. rtp->functionTable.Pack = rtsp_RtpPackH264_Pack_param_u8_i32;
  26. rtp->_onRtp = onRtp;
  27. rtp->_userparam = userparam;
  28. return rtp;
  29. }
  30. // 析构函数。在对象生命周期结束时,自动调用
  31. void rtsp_RtpPackH264_del_RtpPackH264(struct rtpH264_class *rtp)
  32. {
  33. free(rtp);
  34. }
  35. // 非虚函数的实现
  36. int rtsp_RtpPackH264_Demo(struct rtpH264_class *thiz)
  37. {
  38. return 0;
  39. }
复制代码

以上代码中可以看到,类的函数的名字,实际上是由“命名空间+类名+方法名+参数类型”以一定规则,形成的。

而构造函数,实际上是编译器在生成对象是,帮我们调用的。

虚函数表,是在构造函数中指向了各个实际的函数。(不准确,但按此理解无不利影响)

敲黑板

所以,非虚函数,是在编译时就确定了调用关系的。比如调用RtpPackH264:emo,是在编译时就确定了要调这个函数。

虚函数,是在执行时,查表,确定虚函数表中,指向的是哪个函数,从而完成调用。

请仔细研读,对照上述c实现的类代码,与类本身的关系。

请思考如下代码,最终输出的是什么?

  1. class A{
  2. public:
  3. void running(){printf("A running\n");}
  4. virtual void VirtualFunc(){printf("A virtual func\n");}
  5. };
  6. class B: public A{
  7. public:
  8. void running(){printf("B running\n");}
  9. virtual void VirtualFunc(){printf("B virtual func\n");}
  10. };
  11. void main()
  12. {
  13. B b;
  14. A *a = &b;
  15. b.running();
  16. a->running();
  17. b.VirtualFunc();
  18. a->VirtualFunc();
  19. }
复制代码

搜索一下隐藏和覆盖,看看网上五花八门的解释,对照我们把C++的类改成C的写法,你能明白隐藏和覆盖是咋回事了吗?

还有lambda,std::function。

我们将lambda以基础的C++类的方式来实现,它是这样的:

rtp-pack-lambda.hpp (由 rtp-pack 的无lambda写法)

  1. /**
  2. * @author LiuFengxiang (20451250@qq.com)
  3. * @brief 以C++的类模拟 lambda,捕获等行为
  4. * @version 0.1
  5. * @date 2023-12-01
  6. *
  7. * @copyright Copyright (c) 2023
  8. *
  9. */
  10. #pragma once
  11. #include <string>
  12. namespace lambdaTest
  13. {
  14. // 代替 std::function<void(const std::string &rtp)>
  15. // std::function<xxx> 实际上是模板实例化成了一个类,这个类会记录函数指针和lambda捕获的变量
  16. class RtpPackFunc
  17. {
  18. public:
  19. typedef void (*OnRtpData)(RtpPackFunc *thiz, const std::string &rtp);
  20. RtpPackFunc(OnRtpData data):_onRtp(data){}
  21. virtual ~RtpPackFunc(){}
  22. void Call(const std::string &rtp){
  23. _onRtp(this, rtp);
  24. }
  25. public:
  26. OnRtpData _onRtp;
  27. };
  28. class RtpPack
  29. {
  30. public:
  31. RtpPack(RtpPackFunc *callback)
  32. :_callback(callback)
  33. {}
  34. ~RtpPack(){}
  35. void Pack(){
  36. _callback->Call("haha");
  37. }
  38. private:
  39. RtpPackFunc *_callback;
  40. };
  41. } // namespace lambdaTest
复制代码

它的测试代码:testrtp-lambda.cpp, (由testrtp.cpp转化而来)

  1. /**
  2. * @author LiuFengxiang (20451250@qq.com)
  3. * @brief 对应testrtp.cpp,我们不使用lambda,而是改用基础的类来实现
  4. * @version 0.1
  5. * @date 2023-12-01
  6. *
  7. * @copyright Copyright (c) 2023
  8. *
  9. */
  10. #include <sys/types.h> /* See NOTES */
  11. #include <sys/socket.h>
  12. #include <unistd.h>
  13. #include <rtp/rtp-pack-lambda.hpp>
  14. using namespace lambdaTest;
  15. // 实际的实现并不相同,但是这样写起来优雅一点儿,也并不妨碍理解。
  16. class MyRtpPackFunc :public RtpPackFunc
  17. {
  18. public:
  19. MyRtpPackFunc(RtpPackFunc::OnRtpData data, int sock)
  20. :RtpPackFunc(data)
  21. ,_sock(sock)
  22. {}
  23. virtual ~MyRtpPackFunc(){}
  24. public:
  25. int _sock;
  26. };
  27. // 编译器将lambda表达式生成了回调函数
  28. static void MyOnRtpData(RtpPackFunc *func, const std::string &rtp)
  29. {
  30. printf("%s running, data: %s\n", __func__, rtp.c_str());
  31. MyRtpPackFunc *mine = dynamic_cast<MyRtpPackFunc *> (func);
  32. if (mine != nullptr)
  33. {
  34. // 不能真发,没准备好呢
  35. if (0)
  36. send(mine->_sock, rtp.c_str(), rtp.size(), 0);
  37. }
  38. }
  39. int main()
  40. {
  41. int sock = socket(AF_INET, SOCK_DGRAM, 0);
  42. // connect(sock, xxx);
  43. /*在使用lambda时,编译器干了很多事情:
  44. 1,将匿名函数以自有的规则命名(objdump可以看一下,巨长),这里是 MyOnRtpData
  45. 2,将 std::function模板实例化,相当于 MyRtpPackFunc
  46. 3,将实例化的类生成对象,也就是这里的 func, 并传入初始化的两个参数: MyOnRtpData, sock
  47. */
  48. MyRtpPackFunc *func = new MyRtpPackFunc(MyOnRtpData, sock);
  49. // RtpPack记录的,实际上就是 std::function 的对象: func
  50. RtpPack rtp(func);
  51. while (1)
  52. {
  53. rtp.Pack();
  54. usleep(1*1000*1000);
  55. }
  56. }
复制代码

为了简化写法,我们帮编译器翻译的并不精确,但这并不妨碍理解。

你就记住:lambda表达式,就是编译器帮你起名的匿名函数。而std::function 则是编译器帮你生成的类。

所谓捕获,同样没什么神奇之处,值捕获,在类中直接记录了该变量的值,引用捕获,则是在类中记录了该类的指针。

思考:值捕获和引用捕获的变量,它们的生命周期是怎样的?

本节是想告诉你,C++的很多规则,并不是人为制订出来的,而是语言本身的实现上,必须这么做。它的因果关系是:因为这门语言是这样设计的,所以,产生了这样的规则。

本节只是个引子,借此提示。

多思考!

多思考!!

多思考!!!

多思考背后的机理,那才能举一反三,抓住本质。

记住这句话:C++的所有规则,都是因为设计时,只能这样做。

最常用容器

std::string 其实也是容器。但是我们把它当成一个普通类用就好了

vector 是数组容器。用来管理数量不定的同类型的内容

map 相当于一个映射表,key,value的形式。通过key可以快速的查找到对应的值。

如下代码展示了一些常用的函数,更详细内容查文档:DevDocs

  1. /**
  2. * @author LiuFengxiang (20451250@qq.com)
  3. * @brief 介绍常用容器的用法
  4. * @version 0.1
  5. * @date 2023-12-01
  6. *
  7. * @copyright Copyright (c) 2023
  8. *
  9. */
  10. #include <vector>
  11. #include <string>
  12. #include <map>
  13. #include <iostream>
  14. #include <memory> // 智能指针相关
  15. class Node
  16. {
  17. public:
  18. // 智能指针的技巧。简化写法
  19. // 使用的时候 Node::Ptr p 相当于 std::shared_ptr<Node> p
  20. using Ptr = std::shared_ptr<Node>;
  21. Node(int val, const std::string &str)
  22. :_val(val)
  23. ,_str(str){}
  24. virtual ~Node(){}
  25. const std::string &Str()const {return _str;}
  26. int Val()const {return _val;}
  27. void SetVal(int val){_val = val;}
  28. private:
  29. int _val;
  30. std::string _str;
  31. };
  32. static void vectorDemo()
  33. {
  34. // 使用智能指针代替Node * 或者 直接Node。
  35. // 如果用指针,在释放时必须逐个 delete。一但遗漏就会有内存泄漏
  36. // 如果直接用Node,则每次加入时都会产生拷贝动作。
  37. std::vector<Node::Ptr> vec;
  38. for (int i = 0; i < 10; i++)
  39. {
  40. auto node = std::make_shared<Node>(i, "haha");
  41. vec.push_back(node);
  42. }
  43. // 遍历,并按条件删除
  44. for (auto it=vec.begin();it!= vec.end();)
  45. {
  46. if ((*it)->Val() == 5)
  47. {
  48. // 删除成员时,不能直接it++
  49. it = vec.erase(it);
  50. }
  51. else
  52. {
  53. it++;
  54. }
  55. }
  56. // 另一种遍历方式
  57. for (auto &&it : vec)
  58. {
  59. printf("node val: %d, str: %s\n", it->Val(), it->Str().c_str());
  60. it->SetVal(it->Val()+1);
  61. }
  62. // 注意,如果是 std::vector<Node *> vec, vec.clear() 执行时,并不能自动对每个node做delete动作。
  63. // 所以此时,你需要先逐个 delete ,再行 clear
  64. vec.clear();
  65. }
  66. // map 主要是为了快速通过key 来找到对应的内容。key可以是基础类型,string
  67. // 如果要把自定义的类作为key,则需要自定义比较函数
  68. void mapDemo()
  69. {
  70. std::map<int, Node::Ptr> _map;
  71. for (int i = 0; i < 10; i++)
  72. {
  73. auto node = std::make_shared<Node>(i, "haha");
  74. _map.emplace(i, node);
  75. }
  76. // 查找
  77. int key = 5;
  78. auto it = _map.find(key);
  79. if (it != _map.end())
  80. {
  81. std::cout << "we Found it, key: " << it->first << ", str: " << it->second->Str() << std::endl;
  82. }
  83. else
  84. {
  85. std::cout << "We Failed found with key: " << key << std::endl;
  86. }
  87. // 遍历,并按条件删除
  88. for (auto it=_map.begin();it!= _map.end();)
  89. {
  90. if (it->first == 5)
  91. {
  92. // 删除成员时,不能直接it++
  93. it = _map.erase(it);
  94. }
  95. else
  96. {
  97. it++;
  98. }
  99. }
  100. // 另一种遍历方式
  101. for (auto &&it : _map)
  102. {
  103. std::cout << "node key: " << it.first << ", val: " << it.second->Str() << std::endl;
  104. it.second->SetVal(it.second->Val()+1);
  105. }
  106. }
  107. int main()
  108. {
  109. vectorDemo();
  110. mapDemo();
  111. return 0;
  112. }
复制代码

智能指针的使用

智能指针,是现代C++编程非常重要的一个特性。

实际上,有了智能指针之后,我们不应该再使用裸指针了。

下面罗列几个主要的使用场景:

1,配合容器使用

比如有一个类Car,它有很多成员。

如果定义std::vector _carvec,它的问题是:

Car需要可拷贝,有可能需要实现拷贝构造函数

Car是拷贝了多份的,是独立的。它们之间互相完全无关。

而如果使用指针 std::vector _pcarvec。

那你需要注意的是:插入前要new Car, 擦除前要先 delete 成员。

最容易忘的是_pcarvec.clear(). 这个方法执行前,你需要先遍历,逐个delete car

此时,更方便的用法是:std::vector> _shCarVec;

2,回调函数中使用weak_ptr

(weak_ptr的概念可以先百度一下。)

回调函数有个比较大的问题是,当回调上来之后,数据的消费者可能已经被销毁了。这时我们的指针,是否还生效?如何判断?

如下代码中,rtspc的回调,数据上来之后,窗口是否还存在?

这里,我们通过保存它的weak_ptr句柄,使用时,通过lock的形式来处理。

只要lock成功了,weak_ptr将会升级成为强引用,说明对象还在,我们就可以正常输入数据。

  1. //rtspclient.cpp
  2. #include <memory>
  3. #include <map>
  4. #include <vector>
  5. #include <mutex>
  6. #include <thread>
  7. #include <rtspc/rtspc.h>
  8. // 解码窗口
  9. class MyWindow
  10. {
  11. public:
  12. using Ptr = std::shared_ptr<MyWindow>;
  13. MyWindow(int winid)
  14. :_winid(winid)
  15. {}
  16. ~MyWindow(){
  17. printf("window %d destroyed\n", _winid);
  18. }
  19. int InputMediaData(const char *data, int len){
  20. // printf("input data in window: %d\n", _winid);
  21. return 0;
  22. }
  23. private:
  24. int _winid;
  25. };
  26. class WindowMgr
  27. {
  28. private:
  29. WindowMgr(/* args */){}
  30. ~WindowMgr(){}
  31. public:
  32. // 单例
  33. static WindowMgr& Instance(){
  34. static WindowMgr _inst;
  35. return _inst;
  36. }
  37. void SetWindowCnt(int cnt){
  38. std::lock_guard<std::mutex> guard(_mutex);
  39. // 注意哦,这里窗口重建了
  40. if ((size_t)cnt != _windows.size())
  41. {
  42. _windows.clear();
  43. // 延时,扩大rtspc上回调时窗口销毁的概率
  44. std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  45. for (int i = 0; i < cnt; i++)
  46. {
  47. _windows.push_back(std::make_shared<MyWindow>(i));
  48. }
  49. }
  50. }
  51. MyWindow::Ptr GetWindow(int winid){
  52. std::lock_guard<std::mutex> guard(_mutex);
  53. if ((size_t)winid >= _windows.size())
  54. {
  55. return nullptr;
  56. }
  57. return _windows[winid];
  58. }
  59. private:
  60. std::vector<std::shared_ptr<MyWindow> > _windows;
  61. std::mutex _mutex;
  62. };
  63. static void PlayInWindow(int winid, const char *url){
  64. std::weak_ptr<MyWindow> weak = WindowMgr::Instance().GetWindow(winid);
  65. rtsp::Rtspc rtspc(true, [&rtspc, weak](const char *data, int len){
  66. std::shared_ptr<MyWindow> strongPtr = weak.lock();
  67. if (strongPtr == nullptr)
  68. {
  69. printf("window destroyed, exit rtspc\n");
  70. rtspc.Stop();
  71. }
  72. else
  73. {
  74. strongPtr->InputMediaData(data, len);
  75. }
  76. });
  77. rtspc.Run(url, true);
  78. }
  79. static void SetWindowCnt(int winCnt)
  80. {
  81. printf("Now win cnt: %d\n", winCnt);
  82. WindowMgr::Instance().SetWindowCnt(winCnt);
  83. for (int i = 0; i < winCnt; i++)
  84. {
  85. std::thread([i](){
  86. char url[256];
  87. snprintf(url, sizeof(url), "rtsp://192.168.1.2:554/live/chn%d", i);
  88. PlayInWindow(i, url);
  89. }).detach();
  90. }
  91. }
  92. int main(int argc, const char *argv[])
  93. {
  94. int count = 4;
  95. while (true)
  96. {
  97. SetWindowCnt(count);
  98. getchar();
  99. count++;
  100. }
  101. return 0;
  102. }
复制代码

可能还有同学有疑问,如果strongPtr拿到之后,在InputMediaData执行之前,发生了窗口切换怎么办呢?

这完全无须担心,由于我们已经持有了window的强引用,此时它并不会被销毁。只有等我们InputMediaData执行完之后,rtspc的回调函数执行完,strongPtr的生命周期完结,此时智能指针的计数清零,MyWindow才会得到释放。

3,类成员指针

类成员指针,它的重建,需要先delete老的。析构时,也需要析构。

而使用了智能指针,这些工作都不需要做了

如下代码中,智能指针_packer,构造函数中的创建,ChangePacker函数中把它重新赋值,都不需要考虑销毁。因为智能指针会自动析构老的内容。

同时,~Rtsps()析构函数执行时,也不需要手工析构_packer。

  1. /**
  2. * @author LiuFengxiang (20451250@qq.com)
  3. * @brief rtsp 服务端
  4. * @version 0.1
  5. * @date 2023-12-9
  6. *
  7. * @copyright Copyright (c) 2023
  8. *
  9. */
  10. #pragma once
  11. #include <string>
  12. #include <rtp/rtp-pack.h>
  13. namespace rtsp
  14. {
  15. class Rtsps
  16. {
  17. public:
  18. Rtsps(/* args */){
  19. _packer = RtpPack::CreatePacker("H264", [this](const std::string &rtp){
  20. onRtpData(rtp);
  21. });
  22. }
  23. // 注意,如果该类会被继承,则务必将它写成虚函数。否则影响析构
  24. virtual ~Rtsps(){}
  25. /// @brief 更换打包器
  26. /// @param encode 打包器名称
  27. void ChangePacker(const char *encode){
  28. _packer = RtpPack::CreatePacker(encode, [this](const std::string &rtp){
  29. onRtpData(rtp);
  30. });
  31. }
  32. int Run(){
  33. return 0;
  34. }
  35. private:
  36. void onRtpData(const std::string &rtp){
  37. printf("onRtpData\n");
  38. }
  39. private:
  40. // 打包器的句柄。
  41. RtpPack::Ptr _packer;
  42. };
  43. } // namespace rtsp
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行