当前位置: 首页 > news >正文

C++类和对象中(构造函数,析构函数,拷贝构造函数)详解

C++类和对象中[构造函数,析构函数,拷贝构造函数]详解

  • 一.前言
    • 1.类的6个默认成员函数
  • 二.构造函数
    • 1.构造函数的引出
    • 2.无参构造函数
    • 3.缺省参数在构造函数中的应用
    • 4.编译器实现的默认构造函数
    • 5.广义的默认构造函数
    • 6.默认构造函数的形成规则
  • 三.析构函数
    • 1.析构函数的语法
    • 2.编译器实现的默认析构函数
  • 四.拷贝构造函数
    • 1.拷贝构造函数的引出
      • 1.浅拷贝的局限性
    • 2.拷贝构造函数语法形式
      • 1.浅拷贝的另一大坏处:程序死递归(以date类为例引出)
      • 2.日期类的拷贝构造函数的实现
      • 3.Stack类的拷贝构造函数的实现
        • 1.思路
        • 2.具体实现
      • 4.指针作为拷贝构造函数的参数
      • 5.const Date& d1
    • 3.拷贝构造函数的"传递性"
    • 4.拷贝构造函数典型调用场景
    • 5.拷贝构造函数的总结

一.前言

1.类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,当一个类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
在这里插入图片描述
这篇博客我们重点介绍构造函数和析构函数还有拷贝构造函数
这里请大家先建立一个观念:
构造函数和析构函数是函数中的贵族,我们不能用普通函数的视角去看待它们,而拷贝构造函数是构造函数中的一种
它们是非常独特的函数

二.构造函数

1.构造函数的引出

在这里插入图片描述
注意:构造函数虽然叫做构造函数,但是它的任务并不是开辟空间去创建一个对象,而是初始化一个对象
在这里插入图片描述
在这里插入图片描述
这里的构造函数是:

Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}

注意:构造函数没有返回值,不写返回值类型,不写void

你发现没有,这个Date的构造函数跟Init的实现一样啊,这也就说明了构造函数的功能就是初始化对象,不是开辟空间创建一个对象

那它为什么不叫作初始化函数呢?C++创始人就是这么定义的,没有为什么

构造函数是自动调用的,不过我们在构造函数中写了三个形参,所以即使构造函数是自动调用的,我们也要去传参啊,怎么传参呢?

C++语法这么规定:在创建对象的时候直接一起传参

Date d1(2023,10,20);

2.无参构造函数

这不还是需要传参吗?
如果我在创建对象的时候都忘了传参呢?
因为构造函数是支持重载的
所以我们可以再去定义一个无参构造函数
在这里插入图片描述

Date()
{_year = 1;_month = 2;_day = 3;
}
调用:Date d2;

注意:调用无参构造函数的时候不能加()
也就是不允许这样去调用:
Date d2();

为什么呢?
因为这行代码也可以被认为是一个函数的声明,这个函数的函数名是d2,函数的返回值类型是Date类型,函数没有任何形参
所以会产生歧义

因此C++创始人就规定不要加括号,直接Date d2;就行

3.缺省参数在构造函数中的应用

还是有点麻烦啊,我想去随心所欲地创建一个对象
就像这样:我还要定义这么两个构造函数,
能不能就只定义一个构造函数,而且还能让我更加随心所欲地去传参呢?

int main()
{Date d1(2023, 10, 20);d1.Print();Date d2;d2.Print();return 0;
}

答案是:当然可以了,我们之前学的缺省参数就派上用场了
然后我们写完之后迫不及待地去运行,结果:报错…
在这里插入图片描述
为什么呢?
在这里插入图片描述
两个报错,
1.其实这个全缺省参数的构造函数跟

Date(int year,int month,int day);

无法构成重载,(因为函数名相同,参数的个数,类型均相同)
2.这个全缺省参数的构造函数尽管与无参构造函数形成了重载,但是当我们无参调用的时候就会发生歧义,因此这两个构造函数也不能同时存在
在这里插入图片描述
不错,达到了我们的要求

但是有一个问题:当我们不写构造函数的时候会发生什么呢?

4.编译器实现的默认构造函数

这里所提到的默认构造函数是编译器为我们实现的默认构造函数(大家可以理解为这里是狭义的默认构造函数,后面我们会提到广义的默认构造函数)
在这里插入图片描述
编译器会给我们实现一个默认构造函数,不过它会给我们初始化为随机值,
那我要它有什么用?
别急,我们先来看一个代码
在这里插入图片描述
这不还是没有什么好的初始化吗?
还是随机值啊
大家不要急吗,下面才是重头戏
我们先给Stack这个类写上我们定义的构造函数

Stack(int capacity = 4)
{_a = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _a){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_top = 0;
}

在这里插入图片描述
在这里插入图片描述
注意:如果这里的Stack没有我们自定义的构造函数,那么Stack就会调用Stack的默认构造函数(编译器生成的默认构造函数),也就是初始化为随机值

所以:
如果一个类中有自定义类型的成员变量,
那么我们可以考虑使用这个类的默认构造函数(编译器给我们生成的默认构造函数)

既然你去使用这个类的默认构造函数,那么这个类中的内置类型的成员变量怎么办呢?
C++11支持我们去在类的成员变量当中给缺省值

class MyQueue
{
private:Stack pushst;Stack popst;int _size = 1;这个依然是声明,只不过是给了一个缺省值而已永远记住:对于非函数来说,到底是声明还是定义要看是否真的开辟了空间函数的话看什么声明还是定义就很简单了吧
};

在这里插入图片描述
在VS2019中,编译器给我们处理了,而在VS2013中编译器没有给我们去处理
在这里插入图片描述
总结:
1.一般情况下,我们都要自己写构造函数
2.成员都是自定义类型,或者声明时给了缺省值:可以考虑让编译器自己生成构造函数

5.广义的默认构造函数

上面第四点提到的都是狭义的默认构造函数(有一个地方提到了广义的默认构造函数,我在那个地方已经标明了)
下面我们来看看广义的默认构造函数是什么?
在这里插入图片描述
这个初始化列表我们后面会进行详细介绍的

6.默认构造函数的形成规则

那么在什么情况下编译器才会给我们自动去生成一个无参的默认构造函数呢?

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。
在这里插入图片描述
当我们调用默认构造函数时发现不存在默认构造函数,这也就说明了上面那个规则

三.析构函数

1.析构函数的语法

在这里插入图片描述
析构函数是非常有价值的,回想一下我们在用C语言去实现数据结构并且使用的时候,
是不是经常很容易就会忘了调用destroy销毁函数,所以析构函数是很有价值的

我们以日期类为例,熟悉一下析构函数的语法

class Date
{
public:Date(int year = 1, int month = 2, int day = 3){_year = year;_month = month;_day = day;}~Date(){cout << "~Date4()" << endl;}void Print(){cout << _year << " " << _month << " " << _day << endl;}
private:int _year = 0;int _month = 0;int _day = 0;
};

在这里插入图片描述
因此:
如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;
有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

2.编译器实现的默认析构函数

当我们不写析构函数的时候,会发生什么现象呢?

默认生成的析构函数,行为跟构造函数类似
内置类型成员不做处理
自定义类型回去调用它的析构

在这里插入图片描述

四.拷贝构造函数

1.拷贝构造函数的引出

在这里插入图片描述
在C++中结构体扩展为了类,所以对于类而言,它同时也包含了许多结构体的属性

1.浅拷贝的局限性

下面先请大家看一下这个代码
在这里插入图片描述
这里我们对Date类进行了传值传参,
而这个传值传参:形参d是实参d1的一份拷贝,d拷贝了d1的所有数据,这个过程是采用值拷贝的方式进行的
而这个代码运行完全正常,别急,下面请大家再看一份代码
在这里插入图片描述
我们给Stack函数定义了构造函数和析构函数(这份代码中这两个函数都没有任何问题
但是程序还是崩了,这是为什么呢?

这里Func2函数的形参st是实参st1的一份拷贝,这里的拷贝依然是采用值拷贝的方式来进行的
也就是说:
在这里插入图片描述
在这里插入图片描述

这种编译器默认形成的采用值拷贝的方式来进行拷贝构造的方式,我们称之为浅拷贝

然后我们再来仔细地看一下上面那个报错的代码
在这里插入图片描述
结合这个控制台打印出的信息和上面画的那一张分析图片,
我们再来调试看一下这个过程

这里的st和st1的_a的值是一样的:
在这里插入图片描述
当_a指向的空间随着形参st的销毁被释放后,实参st1的_a依然是指向这个已经被释放了的空间的,也就是说这个st1的_a已经成为了一个野指针,因此发生了同一空间多次释放的问题:
在这里插入图片描述
我们就能更好地理解浅拷贝的局限性了

也正是因为这种局限性,
对于日期类这种没有在堆上去申请空间的类来说值拷贝没有报错的风险,
但是对于Stack这种在堆上申请了空间的类来说,值拷贝有着致命的缺陷

怎么解决呢?
在这里插入图片描述
这就需要我们对Stack类自己写一个拷贝构造函数了,我们要怎么写呢?
这就涉及到深拷贝的知识点了,不要急,我们先来实现一下Date类的拷贝构造函数

2.拷贝构造函数语法形式

在这里插入图片描述
这个无穷递归调用是怎么一回事?
下面就会给大家说明

1.浅拷贝的另一大坏处:程序死递归(以date类为例引出)

拷贝构造函数:顾名思义,就是用来拷贝一个对象的所有属性的,
所以不难写出下面的代码
在这里插入图片描述
可是编译器直接给我们报错了
这是为什么呢?
这就是无穷递归所引发的错误,为什么会无穷递归呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.日期类的拷贝构造函数的实现

既然传值不行,那就传引用试试
毕竟引用就是取别名,不需要进行值拷贝,在语法上引用作为形参,这个形参就是实参
不难写出这样的代码:

	Date(Date& dd){_year = dd._year;_month = dd._month;_day = dd._day;}

在这里插入图片描述
传引用就非常好地解决了这个无限递归的问题,
其实我们在这里实现的拷贝构造函数是浅拷贝,但是对于Date类来说,浅拷贝没有危害(因为Date类并没有在堆上开辟空间)
下面我们就去实现Stack类的拷贝构造函数

3.Stack类的拷贝构造函数的实现

注意:我们在这里就要去实现深拷贝了
因为浅拷贝无法解决Stack类的同一空间多次释放的错误

那么怎么去实现呢?

1.思路

在这里插入图片描述

2.具体实现
	Stack(Stack& st){_a = (int*)malloc(sizeof(int) * st._capacity);if (_a == nullptr){perror("malloc fail");exit(-1);}memcpy(_a, st._a, sizeof(int) * st._top);_top = st._top;_capacity = st._capacity;}

在这里插入图片描述
我们一上来遇到的问题就成功解决了
顺便祝大家1024节日快乐

那我们直接试试拷贝构造函数呗
在这里插入图片描述
调试看了一下,st1和st2指向的空间的确是不一样的

4.指针作为拷贝构造函数的参数

经过了上面的探索,我们发现引用作为拷贝构造函数的参数是真的好用
那么我用指针行吗
当然可以
不过就是没有引用更加简洁
我们以Date类为例,来看一下

	Date(Date* dd){_year = dd->_year;_month = dd->_month;_day = dd->_day;}调用方式:Date d2(&d1);

在这里插入图片描述
行是行,不过还是没有引用简洁啊~

5.const Date& d1

如果有人写代码写着写着写懵了,写出了这样的代码

	Date1(Date1& dd){dd._year = _year;//这里写反了_month = dd._month;_day = dd._day;}

在这里插入图片描述
这就坑了
我想要给d1拷贝一份数据命名为d2,结果年份那里我非但没有拷贝成功,还把我自己给改了
怎么办呢?
因此,C++语法建议在拷贝构造函数的参数那里这么定义:

	Date1(const Date1& dd){dd._year = _year;//这里就会报错了_month = dd._month;_day = dd._day;}

在这里插入图片描述
在这里插入图片描述
然后这个懵了的程序员就意识到问题所在了,然后就能轻而易举地将代码修改正确

3.拷贝构造函数的"传递性"

既然拷贝构造函数是一种特殊的构造函数
那么拷贝构造函数会跟构造函数一样具有"传递性"吗?
答案是:是的.
在这里插入图片描述
在这里插入图片描述

4.拷贝构造函数典型调用场景

在这里插入图片描述

Stack func1()
{Stack st;return st;//这里st也要调用一个拷贝构造函数生成一个临时拷贝,返回临时拷贝
}
//引用是C++入门最重要的知识
Stack& func2()
{static Stack st;return st;//static 修饰,直接返回引用就行
}

5.拷贝构造函数的总结

那么我们什么时候适合需要自己去写拷贝构造函数,什么时候适合直接用编译器默认生成的拷贝构造函数呢?
在这里插入图片描述

以上就是C++类和对象中(构造函数,析构函数,拷贝构造函数)详解的全部内容,希望能对大家有所帮助!

相关文章:

C++类和对象中(构造函数,析构函数,拷贝构造函数)详解

C类和对象中[构造函数,析构函数,拷贝构造函数]详解 一.前言1.类的6个默认成员函数 二.构造函数1.构造函数的引出2.无参构造函数3.缺省参数在构造函数中的应用4.编译器实现的默认构造函数5.广义的默认构造函数6.默认构造函数的形成规则 三.析构函数1.析构函数的语法2.编译器实现…...

智能矩阵系统解决的问题?

智能矩阵系统可以解决的问题多种多样&#xff0c;它主要通过人工智能技术应用于矩阵系统&#xff0c;解决一些传统方法难以处理的问题。 以下是一些常见的应用场景&#xff1a; 1. 数据管理&#xff1a;智能矩阵系统可以有效地管理大量的数据&#xff0c;包括数据的存储、检索…...

计算机网络——计算机网络体系结构(3/4)-计算机网络体系结构分层思想举例

目录 发送请求报文 应用层构建HTTP请求报文 运输层添加TCP首部 网络层添加IP首部 数据链路层形成帧 物理层转化为比特流 路由器处理 服务器处理 发回响应报文 计算机网络体系结构分层思想举例 假设网络拓扑如下所示&#xff0c;主机属于网络N1&#xff0c;Web服务器属…...

计算机网络,网络(OSI)七层模型,三次握手四次挥手,get与post请求区别,网络IO(BIO\NIO\AIO),TCP与UDP区别

1.OSI模型&#xff1f; 开放式系统互联通信参考模型(Open System Interconnection Reference Model) OSI网络七层模型&#xff1a;应用层、表示层、会话层、传输层、网络层、数据链路层、物理层 TCP/IP协议群简化了OSI七层模型&#xff1a;应用层、传输层、网络层、数据链路…...

【网络爬虫 | Python】数字货币ok链上bitcoin大额交易实时爬取,存入 mysql 数据库

文章目录 一、网站分析二、js 逆向获取 X-Apikey三、python 调用 js 获取 X-Apikey四、python 爬虫部分五、mysql 数据库、日志、配置文件、目录结构六、结尾 一、网站分析 oklink&#xff1a;https://www.oklink.com/ btc 大额交易&#xff1a;https://www.oklink.com/btc/tx-…...

【Servlet】实现Servlet程序

文章目录 1. 最朴素方式1. 创建项目2. 引入依赖3. 创建目录4. 编写代码5. 打包程序6. 部署程序7. 验证程序 2. 更方便方式1. 安装Smart TomCat插件2. 启动 1. 最朴素方式 1. 创建项目 选择Maven项目 2. 引入依赖 Maven项目创建完后会生成一个pom.xml文件&#xff0c;我们可…...

binlog 和 redolog 有什么区别

binlog 和 redolog 都是 Mysql 里面用来记录数据库数据变更操作的日志. binlog 其中 binlog 主要用来做数据备份、数据恢复和数据同步&#xff0c;在Mysql 的主从数据同步的场景中&#xff0c;master 节点的数据变更&#xff0c;会写入到 binlog 中&#xff0c;然后再把 binl…...

Git 修改已提交的用户名和邮箱

Git 修改已提交的用户名和邮箱 修改上一次提交的邮箱和用户名 git commit --amend --author Name<email>批量修改多次提交的邮箱和用户名 新建一个 .sh 脚本在 git 根目录下.sh脚本内容如下 git filter-branch --env-filter an"$GIT_AUTHOR_NAME" am"…...

小游戏外包开发流程及费用

小游戏的开发流程和费用会因项目的规模、复杂性和所选技术平台而有所不同。以下是一般的小游戏开发流程和可能的费用因素&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 开发流程&#xff1a; 概念和…...

Homeassistant docker配置

Homeassistant docker配置 【说明】本系列为自用教程&#xff0c;记录以便下次使用 【背景】一台J1900 4G64G的小主机&#xff0c;安装了OP系统&#xff0c;里面自带了Docker。为实现Homeassistant&#xff08;简称HA&#xff09;控制智能家居设备&#xff0c;进行如下配置。 【…...

Go 深入解析非类型安全指针

一、引言 非类型安全指针&#xff08;也称为“裸指针”或“原始指针”&#xff09;在编程领域中一直是一个具有争议和挑战性的主题。它们赋予程序员直接操作计算机内存的能力&#xff0c;为高级性能优化和底层系统交互提供了可能。然而&#xff0c;这种能力往往伴随着高风险&a…...

vue动态绑定class

Vue.js 允许您使用 v-bind 指令或简写的 : 来动态绑定 class 属性。这允许您基于某些条件为元素添加或删除类名&#xff0c;从而实现动态样式控制。以下是一些示例&#xff1a; 动态添加单个类名&#xff1a; <template> <div> <p :class"{ active: isActi…...

UDP网络通信反复发收

package UDP2;import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner;/* * 完成UDP 通信快速入门 实现发1收1*/ public class Client {public static void main(String[] args) throws Exception{// …...

ip报头和ip报文切片组装问题

在tcp层将数据打包封装向下传递后&#xff0c;网络层将其整个看为一个数据&#xff0c;然后对其数据加网络报头操作&#xff0c;在网络层最具有代表的协议就是ip协议。在这里我们探究ipv4的报头。 ip报头 4位版本&#xff1a;指定ip的版本号&#xff0c;对于ipv4来说就是4。 …...

linux之应用编程回顾总结

gcc编译过程 一个c/c文件要经过预处理、编译、汇编和链接4个阶段&#xff0c;才能变成可执行文件 1.预处理 C/C源文件中&#xff0c;以“#”开头的命令被称为预处理命令&#xff0c;如包含命令“#include”、宏定义命令“#define”、条件编译命令“#if”、“#ifdef”等。预处理…...

nginx配置负载均衡--实战项目(适用于轮询、加权轮询、ip_hash)

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…...

Mac GPU MPS常用方法

Requirements Mac computers with Apple silicon or AMD GPUs macOS 12.3 or later Python 3.7 or later Xcode command-line tools: xcode-select --install 判断是否可用 import torch if torch.backends.mps.is_available():mps_device torch.device("mps")x …...

【数据结构】线性表(四)双向链表的各种操作(插入、删除、查找、修改、遍历打印)

目录 线性表的定义及其基本操作&#xff08;顺序表插入、删除、查找、修改&#xff09; 四、线性表的链接存储结构 1. 单链表 2. 循环链表 3. 双向链表 a. 双向链表节点结构 b. 创建一个新的节点 c. 在链表末尾插入节点 d. 在指定位置插入节点 e. 删除指定位置的节点…...

数据结构和算法——图

图 有向图 带权图 邻接矩阵 邻接表相较于邻接矩阵&#xff0c;减少了存储空间&#xff1b; 邻接表 参考视频&#xff1a;【尚硅谷】数据结构与算法&#xff08;Java数据结构与算法&#xff09;_哔哩哔哩_bilibili...

大数据学习(16)-mapreduce详解

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...