【C++】多文件的代码规范

news2025/4/28 15:28:15

算是C嘎嘎入门教程(但至少需要知道HelloWorld怎么写
内容不能说全,因为是想到哪写到哪,再次就是C++是真的很杂。
(写完后博文编辑器提示我,本文章可能要20分钟读完,做好心理准备…



1、头文件(*.h)、源文件(*.cpp)

  • 头文件不参与编译,头文件的最大作用是简化/规范代码。一大段代码可以移动到“XXX.h”中,然后那大段代码的位置可以简单一个#include"XXX.h"代替。通常情况下头文件仅可包含函数声明、变量声明、类/结构体的声明或定义。
  • 源文件参与编译,无法通过编译那一般是某名称未声明/未定义(或者过定义/重复定义),一般IDE(例如VSCode)能给你指出这些语义错误。
  • 绝对不能#include"XXX.cpp",哪怕目前能跑,下一步指不定瘸给你看。迫不及待想看是怎么瘸的?跳转到4.2,但还是建议顺序阅读(毕竟文章内容我进行过调整的


2、声明与定义

  • 声明可以重复无数次,但定义只能一次(不能多也不能少),无论是变量、函数、类、结构体。
  • C++的定义具有声明效果。
  • 如果变量有初始化那么肯定是定义,不可能是声明。例如extern int num;extern int num=3;,前者是声明,后者是定义;也例如struct Item;struct Item{int num=5;};,前者是声明,后者妥妥的是定义。
  • 仅有声明而没有定义的类/结构体是无法实例化的(也就是无法创建对象),会在编译阶段出现报错。
  • 仅有声明而没有定义(或未能寻找到定义)的变量/函数,会在链接阶段出现报错。

2.1、以下表格展示声明和定义的区别:

说明代码截图
裸奔(不带static/extern修饰)的是定义,并且会被赋予默认初值代码截图-1.1
static修饰的变量是定义代码截图-1.2
extern修饰的变量是声明。
但如果对变量赋初值那么为是定义(因为初始化)
代码截图-1.3
声明/创建类型:class/struct代码截图-1.4
创建类型:typedef代码截图-1.5
声明/创建函数代码截图-1.6

2.2、以下表格展示仅有声明没有定义时的报错:

说明代码截图
变量代码截图-1.7
函数代码截图-1.8代码截图-1.8-1代码截图-1.8-2
类/结构体代码截图-1.9


3、staticextern

这俩修饰词可以作用于变量和函数,而对类class和结构体struct来说将不起作用的,不起效的原因说来话长,这里展开会跑题。

3.1、staticextern的作用:

作用域externstatic<无>
全局[定义]:变量/函数可供其他cpp文件访问
[声明]:可访问其他cpp文件的变量/函数
[定义]变量/函数仅供本cpp文件使用(可有效防止污染命名空间
[声明]仅用于函数,声明一个静态函数
(等同extern)
局部[声明]:访问全局变量/函数。实际没啥用处,但可以起一个规范作用(强调某东西是全局的[定义]:创建静态局部变量,创建行为只会执行一次并且变量会长久滞留内存中直到程序结束[定义]:创建局部变量,仅供本作用域使用

特别的,在类中使用static修饰的静态成员变量仅仅是起到“声明”的效果,还要对该变量进行定义。因为这是类内的,在此不展开说明。类内搞特殊的语法多了去了,全指出来可是会死人的,除了static之外还有virtualfinalexplictoperatorconstmutable等关键词。


3.2、staticextern的应用:

多cpp文件下它们的作用尤为重要:

  1. extern确保函数/全局变量定义在指定cpp文件,防止出现重复定义;
  2. static保护变量/函数不受其他cpp文件的干扰,同时也不污染全局命名空间(有效避免无意中造成的重定义);
  3. extern用的比较多的情况下是全局声明(也就是放在函数体外),放在函数体内的局部声明更像是起到一种强调作用;
  4. extern在声明/定义函数时可省略(有和没有都一个样)。

以下为样例代码和运行结果:

//T1_Main.cpp
extern int num;
extern void Func_1(int);
extern void Func_2(int);

int main() {
	num = 100;
	Func_1(num);
	Func_2(num);

	//extern void Func_0();//Func_0是T1_Extra-2.cpp下定义的静态函数,
	//Func_0();//取消这两行注释后IDE会提示Func_0未定义(因为静态函数不对外
	return 0;
}
//T1_Extra-1.cpp
#include<stdio.h>

int num = 10;
void Func_1(int val) {
	printf_s("%d,%d\n",num,val);
}
//T1_Extra-2.cpp
#include<stdio.h>

static int num = 10;
extern void Func_1(int);

static void Func_1(int val) {
	return;
}

void Func_2(int val) {
	Func_1(777);//可以看到这里并没有调用外部文件的Func_1,而是本文件下的静态函数Func_1。说明本文件下的静态函数优先级更高
	printf_s("%d,%d\n", num, val);
}

static void Func_0() {//本文件下的静态函数不对外,也就是其他cpp文件是没法访问到这里的函数Func_0
	printf_s("!!!\n");
}

代码截图-2.1



4、#include

4.1、用简单但离谱的代码来展示#include的作用:

//T2_Extra.h
printf_s("%d\n", 555);
//T2_Main.cpp
#include<stdio.h>
int main() {
#include "T2_Extra.h"
	return 0;
}

这份代码等同于:

#include<stdio.h>
int main() {
printf_s("%d\n", 555);
	return 0;
}

#include的作用就是简单粗暴的文本替换罢了,没什么特别的。


4.2、#include一个cpp文件的问题:

为什么我要放在这里才讲#include的作用,那是因为只有这样才能清晰地表述#include"XXX.cpp"这种行为到底有多蠢。

//T3_Main.cpp
#include<stdio.h>
#include"T3_Extra.cpp"

int main() {
	printf_s("%d\n", Func(100));
	return 0;
}
//T3_Extra.cpp
int Func(int val) {
	return val * val;
}

代码截图-2.3-1
结合我上面提到的要点,这里的重定义不是显而易见?


4.3、硬要#include"XXX.cpp"一条路走到黑:

static解君愁(暂时的),仅需在T3_Extra.cpp的Func定义前添加一个static关键词,即:

//T3_Extra.cpp
static int Func(int val) {
	return val * val;
}

T3_Main.cpp保持不变,然后代码能成功运行:
代码截图-2.3-2
只不过很显然,这种行为将会给每个include了cpp文件的源代码的编译工作造成不小的压力,因为这会创建相当多的静态变量/静态函数(直观影响就是程序文件增大),而且我还没提到include重复包含、交错包含的情况(这更会引起重定义问题)。



5、structclass

这俩可以创建对应的数据结构,只不过在缺乏定义的情况下类和结构体是无法实例化的。(小小声补充,虽然没提到union(联合体)enum(枚举类)enum class(强枚举类)但其实它们也和类/结构体是一样的,无定义便无法使用。

多个cpp文件,同时定义同名类/结构体是不会引起冲突的,除了上面提到的“无定义就无法实例化,也就无法使用”的原因外,还有个原因就是C/C++底层的实现逻辑并不在乎这个问题,它会全部转为指针+偏移量。这里我不知道怎么组织语言进行说明,反正可以理解为:定义的类/结构体仅在本cpp文件下生效,就如同被强制赋予了static属性

有人就问了,“既然仅在本cpp文件生效,那么它们是怎么互相联动的”,这是个好问题,以至于我要用多个示例代码进行说明。


5.1、内联函数inline

可以看这篇文章:[CSDN]C++内联函数,这博文非常清楚地说明了内联函数的作用和底层实现。
这里用简单粗暴的方式来说明:内联函数有static的特性,它只在本cpp文件下生效。

为什么我要提内联函数呢?这不是离题了吗,不不不,当你在类定义里头定义成员函数时,这个函数就已经隐式成为了内联函数(也就是它和类定义成为一体,都仅在本cpp文件下生效)。
顺带一提,没有所谓的“内联函数声明”,说白了就是inline修饰函数声明是不会生效的,inline用于修饰函数定义。

以下代码展示“inline函数不对外”的特点:

//T4_Main.cpp
#include<stdio.h>

extern int Func(int);
int main() {
	printf_s("%d\n", Func(100));
	return 0;
}
//T4_Extra.cpp
inline int Func(int val) {
	return val*val;
}

代码截图-2.4

当你将类成员函数的定义移出类声明,但又忘了删掉IDE“智能”添加的inline的话,就会出现成员函数未定义的问题,原因同上。
这里也给一份代码作为参考:

//T5_Main.cpp
#include"T5_Extra.h"

int main() {
	Test t;
	t.Print();
}
//T5_Extra.h
#pragma once

class Test {
public:
	void Print();
};
//T5_Extra.cpp
#include"T5_Extra.h"
#include<stdio.h>

inline void Test::Print() {//删去多余inline,或是将该函数定义放置于头文件T5_Extra.h中,亦或是将函数定义放于类声明内
	printf_s("Hello World!\n");
}

5.2、访问类/结构体成员本质上是通过指针+偏移量进行的

可以看这篇文章[知乎问答]C语言结构体的底层原理,亦或是看下面的示例代码:

//T6_Main.cpp
struct Test {
	int num_1;
	char mid;
	int num_2;
};

#include<stdio.h>
int main() {
	Test t{ 100,'M',200 };
	int* val_d1, * val_d2;
	int* val_p1, * val_p2;

	val_d1 = &t.num_1;
	val_d2 = &t.num_2;

	char* ptr = (char*)&t;//使用指针。char类型可以视作byte类型,方便以1字节为单位进行偏移
	val_p1 = (int*)(ptr + 0);
	val_p2 = (int*)(ptr + 8);//为什么这里是+8而不是+5,我也忘了,好像是跟“内存对齐”有关

	printf_s("%d,%d\n", *val_d1, *val_d2);
	printf_s("%d,%d\n", *val_p1, *val_p2);
	printf_s("%d,%d\n", val_d1 == val_p1, val_d2 == val_p2);
}

代码截图-2.6
这也就说明底层实现压根不关心类/结构体长什么样的,编译器在编译时并不会把类/结构体的定义存进像是符号表之类的地方,而是将其全部转换转换为指针+偏移的样子,而与之做对比的是函数和变量,函数定义将会存放到程序代码段中,变量则是在数据段中。


5.3、类/结构体定义的本质

类/结构体的定义与其说是定义,倒不如说是一种对变量的解释。
在5.2中已经提到,编译器编译时会将类/结构体访问的成员全部用指针+偏移量的形式表示。

有了解过强制类型转换吗?就是那个意思。我可以以强制转换的方式,对一个变量采取完全不同的解释方式访问其内部数据(而这有着程序运行异常的风险)。

下面代码与浮点数底层存储有关,与文章[知乎专栏]IEEE754标准: 一 , 浮点数在内存中的存储方式一起食用效果更佳。

//T9_Main.cpp
#include<stdio.h>

struct Float {//使用了位域,将浮点数32位拆成3个部分,详见IEEE754浮点数存储
	int fraction : 23;
	int exponent : 8;
	int sign : 1;
};


void Explain_F(float val) {
	Float* f = (Float*)&val;//强制转换,但转换的是指针(因为要访问底层数据)
	int sign = f->sign & 0x1;
	int exponent = (f->exponent )& 0xFF;
	int fraction = (f->fraction)&0x7FFFFF;
	printf_s("%.3f  %u %d %u \n",val, sign,exponent,fraction);
}

int main() {
	for (auto i = 0; i < 10; ++i) {
		float val = i + i/10.0;
		Explain_F(val);
		//访问https://www.h-schmidt.net/FloatConverter/IEEE754.html
		//检验结果是否一致
	}
	return 0;
}

在这个网站https://www.h-schmidt.net/FloatConverter/IEEE754.html中比较运行结果,可以发现是一致的
代码截图-2.9

所以定义一个类/结构体,然后生成实例化对象,本质上就是创建一个有着一定大小的变量罢了,而变量内部数据如何访问,取决于这个变量所对应的类/结构体的定义。
至于类成员函数则是另一套做法,具体可以看这篇文章[CSDN]C++中类所占的内存大小以及成员函数的存储位置,我这里就不做过多解释。


5.4、定义类静态成员变量

在3.1中提到了一点:在类中使用static修饰的静态成员变量仅仅是起到“声明”的效果,还要对该变量进行定义。
这里不过多进行解释,给一份样例代码进行参考:

//T8_Main.cpp
#include"T8_Extra.h"
#include<stdio.h>
int main() {
	printf_s("%d\n",Test::num);
	return 0;
}
//T8_Extra.h
class Test {
public:
	static int num;
};
//T8_Extra.cpp
#include"T8_Extra.h"
int Test::num = 777;//定义类静态成员变量,哪怕这个静态成员变量是private也一样


6、头文件规范

规范的头文件应该只包含声明,不应包含函数/变量定义(除了类和结构体)。只不过在特殊场合下的确可以往头文件中写入定义以供多个cpp文件使用,但也仅是特殊场合(不建议在不熟练C++的情况下搞特殊)。
问我什么是声明?跳回去看

这里放一份超杂大杂烩代码以及运行结果作为参考(其实这代码写到后面都不知道自己在干啥,作用性不强,完全作为示例罢了):

//Main.cpp
//【主程序】
#include"Integer.h"
#include"Utility.h"

int main() {
	Integer n1(1);
	Integer n2(3);
	Integer n3(5);
	printf_s("Create: %d,%d,%d\n",n1,n2,n3);
	printf_s("Sum: %d\n",Integer::Get_Sum());
	printf_s("Max: %d\n",Integer::Get_Max());
	Print_Enter();

	Integer n4(7);
	Integer n5(9);
	printf_s("Create: %d,%d\n", n4, n5);
	printf_s("Sum: %d\n", Integer::Get_Sum());
	printf_s("Max: %d\n", Integer::Get_Max());
	Print_Enter();

	int val = 100;
	printf_s("SetVal: %d -> %d\n",n4,val);
	n4.Set_Val(val);
	printf_s("Sum: %d\n", Integer::Get_Sum());
	printf_s("Max: %d\n", Integer::Get_Max());
	Print_Enter();

	return 0;
}
//Integer.h
//【写着搞笑的整型类】
#pragma once

class Integer {//一个没啥用的类,写着搞笑的
public:
	Integer(int val);//初始化时传入整数值
	~Integer();
	operator int();//隐式类型转换
public:
	int Get_Val();//获取值
	void Set_Val(int val);//设置值
	static int Get_Sum();//获取所有Integer对象的值之和
	static int Get_Max();//获取所有Integer对象中的最大值

private:
	int __val;//整数值
	static int __sum;//所有Integer对象的值之和
};


//——————————————————————————————————————————————————————

inline int Integer::Get_Val(){//【内联函数】获取记录的整数值
	return this->__val;
}
//Utility.h
//【通用函数】
#pragma once
#include<vector>
#include<stdio.h>

extern void Print_Enter(int count=1);//【外部函数】简单输出个换行符
template<typename T>
int Search(std::vector<T>lst,T target);//【模板函数】对已升序排序的列表lst以二分查找算法查找target的索引位置


//——————————————————————————————————————————————————————

template<typename T>
int Search(std::vector<T>lst, T target) {//模板函数的定义比较特殊,它只能落于头文件中。原因我忘了,感兴趣的可以自己去查
	int L = 0;
	int R = lst.size();
	int M = (L + R) / 2;
	while (L+1 < R) {
		int mid = lst[M];
		if (mid== target)//找到目标
			return M;
		if (mid < target)
			L = M;
		else
			R = M;
		M = (L + R) / 2;
	}
	return R;
}
//Integer.cpp
#include "Integer.h"
#include"Utility.h"
#include<vector>

static std::vector<int>__record;//【静态数据,仅供本cpp使用】记录Integer对象的值(升序排序),以便查找最大值
int Integer::__sum = 0;//【类静态数据成员的初始化】这可没有错,变量定义总该要落到某个cpp文件中,哪怕这个数据成员是private也一样

static void Insert(int val);//【函数声明】向__record中插入数值
static void Erase(int val);//【函数声明】移除__record中的数值


Integer::Integer(int val) {
	Insert(val);
	this->__val = val;
	this->__sum += val;
}

Integer::~Integer() {
	Erase(this->__val);
	this->__sum -= this->__val;
}

void Integer::Set_Val(int val) {
	Erase(this->__val);
	Insert(val);
	Integer::__sum += val - this->__val;
	this->__val = val;
}

int Integer::Get_Max() {
	return __record.back();
}

int Integer::Get_Sum() {
	return Integer::__sum;
}

Integer::operator int(){
	return this->Get_Val();
}


//——————————————————————————————————————————————————————

static void Insert(int val) {//【静态函数,仅供本cpp使用】向__record中插入数值
	int pos = Search(__record, val);
	auto iter = __record.begin() + pos;
	__record.insert(iter, val);
}
static void Erase(int val) {//【静态函数,仅供本cpp使用】移除__record中的数值
	int pos = Search(__record, val);
	auto iter = __record.begin() + pos;
	if (*iter == val)
		__record.erase(iter);
}
//Utility.cpp
#include"Utility.h"

void Print_Enter(int count){
	do {
		printf_s("\n");
	} while (--count>0);
}

代码截图-3.1

这一盆大杂烩代码涉及的东西很多,(对新手不友好):

  • 外部函数extern
  • 静态函数static
  • 内联函数inline
  • 模板函数template
  • 类型转换函数operator XXX()
  • 静态变量定义
  • 类静态成员函数的声明与定义
  • 类静态成员变量的声明与定义


参考:

  • C++静态局部变量:[CSDN]https://blog.csdn.net/weixin_44470443/article/details/104503759
  • C语言结构体的底层原理:[知乎问答]https://www.zhihu.com/question/563145415/answer/2734765913
  • C++类成员函数:[菜鸟教程]https://www.runoob.com/cplusplus/cpp-class-member-functions.html
  • C++中类所占的内存大小以及成员函数的存储位置:[CSDN]https://blog.csdn.net/luolaihua2018/article/details/110736211
  • C++对象调用成员函数的底层实现:[知乎问答]https://www.zhihu.com/question/31265363/answer/55045589
  • C++内联函数:[CSDN]https://blog.csdn.net/Hello_World_213/article/details/125854669
  • C++ 中的 inline 用法:[菜鸟教程]:https://www.runoob.com/w3cnote/cpp-inline-usage.html
  • 标准C++关键字:[MSDN]https://learn.microsoft.com/zh-cn/cpp/cpp/keywords-cpp?view=msvc-170#standard-c-keywords
  • C++隐式类型转换:[博客园]https://www.cnblogs.com/apocelipes/p/14415033.html#用户自定义转换
  • 用户定义的C++类型转换:[MSDN]https://learn.microsoft.com/zh-cn/cpp/cpp/user-defined-type-conversions-cpp?view=msvc-170#ConvFunc
  • IEEE754标准: 一 , 浮点数在内存中的存储方式:[知乎专栏]https://zhuanlan.zhihu.com/p/343033661
  • IEEE754浮点数转换工具:https://www.h-schmidt.net/FloatConverter/IEEE754.html
  • printf_s格式化输出:[MSDN]https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=msvc-170#type-field-characters

以上出现的代码上传至github:https://github.com/Ls-Jan/CPP_Grammar/tree/main
未经本人同意不得私自转载,本文章发布于CSDN:https://blog.csdn.net/weixin_44733774/article/details/134163900

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1179581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

bin.zip和bin.tar.gz以及src.zip和src.tar.gz以及rpm和dmg的区别

下载JDK时Java Downloads | Oracle会有很多文件&#xff0c;时间长了容易混淆&#xff0c;在此记录一下。 如上面三张图所示: bin代表二进制文件&#xff0c;是编译后的文件&#xff0c;而src是源码。.tar.gz是linux的压缩包&#xff0c;.zip是windows的压缩包 所以: bin.ta…

11.6哈夫曼树

创建哈夫曼树 经过这一步后&#xff0c;树的集合里就有n个叶子结点 不断从树集合里取出两个权重最小的树合并成一个新树&#xff0c;这时候就是两个根节点并成兄弟到一个新的根节点下&#xff0c;这个新的根节点的权重是两个兄弟的权重和&#xff0c;之后再把 每次合并的时…

32岁华为员工合同不续约,赔偿N+1,额外3个月年终奖,加班费10万+,总共25万多...

各位中生代社区的读者大大们好&#xff0c;我是你们的老朋友大白:) 一直以来&#xff0c;IT行业都有着“程序员是吃青春饭”的说法&#xff0c;这一年龄危机甚至逐渐演变为“45岁退休&#xff0c;35岁换人”的段子。 作为国内知名互联网大厂&#xff0c;华为近几年就曾几次三番…

Bash脚本实现Linux开机自启Redis,Nginx,MySQL等服务

一. MySQL服务自启 在CentOS 7及以上版本&#xff0c;MySQL以服务形式进行启动&#xff0c;运行两行命令即可实现。 systemctl start mysqld systemctl enable mysqld 只需运行一次即可实现MySQL开机自启动 二. 编写bash脚本 使用bash命令编写脚本实现 #!/bin/bashfunction …

linux下实现电脑开机后软件自启动

实现linux的软件自启动&#xff0c;需要四个文件 第一个【displayScreen.desktop】文件&#xff0c;.desktop文件就是一个用来运行程序的快捷方式,也叫启动器&#xff0c;常用来自启动用的文件&#xff0c;内容如下 [Desktop Entry] #要执行的脚本位置 Exec/home/yicaobao/te…

19.7 Boost Asio 传输序列化数据

序列化和反序列化是指将数据结构或对象转换为一组字节&#xff0c;以便在需要时可以将其存储在磁盘上或通过网络传输&#xff0c;并且可以在需要时重新创建原始对象或数据结构。 序列化是将内存中的对象转换为字节的过程。在序列化期间&#xff0c;对象的状态被编码为一组字节…

串台 fit4life | 催吐辟谷减肥药,你没看错这就是我们仨的黑历史!

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 运动博主/食品博士/营养师居然也都曾有程度不同的进食障碍&#xff1f;&#xff01; 是&#xff0c;这没…

笔记本电脑 禁用/启用 自带键盘

现在无论办公还是生活 很多人都会选择笔记本电脑 但很多人喜欢机械键盘 或者 用一些外接键盘 但是很多时候我们想操作 会碰到笔记本原来的键盘导致错误操作 那么 我们就需要将笔记本原来的键盘禁用掉 我们先以管理员身份运行命令窗口 然后 有两个命令 禁用默认键盘 sc conf…

Apex的addError()显示的消息中实现换行

直接用‘<br/>’是无效的&#xff0c;因为addError默认不转义HTML符号&#xff0c;如果需要转义&#xff0c;应该将第二个参数escape设置为false。不过即使设置了也只对classic页面生效&#xff0c;lightning页面还是无法转义。 官方文档&#xff1a; 参考资料&#xf…

QQ邮箱批量发送

场景 已有用户邮箱,需要批量对他们发送一些广告信息。 完整代码 # coding=gbk import smtplib import csv from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipartdef send_email(msg_from, passwd, msg_to_list, text_content)

服务器数据恢复—误还原虚拟机快照后如何恢复之前的数据?

服务器数据恢复环境&#xff1a; vmfs文件系统&#xff0c;存放的是SqlServer数据库及其他办公文件。 服务器故障&#xff1a; 工作人员误操作还原快照&#xff0c;导致了SqlServer数据库数据丢失。 服务器数据恢复过程&#xff1a; 1、拿到故障服务器的所有磁盘后&#xff0c…

【Linux】拓展:运维面试题,进程管理常见的7大问题

目录 一、如何判断一个程序是单线程还是多线程 二、僵尸进程是什么&#xff0c;有什么危害&#xff0c;如何解决 三、如何找回删掉的文件 四、删除文件以后&#xff0c;空间不释放 五、遇到一个病毒&#xff08;如死循环病毒&#xff09;&#xff0c;解决思路 六、机器开机…

Pytorch里面参数更新前为什么要梯度手动置为0?

因为在一般情况下&#xff0c;每次minibatch之后&#xff0c;都会计算得到一个loss&#xff0c;进而计算该loss关于全局参数的梯度。如果在下一次minibatch 进入模型&#xff0c;计算得到相应的loss和梯度之前&#xff0c;不对优化器的梯度进行置0操作&#xff0c;那么几次batc…

生成式人工智能的“经济学”,The Economic Case for Generative AI#a16z

a16z召集了行业精英&#xff0c;为我们带来了有关生成式AI的洞察。在创造力方面&#xff0c;生成式AI带来了3-4倍量级的成本优势&#xff0c;更多新的需求将诞生。 AI REVOLUTION The Economic Case for Generative AI Martin Casado MixCopilot 大家好&#xff0c;欢迎来到本期…

年营收增长30%起!武汉迪赛威用飞桨助推智慧执法

智慧执法已成为建设文明城市不可忽视的环节。而“智慧执法”这四个字背后&#xff0c;却是社会治理中无穷无尽的需要考虑和衡量的微小细节。有时候&#xff0c;仅仅是一个环节的改进&#xff0c;就能节省极大的人力物力财力。 例如&#xff0c;“随身物品智能识物终端”&#x…

Amazon Fargate使用Seekable OCI 实现更快的容器启动速度

前言 虽然在部署和扩展应用程序时&#xff0c;使用容器进行开发的方式已日趋流行&#xff0c;但仍有一些领域可以改进。扩展容器化应用程序的主要问题之一是启动时间长&#xff0c;尤其是在纵向扩展期间&#xff0c;需要添加较新的实例。此问题可能会对客户体验&#xff08;例如…

python图像处理 —— 实现图像滤镜效果

python图像处理 —— 实现图像滤镜效果 前言一、浮雕二、素描三、怀旧四、水彩画五、水波六、卡通七、流年八、美颜完整代码 前言 随着数字图像处理技术的不断发展&#xff0c;越来越多的人开始关注图像滤镜的应用。其中&#xff0c;使用Python的Opencv库实现图像滤镜效果成为…

零门槛,不等待!立刻领取 Embedding API 密钥及 1 万免费 tokens!

2023 年 10 月 30 号&#xff0c;Jina AI 正式发布了 jina-embeddings-v2&#xff0c;是全球首个唯一支持 8K&#xff08;8192&#xff09;输入长度的开源向量大模型&#xff0c;今天&#xff0c;我们趁热打铁&#xff0c;为企业和开发者提供 Embedding API&#xff0c;即插即用…

Rabbit的高可用机制

RabbitMQ是一个消息中间件&#xff0c;提供了多种高可用机制来确保系统在出现故障时仍能保持可用性。以下是RabbitMQ的一些高可用机制&#xff1a; 镜像队列&#xff08;Mirrored Queues&#xff09;&#xff1a; 作用&#xff1a; 镜像队列可以在集群中复制队列的消息到多个节…

SpringBoot整合定时任务遇到的多实例问题

唠嗑部分 是这样&#xff0c;前几日完善了定时任务的日志记录&#xff0c;今日切换了服务器&#xff0c;多部署了一个节点&#xff0c;使用nginx负载均衡&#xff0c;但是查看日志却发现了如下情况 那糟糕了&#xff0c;传说中的多实例问题出现了&#xff0c;今天我们就来聊聊…