【C++】类中的“默认成员函数“--构造函数出现的意义?拷贝构造时“无穷递归”和“双重释放”出现的原因?

目录

"默认"成员函数 概念引入:

一、构造函数

问题引入:

1)构造函数的概念

2)构造函数实例

3)构造函数的特性

4)关于默认生成的构造函数     (默认构造函数)

默认构造函数未完成初始化工作实例:

二、析构函数

1)析构函数概念

2)析构函数特性

三、拷贝构造函数

1)拷贝构造函数概念

示例代码:

2)深拷贝

3)拷贝构造函数特性

四、赋值运算符重载

运算符重载

赋值运算符重载

"默认"成员函数 概念引入:

C++中的默认成员函数是系统自动生成的,如果没有手动编写该类的成员函数,编译器就会自动为该类生成默认成员函数。默认成员函数包括默认构造函数、默认析构函数和默认拷贝构造函数等。

  1. 默认构造函数:当创建对象时,如果没有显式地调用构造函数,系统会自动调用默认构造函数来初始化对象。默认构造函数不接受任何参数,也不返回任何值。
  2. 默认析构函数:当对象被销毁时,系统会自动调用析构函数来清理对象。默认析构函数不接受任何参数,也不返回任何值。
  3. 默认拷贝构造函数:当将一个对象赋值给另一个对象时,系统会自动调用拷贝构造函数来完成对象的复制。默认拷贝构造函数会将原对象的所有成员变量逐个复制给新对象。

除了以上三种默认成员函数外,还有默认赋值运算符、取地址运算符等。这些默认成员函数可以让我们更方便、更高效地使用C++语言进行面向对象编程。

以上有六个默认成员函数,但是我们今天只探讨前4个,后面两个实际操作中我们很少直接编写,一般都是使用默认生成式的。

一、构造函数

问题引入:

        在C++编程语言中,构造函数和析构函数的出现与对象及对象的生命周期管理密切相关。在现实世界中,每个事物都有其生命周期,会在某个时候出现也会在另外一个时候消亡。类似地,程序是对现实世界的反映,其中的对象就代表了现实世界的各种事物,自然也就具有生命周期,也会被创建和销毁。

        因此,为了恰当地管理对象的生命周期,特别是对象的初始化和清理工作,C++引入了构造函数和析构函数这两个特殊的成员函数。每一个类都有一个默认的构造函数和析构函数;构造函数在类定义时由系统自动调用,析构函数在类被销毁时由系统自动调用。

具体来说,构造函数主要用于完成对象的初始化工作,它的名字和类名相同,一个类可以有多个构造函数。如果程序员没有手动编写构造函数,编译器会默认生成一个构造函数。另一方面,析构函数则用于完成对象的清理工作,它的名字是类名前面加一个~符号。当对象的生命期结束时,会自动执行析构函数。

        总的来说,构造函数和析构函数的出现,让程序员可以更加方便、准确地管理对象的生命周期,这是C++面向对象编程特性的一个重要体现。

1)构造函数的概念

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次(用一个已经存在的对象去初识另一个和对象)

2)构造函数实例

而且可以结合我们前面学过的知识,构造函数也是可以重载的:

//普通版
Date()//无参的
{
    m_year = 2023;
    m_month = 10;
    m_day = 17;
}
Date(int year,int month,int day)//带参且无缺省参数
{
    m_year = year;
    m_month = month;
    m_day = day;  
}

//融合版
Date(int year = 2023,int month = 10,int day = 17)//全缺省的
{
    m_year = year;
    m_month = month;
    m_day = day;
}

//普通版容易在使用时造成二义性问题,所以推荐后面的用法

3)构造函数的特性

不允许出现这正形式的调用:!!!

Date d1();

这种类型的调用,因为它可以被认为成函数的声明,就好比:

Date fun1();

所以,如果想定义一个日期类,则不能加括号,不然括号内需要加参数,

  1. 函数名与类名相同
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成

4)关于默认生成的构造函数     (默认构造函数)

关于编译器生成的默认成员函数,很多同学会有疑惑:不手动实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来它生成的默认构造函数又没什么用?我们在定义对象时调用了编译器生成的默认构造函数,但是对象的成员变量_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数没有实质性作用吗?

默认构造函数未完成初始化工作实例:

class Date
{
public:
	void Print()
	{
		std::cout << _year << _month << _day 
			<< std::endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main(void)
{
	Date date;
	date.Print();

	return 0;
}

对比手动添加构造函数的结果:
 

class Date
{
public:
	void Print()
	{
		std::cout << _year << _month << _day 
			<< std::endl;
	}
	Date(int year = 2024, int month = 11, int day = 13)//全缺省的
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main(void)
{
	Date date;
	date.Print();

	return 0;
}

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,编译器生成默认的构造函数会对自定类型成员调用的它的默认成员函数,而内置类型则不会,这就是为什么全是int类型的成员变量的日期类我们观察不到其初始化的原因。

 如果我们用两个栈实现一个队列类就可以体会到默认构造的作用了:

class MyQueue
{
private:
    Stack _pushst;
    Stack _popst;
};

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值,自动生成的构造函数会根据此初始化。

class Date
{
private:
    int _year = 2000;//在类中定义的默认值可以用作构造函数初始化的参考
    int _month = 1;
    int _day = 1;
}

 其实关于默认构造函数,只有在我们所有成员变量全是自定义类型的情况下,我们才会去省略掉自己编写的过程去使用编译器默认生成的,大部分情况下构造函数都是要我们自己编写的。

二、析构函数

1)析构函数概念

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么清除的呢?析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

~Date() {  
        // 在这里可以执行任何必要的清理工作  
        std::cout << "Date destructor called for " 
                  << _year << "-" << _month << "-" << _day 
                  << std::endl;  
    }  

2)析构函数特性

  1. 析构函数名是在类名前加上字符 “~”。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数
  5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数
  6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类
  7. 默认生成的析构函数不会对内置类型进行操作,不然释放掉不该释放的内容将会出问题。

析构函数是类的一种特殊的成员函数,其名称与类名相同但增加一个波浪线符号(~)。当对象超出范围或通过调用delete显式销毁对象时,析构函数会自动被调用。析构函数往往用来做“清理善后”的工作,例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存。此外,析构函数也可以有多个,如果没有手动写析构函数,编译器会生成一个默认的析构函数并自动调用。

析构函数的调用时机主要有以下几种:

  1. 对象生命周期结束:当对象超出作用域或被显式销毁时,析构函数会自动被调用,用来释放对象占用的内存空间。
  2. delete操作符:当使用delete删除指针类对象时,会直接调用析构函数来清理内存。
  3. 包含关系:如果对象Dog是对象Person的成员,那么在Person的析构函数被调用时,Dog对象的析构函数也会被自动调用。

三、拷贝构造函数

1)拷贝构造函数概念

        拷贝构造函数,也称为复制构造函数,是一种特殊的构造函数。它是当创建新对象时,使用同一类中之前已创建的对象来初始化新创建的对象。这种构造函数由编译器自动调用,用于一些基于同一类的其他对象的构建及初始化。

拷贝构造函数的形式参数必须是引用,通常为const引用(可以防止反向拷贝的情况),以便能够处理常量对象和非常量对象的复制。这样,它既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数。

        在编程实践中,拷贝构造函数常常用于以下情况:通过使用另一个同类型的对象来初始化新创建的对象;复制对象把它作为参数传递给函数;以及从函数返回一个对象时复制该对象。

        值得注意的是,如果程序员没有显式定义拷贝构造函数,那么编译器会自动生成一个默认的拷贝构造函数。这个默认的拷贝构造函数会将原对象的成员变量值赋值给新对象的相应成员变量。

拷贝构造函数的使用加场景类似于:

//使用a创建b的过程
int a = 1;
int b = a;

示例代码:

#include <iostream>  
#include <cstring>  

class MyClass {  
public:  
    char* data;  

    // 构造函数  
    MyClass(const char* str) {  
        data = new char[strlen(str) + 1];  
        strcpy(data, str);  
    }  

    // 拷贝构造函数  
    MyClass(const MyClass& other) {  
        data = new char[strlen(other.data) + 1]; // 重新分配内存  
        strcpy(data, other.data); // 深拷贝  
    }  

    // 析构函数  
    ~MyClass() {  
        delete[] data;  
    }  
}  

2)拷贝构造函数特性

  1. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发“无穷递归”调用。
  2. 拷贝构造函数典型调用场景:使用已存在对象创建新对象函数参数类型为类类型对象函数返回值类型为类类型对象
  3. 拷贝构造对应的默认的拷贝方式是浅拷贝,但是这样会引发“双重释放”的问题,所以在遇到一些指针成员时我们需要自定义拷贝构造函数以实现更安全的深拷贝,以确保每个对象都有独立的空间。

 接下来我们探究一下为什么会出现上述情况,无穷递归和双重释放。

 问题一:为什么会出现无穷递归?

在C++中,拷贝构造函数一般的形式为:

ClassName(const ClassName& other);

 这里的参数是一个该类的常量引用。如果我们使用传值方式,即如下形式:

ClassName(ClassName other);

首先我们需要知道,在调用拷贝构造函数时,我们本身需要第一个任务就是“传参”,但是我们不要忘了传参本身也是一种拷贝拷贝就需要调用拷贝构造,这句话就是程序的“思想钢印”,这种逻辑无法改变,所以就会出现这样一种情况,我们需要使用对象a去创建对象b,但是我需要拷贝对象a的内容到参数,既然是拷贝,于是就又去调用拷贝构造函数,然后这个新创建的拷贝构造函数也需要传参,如此往复,形成“无穷递归”,最终会导致栈溢出错误。而引用就不会出现这种情况了,因为引用本质是指针,在传参时调用一次拷贝构造后直接通过引用去获取内容就不会触发“传参就要通过拷贝构造传参”这条规则了,从而跳出我调用我自己的循环。

问题二:问什么会出现双重释放?

出现双重释放的场景一定是:传递内容中有指针,使用C++中的类,发生了值传递。

如果没有指针就不用谈及释放的问题,这是条件一;如果是C语言的话不会出现这种问题,因为C语言中不会有析构函数自动调用,我们人为释放只要不犯错不会出现双重释放的问题,这是条件而;我们想使用对象a创建对象b,针对指针,如果直接拷贝的话就会出现冲突,a的生命周期结束去释放指针指向的地址,b中指针和它的指向地址相同,在其生命周期结束时又去释放,所以就会造成双重释放,也成二次释放的情况。想要解决这个问题很简单,我们需要针对这种变量进行深拷贝,将其内容独立,在创建b的过程中开辟新的空间,然后再将a中指针指向空间里的内容拷贝到b中指向空间里去,这样就不会出现双重释放的问题了。

因此C++规定,自定义类型的传参就需要调用拷贝构造函数

3)深拷贝--最终解决方案

深拷贝,是指源对象与拷贝对象互相独立其中任何一个对象的改动都不会对另外一个对象造成影响。深拷贝会复制所有字段,并复制字段所指向的动态分配内存。深拷贝发生在对象及其引用的对象被复制时。对于基本数据类型,如预定义类型Int32,Double等,深拷贝复制所有基本数据类型的成员变量的值。对于引用数据类型的成员变量,深拷贝申请新的存储空间,并复制该引用对象所引用的对象。

深拷贝是一种特殊的拷贝方式,它不仅复制了对象的基本数据类型成员变量的值,还为引用类型的成员变量申请了新的存储空间,并递归复制了这些引用对象所引用的其他对象。这样,源对象与拷贝对象就完全独立任一对象的修改都不会影响到另一个对象

需要注意的是,在C++中,对于基本类型的数据以及简单的对象,它们之间的拷贝非常简单,通常是按位复制内存。但对于复杂对象和包含指针或动态内存分配的对象来说,需要进行深拷贝来确保两个对象不会相互影响。 默认情况下,基本数据类型(number,string,null,undefined,boolean)的操作都是深拷贝。

四、赋值运算符重载

运算符重载

  1. C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。函数名字为:关键字operator后面接需要重载的运算符符号。函数原型:返回值类型 operator操作符(参数列表)
  2. 不能通过连接其他符号来创建新的操作符:比如operator@
  3. 重载操作符必须有一个类类型参数
  4. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  5. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  6. 当然有特殊情况,以下五个运算符不可以重载!!!!
 *     ::    sizeof      ?     : 
#include <iostream>  

class Point {  
public:  
    int x, y;  

    // 构造函数  
    Point(int x = 0, int y = 0) : x(x), y(y) {}  

    // 重载 + 运算符  
    Point operator+(const Point& other) {  
        return Point(x + other.x, y + other.y);  
    }  
};  

int main() {  
    Point p1(1, 2);  
    Point p2(3, 4);  
    Point p3 = p1 + p2; // 使用重载的 + 运算符  

    std::cout << "Result: (" << p3.x << ", " << p3.y << ")" << std::endl;  

    return 0;  
}

在 C++ 中,操作符重载允许程序员为自定义类型定义操作符的行为。对于双操作数的操作符,在重载时通常需要两个操作数,但可以只传递一个参数,这背后有几个原因:

  1. 隐式对象:当重载二元操作符(如 +- 等)时,第一个操作数通常是一个类的实例(this 指针),因此只需要传递一个参数作为第二个操作数。这是因为在成员函数中,可以直接访问对象的成员。

  2. 符号语义:使用只传递一个参数的方式,可以保持操作符的原有语义。例如,a + b 中,a 是隐含的成员对象,而 b 是显式传入的参数。这样的设计简化了函数调用,同时确保了对对象内部状态的访问。

  3. 兼容性和一致性:在设计语言时,选择这种方式能够保持与其他语言操作符使用的一致性,尤其是那些不支持方法调用的语言。这种设计使得 C++ 的操作符重载更直观和易于使用。

赋值运算符重载

我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数。

                                                                                                                --《C++ Prime》

  1. 赋值运算符重载格式参数类型:const T&,传递引用可以提高传参效率返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值返回*this :要复合连续赋值的含义
  2. 赋值运算符只能重载成类的成员函数不能重载成全局函数//:原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
  3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

在实际编码中,建议在需要动态资源管理的类中实现拷贝构造函数、赋值操作符、析构函数,以遵循“遵循Rule of Three”的原则,确保资源管理正确无误。

如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

且不能改变操作符的操作数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

fastapi 调用ollama之下的sqlcoder模式进行对话操作数据库

from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel import ollama import mysql.connector from mysql.connector.cursor import MySQLCursor import jsonapp FastAPI()# 数据库连接配置 DB_CONFIG {"database": "web&quo…

基于微信小程序的乡村研学游平台设计与实现,LW+源码+讲解

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自…

基于Java Springboot城市交通管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数…

手机直连卫星NTN通信初步研究

目录 1、手机直连卫星之序幕 2、卫星NTN及其网络架构 2.1 NTN 2.2 NTN网络架构 3、NTN的3GPP标准化进程 3.1 NTN需要适应的特性 3.2 NTN频段 3.3 NTN的3GPP标准化进程概况 3.4 NTN的3GPP标准化进程的详情 3.4.1 NR-NTN 3.4.1.1 NTN 的无线相关 SI/WI 3.4.1.2…

基本数据类型和包装类型的区别、缓存池、自动拆箱装箱(面试题)

目录 1. 八种基本类型及对应包装类型 2. 基本类型和包装类型 区别 3. 自动拆箱装箱 3.1 自动装箱 3.2 自动拆箱 3.3 缓存池 4. 高频面试案例分析 1. 八种基本类型及对应包装类型 基本数据类型类型描述范围&#xff08;指数形式&#xff09;位数包装类型byte整型&#x…

Aria2-CVE-2023-39141漏洞分析

前言&#xff1a; 在偶然一次的渗透靶机的时候&#xff0c;上网查询Aria2的历史漏洞&#xff0c;发现了这个漏洞&#xff0c;但是网上并没有对应的漏洞解释&#xff0c;于是我就就源代码进行分析&#xff0c;发现这是一个非常简单的漏洞&#xff0c;于是发这篇文章跟大家分享一…

androidstudio入门到放弃配置

b站视频讲解传送门 android_studio安装包&#xff1a;https://developer.android.google.cn/studio?hlzh-cn 下载安装 开始创建hello-world 1.删除缓存 文件 下载gradle文件压缩&#xff1a;gradle-8.9用自己创建项目时自动生成的版本即可&#xff0c;不用和我一样 https://…

河道无人机雷达测流监测系统由哪几部分组成?

在现代水利管理中&#xff0c;河道无人机雷达监测系统正逐渐成为一种重要的工具&#xff0c;为河道的安全和管理提供了强大的技术支持。那么&#xff0c;这个先进的监测系统究竟由哪几部分组成呢&#xff1f; 河道无人机雷达监测系统工作原理 雷达传感器通过发射电磁波或激光束…

Mac上详细配置java开发环境和软件(更新中)

文章目录 概要JDK的配置JDK下载安装配置JDK环境变量文件 Idea的安装Mysql安装和配置Navicat Premium16.1安装安装Vscode安装和配置Maven配置本地仓库配置阿里云私服Idea集成Maven 概要 这里使用的是M3型片 14.6版本的Mac 用到的资源放在网盘 链接: https://pan.baidu.com/s/17…

CKA认证 | Day3 K8s管理应用生命周期(上)

第四章 应用程序生命周期管理&#xff08;上&#xff09; 1、在Kubernetes中部署应用流程 1.1 使用Deployment部署Java应用 在 Kubernetes 中&#xff0c;Deployment 是一种控制器&#xff0c;用于管理 Pod 的部署和更新。以下是使用 Deployment 部署 Java 应用的步骤&#x…

ffmpeg编程入门

文章目录 ffmpeg流程常用的音视频术语常用概念复用器编解码器ffmpeg的整体结构注册组件相关封装格式相关函数的调用流程 相关的ffpmeg数据结构简介数据结构之间的关系 ffmpeg流程 图中的函数 以及结构体都是ffmpeg自带提供的 ffmpeg打开的时候 和其他io操作差不多 有一个类似句…

函数指针示例

目录&#xff1a; 代码&#xff1a; main.c #include <stdio.h> #include <stdlib.h>int Max(int x, int y); int Min(int x, int y);int main(int argc, char**argv) {int x,y;scanf("%d",&x);scanf("%d",&y);int select;printf(&q…

间接采购管理:主要挑战与实战策略

间接采购支出会悄然消耗掉企业的现金流&#xff0c;即使是管理完善的公司也难以避免。这是因为间接支出不直接关联特定客户、产品或项目&#xff0c;使采购人员难以跟踪。但正确管理间接支出能为企业带来显著收益——前提是要有合适的工具。本文将分享管理间接支出的关键信息与…

TCP(下):三次握手四次挥手 动态控制

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持! TCP(上)&#xff1a;成熟可靠的传输层协议-CSDN博客 &#x1f95d;在上篇博客中&#xff0c;我们针对TCP的特性,报文结构,连接过程以及相对于其他协议的区别进行了探讨&#xff0c;提供了初步的理解和概览。本…

ASP.NET 部署到IIS,访问其它服务器的共享文件 密码设定

asp.net 修改上面的 IIS需要在 配置文件 添加如下内容 》》》web.config <system.web><!--<identity impersonate"true"/>--><identity impersonate"true" userName"您的账号" password"您的密码" /><co…

python实现十进制转换二进制,tkinter界面

目录 需求 效果 代码实现 代码解释 需求 python实现十进制转换二进制 效果 代码实现 import tkinter as tk from tkinter import messageboxdef convert_to_binary():try:# 获取输入框中的十进制数decimal_number int(entry.get())# 转换为二进制binary_number bin(de…

现代密码学|古典密码学例题讲解|AES数学基础(GF(2^8)有限域上的运算问题)| AES加密算法

文章目录 古典密码凯撒密码和移位变换仿射变换例题多表代换例题 AES数学基础&#xff08;GF&#xff08;2^8&#xff09;有限域上的运算问题&#xff09;多项式表示法 | 加法 | 乘法X乘法模x的四次方1的乘法 AES加密算法初始变换字节代换行移位列混合轮密钥加子密钥&#xff08…

ubuntu使用DeepSpeech进行语音识别(包含交叉编译)

文章目录 前言一、DeepSpeech编译二、DeepSpeech使用示例三、核心代码分析1.创建模型核心代码2.识别过程核心代码 四、交叉编译1.交叉编译2.使用 总结 前言 由于工作需要语音识别的功能&#xff0c;环境是在linux arm版上&#xff0c;所以想先在ubuntu上跑起来看一看&#xff…

阿里云引领智算集群网络架构的新一轮变革

阿里云引领智算集群网络架构的新一轮变革 云布道师 11 月 8 日~ 10 日在江苏张家港召开的 CCF ChinaNet&#xff08;即中国网络大会&#xff09;上&#xff0c;众多院士、教授和业界技术领袖齐聚一堂&#xff0c;畅谈网络未来的发展方向&#xff0c;聚焦智算集群网络的创新变…

PyQt5 加载UI界面与资源文件

步骤一: 使用 Qt Designer 创建 XXX.ui文件 步骤二: 使用 Qt Designer 创建 资源文件 步骤三: Python文件中创建相关类, 使用 uic.loadUi(mainwidget.ui, self ) 加载UI文件 import sys from PyQt5 import QtCore, QtWidgets, uic from PyQt5.QtCore import Qt f…