【c/c++3】类和对象,vector容器,类继承和多态,systemd,std&boost

发布于:2025-07-05 ⋅ 阅读:(18) ⋅ 点赞:(0)


1.类和对象:结构体升级为类,C++中枚举类enum class,c中enum

C++动态内存分配:C语言动态分配内存用malloc(),释放内存用free()。C++中new和delete。
在这里插入图片描述
下面为三种show函数重载实现,如下字符串理论上可定义为char name[10],但函数里字符串也只能传地址,所以只能定义为char * name
在这里插入图片描述
在这里插入图片描述
构造函数:必须public属性,名字和类名相同,没有返回值(void也不允许),不能被显示调用(只能创建对象自动执行)。
在这里插入图片描述
析构函数:一个类只有一个,不允许被重载,清理工作如释放内存。
在这里插入图片描述
C中&取地址,C++中&引用。引用就像起别名=typedef=宏define。
在这里插入图片描述
在这里插入图片描述
C++中string随着存放字符长度自动伸缩,不用担心内存溢出。string类是一个模板类,位于std命名空间,如果不加using namespace std;就要用std::string str。string特性描述函数:int size()返回当前字符串大小int length()返回当前字符串的长度void clear()清空字符串。string本质是一个类,通过动态分配内存实现对字符串的存储,string对象用于存放字符的内存地址是变化的。也就是地址存放的下就不再重新分配,存放不下就重新分配地址。
在这里插入图片描述

2.vector容器:vector与string类一样属于STL

容器的使用:1.存放整数
在这里插入图片描述
访问容器中元素可以像数组形式一样。
在这里插入图片描述
2.存放字符串
在这里插入图片描述
3.存放结构体4.存放类:存放字符串中,string就是类。
在这里插入图片描述
vector其他成员函数:1.定位的函数
在这里插入图片描述
2.增加元素的函数
在这里插入图片描述
3.删除元素的函数
在这里插入图片描述
4.判断容器的大小
bool empty():判断容器是否为空
int size():返回容器中元素的个数
5.作业题:封装随机数
在这里插入图片描述

//此程序用于生成一组随机数, 指定数组范围和是否重复
#include"_public.h"

class CRand
{
public:
	CRand();
	~CRand();
	vector <int> m_val;  //m_val容器	
	bool checkexit(const int aryyval, const int aryysize); // 用于检查是否为重复数据,aryyval为重复的值,这函数不单用,用于Rand成员函数里
	void Rand(const int minvalue,const int maxvalue,bool brep=true, const int nog=5); //brep为是否允许重复; 默认为允许重复,nog指定生成多少个随机数
};

//111111111111111111111111111111111111111111111111111111111111111111111111111111
void CRand::Rand(const int minvalue,const int maxvalue,bool brep,const int nog)
{
	int len = maxvalue-minvalue;
	int ii=0, itmp=0, jtmp=0;  // ii生成第几个,jtmp实际生成共多少个,itmp生成的值
	m_val.clear();
 
	if(brep==true)    // 允许重复
	{
		jtmp = nog;
		for(ii=0;ii<jtmp;ii++)
		{
			itmp = rand()%(len+1)+minvalue; // (0~len)+minvalue,itmp就是min~max之间的值,不是len长度
			m_val.push_back(itmp);    
		}
		return; //return是函数直接返回, 也就是结束该函数。
	//要跳出循环用break, if代码段是不能用break跳出的, 在一个函数内任意位置调用return, 直接退出Rand函数,下面代码不执行。
	}

	jtmp = nog;    // 以下为不允许重复 ,因为没进入if(brep==true)
	if (nog>len) jtmp = len + 1;  // 比如5-1=4,但1到5可以生成5个,所以如果nog大于len的话就取len+1个,前提不允许重复。
	while(1)   
	{
		if (jtmp == m_val.size()) break;  //生成满了跳出循环
		itmp = rand()%(len+1)+minvalue;
		if (ii==0)  // 生成第一个不用管checkexit重不重复
		{
			m_val.push_back(itmp);
			ii++;
			continue;
		} 

		if (checkexit(itmp,ii) == false) continue;  // checkexit为false则不允许重复
		m_val.push_back(itmp); ii++;
	}
	return;
}

//11111111111111111111111111111111111111111111111111111111111111111111111111111
bool CRand::checkexit(const int aryyval, const int aryysize) // aryyval重复的值,aryysize允许多少个重复
{
	for (int ii=0; ii<aryysize; ii++)
	{
		if (aryyval == m_val[ii]) return false;
	}
	return true;
}

CRand::~CRand()
{
	m_val.clear();
}

CRand::CRand()
{
	struct timeval begin;
	gettimeofday(&begin, 0);
	srand(begin.tv_usec);
}

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
int main()   //如何用CRand这个类
{
	CRand CrtRand;
	CrtRand.Rand(0, 10, false);   // 若false为true允许重复,不管范围多少取nog个
	for(int ii=0;ii<CrtRand.m_val.size();ii++)
	{
		printf("%d\n",CrtRand.m_val[ii]);
	}	
	return 0;
}

3.类继承和多态:子类可直接用父类属性和方法

类多态:子类必重写父类纯虚函数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.生成数据:手动执行bin不受systemd管控

4.1 systemd:journalctl等带ctl都属于systemd,systemctl查看所有service状态

linux开机自动启动脚本:/etc/rc.localcrontab rc.d【sudo update-rc.d a.sh defaults 90,90表明一个优先级,越高表示执行的越晚,a.sh在/etc/init.d/里】, systemd【Restart=on failure是失败会继续执行,默认执行5次(同rc.d中sv restart sensor-mon),Type=oneshot是无论怎样就只执行一次】。
在这里插入图片描述
systemd 可以管理所有系统资源,不同的资源统称为Unit,一共分如下12种:systemctl --state failed | catsystemctl list-dependencies --type=service --no-pager
在这里插入图片描述
在这里插入图片描述
target是特殊的unit,它不执行任何实际的操作,作用把一组unit利用各种依赖和包含关系组织起来共同完成某件事情。systemd用目标(target)替代了initd中运行级别的概念:
在这里插入图片描述

# systemctl get-default   # 查询当前默认开机运行的target
multi-user.target
# systemctl cat multi-user.target
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target   # 要求basic.target一起运行
Conflicts=rescue.service rescue.target  # 如果rescue.service或rescue.target正在运行,multi-user.target就不能运行,反之亦然
After=basic.target rescue.service rescue.target
AllowIsolate=yes  # 允许使用systemctl isolate命令切换到multi-user.target

# service-a.service
Requires=service-b.service
After=service-b.service
# service-b.service会在service-a重启时自动重启
Type=simple
ExecStart=/usr/bin/b
Restart=always

# 在/etc/systemd/system/reboot.target.d/refuse.conf中添加:则禁止systemctl start reboot.target
[Unit]
RefuseManualStart=yes
RefuseManualStop=yes

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 crtsurfdata.cpp:字符拆分存入结构体再存入容器

在这里插入图片描述
在这里插入图片描述
如下每星期每月每日每小时的第20分钟到第40分钟每分钟都会执行一次(1小时执行21次)。
在这里插入图片描述
在这里插入图片描述
crontab -e进入如下,dow表示星期。
在这里插入图片描述

// makefile
CC=g++
FLAG=-g
#FLAG=-02
all:crtsurfdata
crtsurfdata:crtsurfdata.cpp _public.h _public.cpp   // _public.h _public.cpp改变也要重新编译
        $(CC) $(FLAG) -o crtsurfdata crtsurfdata.cpp _public.cpp 
        cp crtsurfdata ../bin/.
clean:
        rm -rf crtsurfdata

在这里插入图片描述
在这里插入图片描述

#include "_public.h" // 安徽,58102,亳州,33.47,115.44,39.1,_public.h同目录
// 全国气象 (站点参数stcode) 数据结构
struct st_stcode
{
  char provname[31];   // 省名称
  char obtid[11];      // 站点代码 //也可能是字母所以用字符串表达
  char cityname[31];   // 城市名
  double lat;          // 纬度
  double lon;          // 经度
  double height;       // 海拔高度
};
// 全国气象站点分钟 (生成的观测数据surfdata) 数据结构
struct st_surfdata
{
  char obtid[11];      // 站点代码
  char ddatetime[21];  // 数据时间:格式yyyy-mm-dd hh24:mi:ss。
  int  t;              // 气温:单位,0.1摄氏度
  int  p;              // 气压:0.1百帕 
  int  u;              // 相对湿度,0-100之间的值。
  int  wd;             // 风向,0-360之间的值。
  int  wf;             // 风速:单位0.1m/s
  int  r;              // 降雨量:0.1mm
  int  vis;            // 能见度:0.1米
}; 
vector<struct st_stcode> vstcode;   // 存放全国站点参数的容器  //将结构体放入容器中,就直接操作容器了
vector<struct st_surfdata> vsurfdata;   // 存放全国气象站点分钟观测数据的容器
// 从站点参数文件中加载到vstcode容器中,在函数里字符串只能传地址
bool LoadSTCode(const char *inifile);
// 创建全国气象站点分钟观测数据,存放在vsurfdata容器中
void CrtSurfData();
// 把容器vsurfdata中的全国气象站点分钟观测数据写入文件
bool CrtSurfFile(const char *outpath);
CLogFile logfile;
void EXIT(int sig);

int main(int argc,char *argv[],char *envp[])    //*argv[]:指针的数组,也可以**argv指针的指针
{  
  if (argc!=4)
  {  
    printf("\n本程序用于生成全国气象站点观测的分钟数据。\n");
    printf("/root/qxidc/bin/crtsurfdata 站点参数 数据文件存放的目录 日志文件名\n");
    printf("例如:/root/qxidc/bin/crtsurfdata /root/qxidc/ini/stcode.ini /root/qxidc/data/ftp/surfdata /root/qxidc/log/crtsurfdata.log\n");  //目录自动创建
    return -1;  //程序没有捕获运行结果,所以0,-1无所谓,return -1失败跳出大循环,return 0成功
  }
  // 关闭全部输入输出和信号,也可写成void CloseIOAndSignal(); 下面printf失效
  CloseIOAndSignal();
  // 因为程序运行需要退出,所以捕获退出信号(ctrl+c或kill/kill all),signal将这两个信号值作为sig参数传给EXIT函数
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);  //可以ctrl+c或kill/kill all通知程序退出  
  if (logfile.Open(argv[3],"a+")==false)
  {
    printf("打开日志文件失败(%s)。\n",argv[3]); return -1;
  }
  // 可把while (true)一行和sleep(60)去除,用crontab调度,不能多进程中(在某一进程中关闭指针文件,其他就全部关了不会再打开)
  // 这段代码编出来的crtsurfdata二进制文件运行一次1秒不到就执行完,没必要死循环浪费内存
  while (true)  
  {
    // 从站点参数文件中加载到vstcode容器中,argv[0]计算机从0开始
    if (LoadSTCode(argv[1])==false) { sleep(60); continue; }    
    logfile.Write("加载参数文件(%s)成功!\n",argv[1]);
    CrtSurfData();  // 生成全国气象站点分钟观测数据,存放在vsurfdata容器中
    // 把容器vsurfdata中的全国气象站点分钟观测数据写入文件
    if (CrtSurfFile(argv[2])==false) { sleep(60); continue; }
    sleep(60);  // 进入死循环60s执行一次,自身调度自己
  }
  return 0;
} 

//11111111111111111111.从站点参数文件中加载到vstcode容器中
bool LoadSTCode(const char *inifile)
{
  vstcode.clear();
  CCmdStr CmdStr;  // CmdStr类(_public.h中)切分并暂存字符串
  struct st_stcode stcode;  // st_stcode为数据结构,stcode为结构体变量
  CFile File;
  if (File.Open(inifile,"r") == false)
  {
    logfile.Write("File.Open(%s) 失败。\n",inifile); return false;
  }
  char strbuffer[101];  //101个字节可以存放50个汉字  strbuffer就是stcode.ini站点参数内容
  while (true)
  {
    memset(&stcode,0,sizeof(struct st_stcode));
    // memset(strbuffer,0,sizeof(strbuffer)); 因为Fgets里有初始化,所以这行省去    
    if (File.Fgets(strbuffer,100)==false) break; // 读取一行,在循环里就多行读取,break不能写成return false
    CmdStr.SplitToCmd(strbuffer,",",true); // true为删除空格,CmdStr里存了一行拆分后的片段,循环里就是多行
    CmdStr.GetValue(0, stcode.provname);  //stcode.provname就存了拆分的值,在循环里
    CmdStr.GetValue(1, stcode.obtid);     //GetValue做了重载,有第三个参数取长度
    CmdStr.GetValue(2, stcode.cityname);
    CmdStr.GetValue(3,&stcode.lat);  //double 传地址&
    CmdStr.GetValue(4,&stcode.lon);     
    CmdStr.GetValue(5,&stcode.height);
    vstcode.push_back(stcode);	
	// printf("strbuffer=%s",strbuffer);   //不需写成strbuffer=%s\n,因为文件本身有换行符
	// printf("provname=%s,obtid=%s,lat=%.2lf\n.....",stcode.provname,stcode.obtid,stcode.lat,......);
  }
  return true;
}

//1111111111111111112.创建全国气象站点分钟观测数据,存放在vsurfdata容器中,//Crt是create缩写
void CrtSurfData()  // 返回一定会成功的,所以不需要返回值void
{
  vsurfdata.clear();  // 清空容器
  srand(time(0));     // 播随机数种子  
  char strLocalTime[21];  
  LocalTime(strLocalTime,"yyyy-mm-dd hh24:mi"); // LocalTime3个参数:一:存放时间的字符串。二:取出当前时间用什么格式。三:时间偏移量秒。将memset(strLocalTime,0,sizeof(strLocalTime));放入LocalTime()中
  strcat(strLocalTime,":00"); //不计秒,里面800多行记录时间和文件名时间一样就把秒变成00
  struct st_surfdata stsurfdata;
  for (int ii=0;ii<vstcode.size();ii++)  //根据站点代码个数
  {
    memset(&stsurfdata,0,sizeof(struct st_surfdata)); 
    STRCPY(stsurfdata.obtid,10,vstcode[ii].obtid);  // 站点代码  //STRCPY(目的,长度,源)
    // LocalTime(stsurfdata.ddatetime);
    STRCPY(stsurfdata.ddatetime,20,strLocalTime);   // 数据时间采用当前时间
    stsurfdata.t=rand()%351;       // 气温:单位,0.1摄氏度  // 结构体值 //0-350间整数就行
    stsurfdata.p=rand()%265+10000; // 气压:0.1百帕  //0到264再加10000就是10000到10264
    stsurfdata.u=rand()%100+1;     // 相对湿度,1-100之间的值。
    stsurfdata.wd=rand()%360;      // 风向,0-360之间的值。
    stsurfdata.wf=rand()%150;      // 风速:单位0.1m/s
    stsurfdata.r=rand()%16;        // 降雨量:0.1mm
    stsurfdata.vis=rand()%5001+100000;  // 能见度:0.1米
    vsurfdata.push_back(stsurfdata);
  }
}

//1111111111111111113.把容器vsurfdata中的全国气象站点分钟观测数据写入文件
bool CrtSurfFile(const char *outpath)
{
  CFile File;
  char strLocalTime[21];
  LocalTime(strLocalTime,"yyyymmddhh24miss");
  char strFileName[301]; //目录名+文件名即下行outpath+/SURF_ZH_%s_%d.txt,初始化也在SNPRINTF中
  SNPRINTF(strFileName,300,"%s/SURF_ZH_%s_%d.txt",outpath,strLocalTime,getpid());  // outpath目录名   
  if (File.OpenForRename(strFileName,"w")==false)
  {
    logfile.Write("File.Open(%s) 失败!\n",strFileName); return false; // 不用printf显示到屏幕了
  }
  for (int ii=0;ii<vsurfdata.size();ii++) //装进容器里就是为了这行循环
  {
    // 站点代码,数据时间,气温,气压,相对湿度,风向,风速,降雨量,能见度  //存进容器的是整数,除以小数得小数显示到文件中
    File.Fprintf("%s,%s,%.1f,%.1f,%d,%d,%.1f,%.1f,%.1f\n",\
         vsurfdata[ii].obtid,vsurfdata[ii].ddatetime,vsurfdata[ii].t/10.0,vsurfdata[ii].p/10.0,\
         vsurfdata[ii].u,vsurfdata[ii].wd,vsurfdata[ii].wf/10.0,vsurfdata[ii].r/10.0,vsurfdata[ii].vis/10.0);
  }
  File.CloseAndRename();   // 关闭文件
  logfile.Write("生成数据文件(%s)成功,数据时间=%s,记录数=%d!\n\n",strFileName,vsurfdata[0].ddatetime,vsurfdata.size()); 
  // 数据时间=和文件名时间一样就是最后秒改为00(即文件里839行【vsurfdata.size()行】记录时间)
  vstcode.clear(); vsurfdata.clear(); //这里不清空容器,记录数=839,1678....(除非每次运行都重新make,不然要清空容器)
  return true;
}
void EXIT(int sig)
{
  logfile.Write("程序退出,sig=%d\n\n",sig);  // killall crtsurfdata ,.log日志文件中记录了:时间..程序退出,sig=15
  exit(0); //exit(0)程序退出自然会调用logfile的析构函数,所以这里不需要调用
}

在这里插入图片描述

5.std&boost:下载boost库解压后执行编译和install脚本,一般.so或.a自动装在/usr/local/lib下

在这里插入图片描述

#include <iostream>
#include <string>
#include <utility>
std::pair<int, int> divide(int dividend, int divisor) {
    int quotient = dividend / divisor; // 除数
    int remainder = dividend % divisor;  // 余数
    return std::make_pair(quotient, remainder);
}
int main() {
    std::pair<int, int> result = divide(10, 3);
    std::cout << "Quotient: " << result.first << ", Remainder: " << result.second << std::endl;  // Quotient: 3, Remainder: 1
    return 0;
}
int main() {
    std::string str1 = "Hello";
    std::string str2 = std::move(str1);
    std::cout << "str1: " << str1 << std::endl;  // 此时 str1 的内容已经被移动,为空
    std::cout << "str2: " << str2 << std::endl;  // 输出 "Hello"
    return 0;
}
#include <iostream>
#define INFO_LOG Logger<true>(std::cout)  // true为false时,日志代码在编译期被完全移除
// Logger 类的构造函数需要一个 std::ostream&(输出流引用)参数,std::cout是标准输出流的全局实例,代表控制台输出
template <bool active>
struct Logger
{
    std::ostream& stream;
    Logger(std::ostream& _stream) : stream(_stream)
    {
        if constexpr (active)  // true
        {
            stream << std::dec; // 强制设为十进制格式,C++流对象(如 std::cout)保留格式状态(如十六进制、八进制等)
        }
    }
    template <typename T>   // 模板参数支持任意数据类型
    Logger& operator<<(const T& value)
    {
        if constexpr (active)
        {
            stream << value;   // 实际输出
        }
        return *this;
    }
    ~Logger()
    {
        if constexpr (active)
        {
            stream << std::endl; // 输出换行并刷新缓冲区
        }
    }
};
int main() {
    INFO_LOG << "Starting SPD ownership";  // 串口打印
    INFO_LOG << std::hex << 255;  // 输出 "255" 而不是 "ff"(构造函数中的std::dec会重置格式)
}
int main() {
    std::cout << "222" << std::endl;
}
void a()
{
    std::cout << "111" << std::endl;
}
void a() __attribute__((constructor));  // main函数之前执行

$ g++ a.cpp -o a
$ ./a
111
222
int main(int argc, char **argv) {
    static std::deque<bool>liquidStatus = {true, true, true};
    int status = 0; // 如下0 false,1 true
    bool nowLiquidStatusFlag = status ? true : false;
    std::cout << "nowLiquidStatusFlag is : " << std::boolalpha << nowLiquidStatusFlag << std::endl;
    liquidStatus.push_back(nowLiquidStatusFlag);
    liquidStatus.pop_front();  // 移除队列最前面的元素

    std::cout << "Current liquidStatus: ";
    for (bool val : liquidStatus) {
        std::cout << val << " "; // 最后空格
    }
    std::cout << std::endl; // 换行

    uint8_t data = 0x1c;
    if (data & 0x10) // bit4=1,不用>>4,非0进入
    {
        std::cout << "val非0" << std::endl;
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述
指针对内存的访问,肯定对应一段内存空间,引用是别名,指针不用初始化,引用必须初始化。引用不能为空。指针可以赋不同的值,引用只能绑定一个变量
在这里插入图片描述
string_view对字符串的处理,不用把字符串拷贝,直接赋值就行。调用dbus获取property时是字符串类型还是uint8/16等都可以用any或variant,不会崩溃。

智能指针将new和delete自动化实现,自动释放指针资源,shared_ptr共享指针。
在这里插入图片描述
左值:在内存有分配空间,可以取地址,在等于号左边,不是临时变量

如字符串常量就是右值

move能将左值转为右值,将左值中的值取出来,内存空间不会去使用,左值中的值也不存在了,这样就不用复制开辟一个新内存,如果不加move(左值),可能要开辟新内存用拷贝构造函数,现在用移动构造函数,只取值,不用深拷贝,不用新开辟内存,把值赋到新内存上,不消耗性能


网站公告

今日签到

点亮在社区的每一天
去签到