【C++漂流记】一文搞懂类与对象中的对象特征

在C++中,类与对象是面向对象编程的基本概念。类是一种抽象的数据类型,用于描述对象的属性和行为。而对象则是类的实例,具体化了类的属性和行为。本文将介绍C++中类与对象的对象特征,并重点讨论了对象的引用。
在这里插入图片描述


文章目录

  • 一、构造函数和析构函数
  • 二、函数的分类和调用
    • 1. 分类
    • 2. 调用方式
    • 3. 示例代码
    • 4. 输出结果:
    • 5. 代码解释
  • 三、拷贝构造函数的时机
  • 四、构造函数调用规则
  • 五、深拷贝和浅拷贝
  • 六、初始化列表
  • 七、类对象作为类成员
  • 八、静态成员


相关链接:
一文搞懂类与对象的封装
一文搞懂C++中的引用
函数的高级应用

一、构造函数和析构函数

当我们创建一个类时,它可能具有一些成员变量和成员函数。构造函数和析构函数是类的特殊成员函数,用于初始化对象清理对象

  1. 构造函数的作用是在创建对象时初始化对象的成员变量。它的名称与类名相同,没有返回类型,并且可以有参数。构造函数可以有多个重载版本,根据传入的参数的类型和数量来确定使用哪个构造函数。构造函数在对象创建时自动调用。

  2. 析构函数的作用是在对象销毁时清理对象的资源。它的名称与类名相同,前面加上一个波浪号(~),没有返回类型,并且不接受任何参数。析构函数只能有一个,不能重载。析构函数在对象销毁时自动调用。

示例代码:

class Person {
public:string name;int age;// 默认构造函数Person() {name = "Unknown";age = 0;cout << "Default constructor called" << endl;}// 带参数的构造函数Person(string n, int a) {name = n;age = a;cout << "Parameterized constructor called" << endl;}// 析构函数~Person() {cout << "Destructor called" << endl;}
};int main() {// 使用默认构造函数创建对象Person p1;cout << "Name: " << p1.name << ", Age: " << p1.age << endl;// 使用带参数的构造函数创建对象Person p2("John", 25);cout << "Name: " << p2.name << ", Age: " << p2.age << endl;return 0;
}

输出结果:

Default constructor called
Name: Unknown, Age: 0
Parameterized constructor called
Name: John, Age: 25
Destructor called
Destructor called

代码解释:
在上述示例中,我们定义了一个名为Person的类,它具有两个成员变量:nameage。我们使用构造函数和析构函数来初始化和清理这些成员变量。

首先,我们定义了一个默认构造函数,它没有参数。在默认构造函数中,我们将name设置为Unknown,将age设置为0,并打印一条消息来表示构造函数被调用。

接下来,我们定义了一个带参数的构造函数,它接受一个字符串参数和一个整数参数。在带参数的构造函数中,我们将传入的参数值分别赋给nameage成员变量,并打印一条消息来表示构造函数被调用。

在主函数中,我们首先使用默认构造函数创建一个名为p1的对象。由于没有传入任何参数,因此默认构造函数被调用。然后,我们打印出p1对象的nameage成员变量的值,它们分别为Unknown和0。

接下来,我们使用带参数的构造函数创建一个名为p2的对象。我们传入字符串"John"和整数25作为参数,因此带参数的构造函数被调用。然后,我们打印出p2对象的nameage成员变量的值,它们分别为John和25。

在程序结束时,对象p1p2超出了它们的作用域,因此它们被销毁。在对象销毁的过程中,析构函数被自动调用。在示例中,我们在析构函数中打印了一条消息来表示析构函数被调用。


二、函数的分类和调用

1. 分类

  1. 默认构造函数:如果类没有显式定义构造函数,则编译器会自动生成一个默认构造函数。默认构造函数没有参数,也不执行任何初始化操作。它在创建对象时被隐式调用。

  2. 带参数的构造函数:带参数的构造函数接受一个或多个参数,并用这些参数来初始化对象的成员变量。它们在对象创建时被调用,并且根据传入的参数的类型和数量来确定使用哪个构造函数。

  3. 拷贝构造函数:拷贝构造函数是一种特殊的构造函数,它接受一个同类对象的引用作为参数,并使用该对象的值来初始化新创建的对象。拷贝构造函数在以下情况下被调用:

    • 使用一个对象初始化另一个对象时
    • 将对象作为函数参数传递给函数
    • 从函数返回对象

2. 调用方式

  1. 直接调用:可以通过类名加括号的方式直接调用构造函数。例如:MyClass obj(10);

  2. 隐式调用:构造函数在创建对象时隐式调用。例如:MyClass obj = MyClass(10);,这里会调用带参数的构造函数来创建对象。

  3. 拷贝初始化:使用一个对象初始化另一个对象时,会调用拷贝构造函数。例如:MyClass obj1(10); MyClass obj2 = obj1;

  4. 函数参数传递:将对象作为函数参数传递给函数时,会调用拷贝构造函数。例如:void func(MyClass obj);,在调用函数func(obj)时会调用拷贝构造函数。

  5. 函数返回对象:从函数返回对象时,会调用拷贝构造函数。例如:MyClass func() { return MyClass(10); }

3. 示例代码

#include <iostream>
using namespace std;class MyClass {
public:int num;// 默认构造函数MyClass() {num = 0;cout << "Default constructor called" << endl;}// 带参数的构造函数MyClass(int n) {num = n;cout << "Parameterized constructor called" << endl;}// 拷贝构造函数MyClass(const MyClass& obj) {num = obj.num;cout << "Copy constructor called" << endl;}// 析构函数~MyClass() {cout << "Destructor called" << endl;}
};int main() {// 直接调用构造函数MyClass obj1(10);// 隐式调用构造函数MyClass obj2 = MyClass(20);// 拷贝初始化MyClass obj3(obj1);// 函数参数传递void func(MyClass obj);func(obj1);// 函数返回对象MyClass func();MyClass obj4 = func();return 0;
}

4. 输出结果:

Parameterized constructor called
Parameterized constructor called
Copy constructor called
Copy constructor called
Destructor called
Destructor called
Destructor called
Destructor called

5. 代码解释

在上述示例中,我们定义了一个名为MyClass的类,它包含一个成员变量num和多个构造函数。我们创建了多个对象,并通过不同的方式调用构造函数。

首先,我们通过直接调用构造函数创建了对象obj1,它会调用带参数的构造函数。然后,我们通过隐式调用构造函数创建了对象obj2,它也会调用带参数的构造函数。

接下来,我们通过拷贝初始化创建了对象obj3,它使用对象obj1的值来初始化。在拷贝初始化过程中,会调用拷贝构造函数。

然后,我们定义了一个函数func,它接受一个MyClass对象作为参数。在调用函数func(obj1)时,会调用拷贝构造函数来将对象obj1传递给函数。

最后,我们定义了一个函数func,它返回一个MyClass对象。在调用函数func()并将返回的对象赋值给obj4时,会调用拷贝构造函数。

在程序结束时,所有对象超出了它们的作用域,因此它们被销毁。在对象销毁的过程中,析构函数被自动调用。在示例中,我们在析构函数中打印了一条消息来表示析构函数被调用。


三、拷贝构造函数的时机

  1. 使用一个对象初始化另一个对象时:当使用一个已经存在的对象来初始化一个新对象时,会调用拷贝构造函数。例如:
class MyClass {
public:MyClass(int value) : m_value(value) {}MyClass(const MyClass& other) : m_value(other.m_value) {std::cout << "Copy constructor called" << std::endl;}
private:int m_value;
};MyClass obj1(10);
MyClass obj2 = obj1; // 调用拷贝构造函数
  1. 将对象作为函数参数传递给函数:当将对象作为函数参数传递给函数时,会调用拷贝构造函数。例如:
class MyClass {
public:MyClass(int value) : m_value(value) {}MyClass(const MyClass& other) : m_value(other.m_value) {std::cout << "Copy constructor called" << std::endl;}
private:int m_value;
};void func(MyClass obj) {// Do something with obj
}MyClass obj1(10);
func(obj1); // 调用拷贝构造函数
  1. 从函数返回对象:当从函数返回对象时,会调用拷贝构造函数。例如:
class MyClass {
public:MyClass(int value) : m_value(value) {}MyClass(const MyClass& other) : m_value(other.m_value) {std::cout << "Copy constructor called" << std::endl;}
private:int m_value;
};MyClass func() {MyClass obj(10);return obj; // 调用拷贝构造函数
}
  1. 在使用类对象进行赋值操作时,也会调用拷贝构造函数。例如:
class MyClass {
public:MyClass(int value) : m_value(value) {}MyClass(const MyClass& other) : m_value(other.m_value) {std::cout << "Copy constructor called" << std::endl;}
private:int m_value;
};MyClass obj1(10);
MyClass obj2;
obj2 = obj1; // 调用拷贝构造函数

需要注意的是,编译器有时会进行优化,避免不必要的拷贝构造函数的调用。这种优化称为“拷贝消除”(copy elision)。在某些情况下,编译器可能会直接将对象的值从一个位置移动到另一个位置,而不是进行拷贝构造函数的调用。这可以提高性能,但是不会调用拷贝构造函数。


四、构造函数调用规则

构造函数调用规则如下:

  1. 默认构造函数:如果没有显式定义构造函数,编译器会自动生成一个默认构造函数。默认构造函数没有参数,并且执行默认的初始化操作。当创建对象时,如果没有提供参数,会调用默认构造函数。

  2. 参数化构造函数:参数化构造函数接受一个或多个参数,并用这些参数来初始化对象的成员变量。当创建对象时,如果提供了参数,会调用对应的参数化构造函数。

  3. 拷贝构造函数:拷贝构造函数接受一个同类型的对象作为参数,并使用该对象的值来初始化新对象。拷贝构造函数可以用于对象的拷贝初始化、函数参数传递和函数返回对象等场景。

  4. 移动构造函数:移动构造函数是C++11引入的新特性,它接受一个右值引用作为参数,并使用该参数的值来初始化新对象。移动构造函数通常用于在对象的资源所有权转移时提高性能。

构造函数的调用规则如下:

  • 当创建对象时,会根据提供的参数类型和数量来选择合适的构造函数进行调用。如果没有提供参数,则会调用默认构造函数。
  • 当使用一个对象来初始化另一个对象时,会调用拷贝构造函数。
  • 当将对象作为函数参数传递给函数时,会调用拷贝构造函数。
  • 当从函数返回对象时,会调用拷贝构造函数。
  • 在使用类对象进行赋值操作时,也会调用拷贝构造函数。
  • 在某些情况下,编译器会进行优化,避免不必要的拷贝构造函数的调用。这种优化称为“拷贝消除”(copy elision)。

五、深拷贝和浅拷贝

浅拷贝是指将一个对象的值复制到另一个对象,包括对象的成员变量。这意味着两个对象共享相同的内存地址,对其中一个对象的修改会影响到另一个对象。浅拷贝只复制了对象的表面层次,没有复制对象所拥有的资源。

深拷贝是指将一个对象的值复制到另一个对象,并且为新对象分配独立的内存空间。这样两个对象就拥有了彼此独立的内存空间,对其中一个对象的修改不会影响到另一个对象。深拷贝会递归地复制对象的所有成员变量,包括对象所拥有的资源。

示例代码:

#include <iostream>
#include <cstring>class Person {
public:Person(const char* name, int age) {m_name = new char[strlen(name) + 1];strcpy(m_name, name);m_age = age;}// 拷贝构造函数Person(const Person& other) {m_name = new char[strlen(other.m_name) + 1];strcpy(m_name, other.m_name);m_age = other.m_age;}// 析构函数~Person() {delete[] m_name;}// 打印信息void printInfo() {std::cout << "Name: " << m_name << ", Age: " << m_age << std::endl;}private:char* m_name;int m_age;
};int main() {Person person1("Alice", 25);Person person2 = person1; // 浅拷贝person1.printInfo(); // 输出:Name: Alice, Age: 25person2.printInfo(); // 输出:Name: Alice, Age: 25person2.printInfo(); // 输出:Name: Alice, Age: 25person2.printInfo(); // 输出:Name: Alice, Age: 25person1.printInfo(); // 输出:Name: Bob, Age: 30person2.printInfo(); // 输出:Name: Alice, Age: 25return 0;
}

在上面的示例中,我们定义了一个Person类,其中包含一个字符串类型的成员变量m_name和一个整型的成员变量m_age。在构造函数中,我们使用new运算符为m_name分配了独立的内存空间,并将字符串复制到该内存空间中。

然后,我们创建了一个person1对象,并将其值赋给person2对象。由于默认的拷贝构造函数是浅拷贝,所以person2对象的m_name指针指向了与person1对象相同的内存空间。当我们修改person2对象的m_name时,实际上也会修改person1对象的m_name。这就是浅拷贝的特点。

为了实现深拷贝,我们需要自定义拷贝构造函数,并在其中为m_name分配独立的内存空间,并将字符串复制到该空间中。这样,person2对象就拥有了自己独立的m_name内存空间,对其进行修改不会影响到person1对象。

总结起来,浅拷贝只复制对象的表面层次,而深拷贝会递归地复制对象的所有成员变量,包括对象所拥有的资源。深拷贝需要自定义拷贝构造函数来实现。


六、初始化列表

初始化列表是一种在构造函数中初始化成员变量的方法,可以用于实现深拷贝。

在上面的示例中,我们可以使用初始化列表来实现深拷贝,而不需要在拷贝构造函数中手动分配内存和复制字符串。

下面是使用初始化列表实现深拷贝的示例:

#include <iostream>
#include <cstring>class Person {
public:Person(const char* name, int age) : m_age(age) {m_name = new char[strlen(name) + 1];strcpy(m_name, name);}// 拷贝构造函数Person(const Person& other) : m_age(other.m_age) {m_name = new char[strlen(other.m_name) + 1];strcpy(m_name, other.m_name);}// 析构函数~Person() {delete[] m_name;}// 打印信息void printInfo() {std::cout << "Name: " << m_name << ", Age: " << m_age << std::endl;}private:char* m_name;int m_age;
};int main() {Person person1("Alice", 25);Person person2 = person1; // 深拷贝person1.printInfo(); // 输出:Name: Alice, Age: 25person2.printInfo(); // 输出:Name: Alice, Age: 25person2.printInfo(); // 输出:Name: Alice, Age: 25person2.printInfo(); // 输出:Name: Alice, Age: 25person1.printInfo(); // 输出:Name: Alice, Age: 25person2.printInfo(); // 输出:Name: Alice, Age: 25return 0;
}

在上面的示例中,我们在构造函数的初始化列表中分配了独立的内存空间,并将字符串复制到该空间中。这样,person2对象就拥有了自己独立的m_name内存空间,对其进行修改不会影响到person1对象。

使用初始化列表可以简化代码,并且可以确保在对象构造时成员变量已经正确初始化。这对于实现深拷贝非常有用。


七、类对象作为类成员

当一个类的成员变量是另一个类的对象时,我们需要在拷贝构造函数中正确地拷贝这些成员变量。

以下是一个示例,其中Person类的一个成员变量是Address类的对象:

#include <iostream>
#include <cstring>class Address {
public:Address(const char* city, const char* street) {m_city = new char[strlen(city) + 1];strcpy(m_city, city);m_street = new char[strlen(street) + 1];strcpy(m_street, street);}Address(const Address& other) {m_city = new char[strlen(other.m_city) + 1];strcpy(m_city, other.m_city);m_street = new char[strlen(other.m_street) + 1];strcpy(m_street, other.m_street);}~Address() {delete[] m_city;delete[] m_street;}void printInfo() {std::cout << "City: " << m_city << ", Street: " << m_street << std::endl;}private:char* m_city;char* m_street;
};class Person {
public:Person(const char* name, int age, const Address& address) : m_age(age), m_address(address) {m_name = new char[strlen(name) + 1];strcpy(m_name, name);}Person(const Person& other) : m_age(other.m_age), m_address(other.m_address) {m_name = new char[strlen(other.m_name) + 1];strcpy(m_name, other.m_name);}~Person() {delete[] m_name;}void printInfo() {std::cout << "Name: " << m_name << ", Age: " << m_age << std::endl;m_address.printInfo();}private:char* m_name;int m_age;Address m_address;
};int main() {Address address("New York", "Broadway");Person person1("Alice", 25, address);Person person2 = person1; // 深拷贝person1.printInfo(); // 输出:Name: Alice, Age: 25, City: New York, Street: Broadwayperson2.printInfo(); // 输出:Name: Alice, Age: 25, City: New York, Street: Broadwayreturn 0;
}

在上面的示例中,Person类的一个成员变量是Address类的对象。在Person类的拷贝构造函数中,我们使用拷贝构造函数来正确地拷贝Address对象。这样,当我们拷贝一个Person对象时,Person对象和其成员变量Address对象都会进行深拷贝。

需要注意的是,在Person类的析构函数中,我们只需要释放m_name成员变量的内存空间,因为m_address成员变量的内存空间会在Address类的析构函数中释放。

总结起来,当一个类的成员变量是另一个类的对象时,我们需要在拷贝构造函数中正确地拷贝这些成员变量,以实现深拷贝。


八、静态成员

静态成员变量是属于类本身而不是类的实例的。因此,在拷贝构造函数中不需要拷贝静态成员变量,因为它们在所有类的实例之间是共享的。

以下是一个示例,其中Person类有一个静态成员变量count

#include <iostream>class Person {
public:Person(const char* name, int age) : m_age(age) {m_name = new char[strlen(name) + 1];strcpy(m_name, name);count++;}Person(const Person& other) : m_age(other.m_age) {m_name = new char[strlen(other.m_name) + 1];strcpy(m_name, other.m_name);count++;}~Person() {delete[] m_name;count--;}static int getCount() {return count;}private:char* m_name;int m_age;static int count;
};int Person::count = 0;int main() {Person person1("Alice", 25);Person person2 = person1; // 深拷贝std::cout << "Count: " << Person::getCount() << std::endl; // 输出:Count: 2return 0;
}

在上面的示例中,Person类有一个静态成员变量count,用于记录创建的Person对象的数量。在构造函数中,我们通过递增count来跟踪对象的数量,在析构函数中通过递减count来更新对象的数量。

在拷贝构造函数中,我们不需要拷贝静态成员变量count,因为它是属于类本身而不是类的实例。因此,在拷贝构造函数中只需要拷贝非静态成员变量即可。

总结起来,静态成员变量不需要在拷贝构造函数中进行拷贝,因为它们是属于类本身而不是类的实例。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python入门教程35:使用email模块发送HTML和图片邮件

smtplib模块实现邮件的发送功能&#xff0c;模拟一个stmp客户端&#xff0c;通过与smtp服务器交互来实现邮件发送的功能&#xff0c;可以理解成Foxmail的发邮件功能&#xff0c;在使用之前我们需要准备smtp服务器主机地址、邮箱账号以及密码信息。 #我的Python教程 #官方微信公…

什么是 DNS 隧道以及如何检测和防止攻击

什么是 DNS 隧道&#xff1f; DNS 隧道是一种DNS 攻击技术&#xff0c;涉及在 DNS 查询和响应中对其他协议或程序的信息进行编码。DNS 隧道通常具有可以锁定目标 DNS 服务器的数据有效负载&#xff0c;允许攻击者管理应用程序和远程服务器。 DNS 隧道往往依赖于受感染系统的…

sklearn中的数据集使用

导库 from sklearn.datasets import load_iris 实现 # 加载数据集 iris load_iris() print(f查看数据集&#xff1a;{iris}) print(f查看数据集的特征&#xff1a;{iris.feature_names}) print(f查看数据集的标签&#xff1a;{iris.target_names}) print(f查看数据集的描述…

linux 安装Docker

# 1、yum 包更新到最新 yum update # 2、安装需要的软件包&#xff0c; yum-util 提供yum-config-manager功能&#xff0c;另外两个是devicemapper驱动依赖的 yum install -y yum-utils device-mapper-persistent-data lvm2 # 3、 设置yum源 yum-config-manager --add-repo h…

Lua01——概述

Lua是啥&#xff1f; 官网 https://www.lua.org Lua这个名字在葡萄牙语中的意思是“美丽的月亮”&#xff0c;诞生于巴西的大学实验室。 这是一个小巧、高效且能够很好的和C语言一起工作的编程语言。 在脚本语言领域中&#xff0c;Lua因为有资格作为游戏开发的备选方案&…

51单片机项目(10)——基于51单片机的电压计

本次设计的电压计&#xff0c;使用ADC0832芯片&#xff0c;测到电压后&#xff0c;将电压信息发送到串口进行显示。仿真功能正常&#xff0c;能够运行。&#xff08;工程文件和代码放在最后&#xff09; 电路图如下&#xff1a; 运行过程如下&#xff1a; ADC0832介绍&#xff…

「网页开发|前端开发|Vue」07 前后端分离:如何在Vue中请求外部数据

本文主要介绍两种在Vue中访问外部API获取数据的方式&#xff0c;通过让Vue通过项目外部的接口来获取数据&#xff0c;而不是直接由项目本身进行数据库交互&#xff0c;可以实现前端代码和后端代码的分离&#xff0c;让两个部分的代码编写更独立高效。 文章目录 本系列前文传送…

Flink CDC 菜鸟教程 -环境篇

本教程将介绍如何使用 Flink CDC 来实现这个需求, 在 Flink SQL CLI 中进行,只涉及 SQL,无需一行 Java/Scala 代码,也无需安装 IDE。 系统的整体架构如下图所示: 环境篇 1、 准备一台Linux 2、准备教程所需要的组件 下载 flink-1.13.2 并将其解压至目录 flink-1.13.2 …

CSS学习笔记05

CSS笔记05 定位 position CSS 属性position - 用于指定一个元素在文档中的定位方式。top&#xff0c;right&#xff0c;bottom 和 left 属性则决定了该元素的最终位置。position 有以下常用的属性值&#xff1a; position: static; - 默认值。指定元素使用正常的布局行为&am…

Mojo 摸脚语言,似乎已经可以安装

文章原地址&#xff1a;https://i.scwy.net/it/2023/090821-mojo/ Mojo 吹得很凶&#xff0c;面向AI编程&#xff0c;甩Python几十条街&#xff0c;融资上亿.... 但无缘一试&#xff0c;在Win和Ubuntu上试都不能通过。 由 LLVM 和 Swift 编程语言的联合创始人 Chris Lattner…

想要精通算法和SQL的成长之路 - 课程表III

想要精通算法和SQL的成长之路 - 课程表III 前言一. 课程表III&#xff08;贪心优先队列&#xff09;1.1 优先选择截止时间更小的课程1.2 如果当前课程无法学习怎么办&#xff1f;1.3 优化 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 课程表III&#xff08;贪心优先队列&…

安装程序报错“E: Sub-process /usr/bin/dpkg returned an error code (1)”的解决办法

今天在终端使用命令安装程序时出现了如下的报错信息。 E: Sub-process /usr/bin/dpkg returned an error code (1) 这种情况下安装什么程序最终都会报这个错&#xff0c;具体的报错截图如下图所示。 要解决这个问题&#xff0c;首先使用下面的命令进到相应的目录下。 cd /var/…

使用openWRT 配置SFTP 实现远程文件安全传输

文章目录 前言 1. openssh-sftp-server 安装2. 安装cpolar工具3.配置SFTP远程访问4.固定远程连接地址 前言 本次教程我们将在OpenWRT上安装SFTP服务&#xff0c;并结合cpolar内网穿透&#xff0c;创建安全隧道映射22端口&#xff0c;实现在公网环境下远程OpenWRT SFTP&#xf…

生信豆芽菜-机器学习筛选特征基因

网址&#xff1a;http://www.sxdyc.com/mlscreenfeature 一、使用方法 1、准备数据 第一个文件&#xff1a;特征表达数据 第二个文件&#xff1a;分组信息&#xff0c;第一列为样本名&#xff0c;第二列为患者分组 第三个文件&#xff1a;分析基因名 2、选择机器学习的方…

PHP设备检验系统Dreamweaver开发mysql数据库web结构php编程计算机网页代码

一、源码特点 PHP设备检验系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 下载地址 https://download.csdn.net/download/qq_41221322/88306259 php设备检验系统1 …

企业应用系统 PHP项目支持管理系统Dreamweaver开发mysql数据库web结构php编程计算机网页

一、源码特点 PHP 项目支持管理系统是一套完善的web设计系统 应用于企业项目管理&#xff0c;从企业内部的各个业务环境总体掌握&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 php项目支撑管理系统2 二、功能介绍 (1)权限管理&#xff1…

深度学习-4-二维目标检测-YOLOv5理论模型详解

YOLOv5理论模型详解 1.Yolov5四种网络模型 Yolov5官方代码中&#xff0c;给出的目标检测网络中一共有4个版本&#xff0c;分别是Yolov5s、Yolov5m、Yolov5l、Yolov5x四个模型。 YOLOv5系列的四个模型&#xff08;YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x&#xff09;在参数量和性…

进阶C语言-指针的进阶(中)

指针的进阶 &#x1f4d6;5.函数指针&#x1f4d6;6.函数指针数组&#x1f4d6;7.指向函数指针数组的指针&#x1f4d6;8.回调函数 &#x1f4d6;5.函数指针 数组指针 - 指向数组的指针 - 存放的是数组的地址 - &数组名就是数组的地址。 函数指针 - 指向函数的指针 - 存放的…

【react】Hooks原理和实战

前言 在最初学习react的时候&#xff0c;我们大部分会选择去扒一扒React的官方文档&#xff0c;看看他是什么&#xff0c;怎么使用的。而我却很好奇在文档里学习的第一个完整的组件是 类&#xff08;Class&#xff09;组件&#xff0c;但是在实际工作中我们看到项目中所声明的…

OpenCV 02(色彩空间)

一、OpenCV的色彩空间 1.1 RGB和BGR 最常见的色彩空间就是RGB, 人眼也是基于RGB的色彩空间去分辨颜色的. OpenCV默认使用的是BGR. BGR和RGB色彩空间的区别在于图片在色彩通道上的排列顺序不同. 显示图片的时候需要注意适配图片的色彩空间和显示环境的色彩空间.比如传入的图片…