C++入门:类与对象(1)

news2024/9/23 17:13:35

本篇作为学习类与对象后的一些记录与感想,不适用于第一次接触类和对象的朋友。

目录

1.面向过程和面向对象

2.类

2.1类的基础知识

2.2 类中的访问限定符

2.3类中的函数声明定义分离(如何在不同的文件中实现同一个类)

2.4类的封装

2.5类的实例化

2.5.1对象中成员的存储方式

2.5.2对象的大小

 2.5.3空类的大小

2.6this指针

3.类的默认成员函数

3.1构造函数

3.2析构函数

3.3拷贝构造

3.3.1显式实现拷贝构造及无穷递归之谜

3.3.2自动生成的拷贝构造 


 

1.面向过程和面向对象

C 语言是 面向过程 的, 关注 过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++ 基于面向对象 的, 关注 的是 对象 ,将一件事情拆分成不同的对象,靠对象之间的交互完成。但由于CPP是兼容c语言的,所以c++又不是完全的面向对象语言,有时候会包含c语言的面向过程的逻辑。反观Java,就是完全的面向对象语言,甚至连main函数都是包含在类中的。

2.类

2.1类的基础知识

C语言中最适合的过渡知识点就是结构体struct

C++ 中,结构体内不仅可以定义变量,也可以定义函数。 比如:
    之前在数据结构初阶中,用 C 语言方式实现的栈,结构体中只能定义变量 ;现在以 C++ 方式实现, 会发现 struct 中也可以定义函数。
除此之外,cpp中的类名称可以直接作为这个类的名字而出现,不需要再重复书写struct。

                                           

但是在c++中,更喜欢用类(class)来替代结构体 

class className
{
// 类体:由成员函数和成员变量组成
};  

类比一个结构体可以实例化很多个结构体,1个类也可以实例化 N个对象。

类中既可以定义变量,也可以定义函数

这样就能有效缩短命名。不再需要区分是QueuePush还是StackPush,只需要在各个结构体中写一个相应的push函数即可。函数整体变短了就是好事。

2.2 类中的访问限定符

我们要对一个封装好的类按照“蛋图”的思想去理解。一些数据、函数是放在蛋黄中的,在类之外是无法访问这些数据和函数的;一些函数、接口是提供给你的可以使用的,在蛋的外层,在类之外是可以访问这些数据和函数的。 

首先,在命名方面,若我们初始化了一个类叫作Stack,

class stack st1或者stack st1均可以定义出一个新的该类型的对象。

(在后文中会了解到,stack st1;相当于调用了构造函数)

我们通过访问限定符来限制各个函数、变量等做接口或者“蛋黄”。控制哪些能访问,哪些数据不能访问。

如果不写访问限定符,默认class的访问权限是私有的(也就是无法通过  类  外来使用、调用)。因为要兼容c,struct默认的访问是共有的

一般情况下,成员变量设计成私有,成员函数设计成公有。


类中变量的命名: 

          

如果内域和外部参数的名字重合(如上图),就不太合适。因为会优先搜索局部域的内容,参数year作为局部域被使用,出栈帧后销毁,而private中的成员year没有被赋值。

所以cpp推荐将成员变量加一个特殊的标识符如 前置_ year  或 后加 year_   或者 m_year,其中m表示member,以此来避免歧义。

class Date {
public:
	void DateInit(int year, int month, int day) {
		_year = year;
		_month = month;
		_day= day;
	}
private:
	int _day;
	int _month;
	int _year;
};

2.3类中的函数声明定义分离(如何在不同的文件中实现同一个类)

类定义了一个新的作用域 ,类的所有成员都在类的作用域中 在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
比如Queue中有一个Push、Stack中有一个Push,类域就能很好的分辨这两个函数
#include "class.h"
using namespace std;

class Person {
private:
	int _age;
	char _name[20];
	char _gender[6];
public:
	void PrintPersonInfo();
};

      先在.cpp文件中做好准备工作   ,并在public中写出函数的声明。

      接着我们到相应的class.h中去实现该函数

由前文中域的概念我们可知,类有类域,类域中的成员函数或者成员变量只能在类中搜索。PrintPersonInfo是一个类域中的函数,应当使用 类名 :: 函数名,才能找到该函数。

      此处也能观察到在private中的gender/age/name的左下角都有一个锁的标识,表示这是一个被private修饰了的变量。

         

void Person::PrintPersonInfo() {
	cout << "_name" << "_gender" << _age << endl;
}

有了Person::之后,就相当于是在类的内部写函数了,不需要再在"_name"  "_gender"等前面再写一次Person:: 

tip:任何一个花括号里的部分都是类域,包括if、while等语句的花括号。

至此为止,我们了解到的域的概念有:类域、全局域、局部域、命名空间域

2.4类的封装

在编程语言中,如果函数和对象不限制不封装,非常要求使用者的“素质”,就比如此处的top到底是指向栈顶的下一个还是栈顶?初始化时让top=-1还是0?

                                                   

C语言就像这样,是“露天的”,无约束的;而c++经过封装之后,就会更有秩序、更便于使用者管理

2.5类的实例化

定义类的时候,其实是在“声明”

用类类型创建对象的过程,称为类的实例化
类和对象是一对多的关系

类就像是设计图, 类实例化出对象就像现实中使用建筑设计图建造出房子

类就是一个模型,类实例化出对象就是根据这个模型来产出的。因此,类和对象是一对多的关系

2.5.1对象中成员的存储方式

    每个对象中成员变量是不同的,但是调用同一份函数,如果按照每个实例化的类中都有所有的函数的方式存储,当一 个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。
    所以将类成员函数都保存到公共代码段中,类成员变量保存到每一个对象中

2.5.2对象的大小

可通过sizeof证明, 当计数一个对象的大小时,只算了成员变量,未计算成员函数
                                   
(根据内存对齐规则,最大的为int,四个字节,数组按照元素类型计算,_gender中6个char需要2(1.5)个int类型的空间,_name刚好五个,共5+2+1=8个,8*4=32个字节)
“通过建筑房屋的图纸也能计算出房屋的大小”
 为什么要对齐:
由于硬件问题,为了减少读取的次数,只能整数倍整数倍的读。 对齐的本质是一种空间换时间的做法,可以通过#prama 修改默认对齐数:
# prama pack(1)

 我们在用同一个类Stack定义两个对象:st1和st2,Stack类里面实现一个Init函数,通过汇编代码观察st1和st2在调用Init函数时call的函数地址:

        
对象是两个对象,但是大家调用的函数地址是一样的。
成员变量都会被各当做各的,但是成员函数会存到公共区域。
不要将此概念和访问限定符中的概念混合 , 是否是类成员函数或类成员变量与是否被private或public修饰无关。只要是函数就会被放入公共代码段,只要是变量就会放在实例化的类中,计数类大小时也只会计数该部分。


 2.5.3空类的大小

空类的大小是1:

定义成功就得开空间,就得有地址

给他开一个空间但是不存有效数据,有这一个空间,表示这个对象被开出来了。
class A3 {

};

比如我们用A3定义了一个a3和b3,如果类的大小是0,那么如何说明a3和b3到底是被开出来了还是没有开出来?


class A2{
public:
    void f(){
       ;  
   }
}

同理,A2的大小也是1,因为成员函数不会被计入大小的计算

2.6this指针

class Date {
public:
	void Init(int year=2000, int month=1, int day = 1);
	void Print() {
		cout << _year << "." << _month << "." << _day << "." << endl;
	}
private:
	int _day;
	int _month;
	int _year;
};

我们以Date为例,引出this的概念。

做以下调用:

                                              

前文中我们提到,Print函数和Init函数是存放在公共代码段的,请问两次调用是如何分别找到d1和d2的_day、_month、_year的呢?

隐含的this指针:

    C++ 中通过引入 this 指针解决该问题,即: C++ 编译器给每个 非静态的成员函数 增加了一个隐藏 指针参数this ,让该指针指向当前对象 ( 函数运行时调用该函数的对象 ) ,在函数体中所有需要访问 成员变量 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递该指针,编 译器自动完成 。类成员函数在使用时,都会默认再传一个“调用该函数对象的指针”,即this指针

注释部分是编译器实际上的运行逻辑: 

                                                   

cout语句中涉及成员变量的也有一个this指针(指针应用->来找到对应的成员):

 总体来看:

this是指向对象,用来访问成员变量的,因为对象中也只有成员变量。

为什么叫“隐含的this指针”?:

首先,实参和形参的位置不能自己手动写上一个this,否则会编译不通过。编译器会自己加上this。但是在对象里面可以使用this指针来找到自己。 

其实Date* this写作Date* const this更为贴切。

const在*右边,this指向的空间是被锁死了的this是一个固定指向的指针,不可修改。


 经典试题:

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
 void Print()
 {
 cout << "Print()" << endl;
 }
private:
 int _a;
};
int main()
{
 A* p = nullptr;
 p->Print();
 return 0;
}

提示:空指针存在时不报编译错误,而是在需要对空指针解引用时发生运行错误(而不是编译错误)。

单纯的对this赋空是不可以的(因为存在的this都被const修饰过),可以强转直接赋空,不过一般不进行这样的操作


我们先通过p!=nullptr时的情况来看: 

                                                    

p的意义:1.让编译器知道p是A类型的指针,去A类型的公共函数代码段中找成员函数。(而不是通过this指针去对象中找)

                 2.将p的指传给this

所以,并不是所有的“->”都会执行解引用。如步骤2,单独传一个空指针是不会报错的

综上所述,并没有真正的通过p这个空指针去找对象中的成员变量,甚至可以打印出this的值:

应该选c,可以正常运行。

对题目稍加修改:

class A
{ 
public:
    void PrintA() 
   {
        cout<<_a<<endl;
   }
private:
 int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}

此时会运行崩溃,欢迎将原因写在评论区!


this指针存放在哪里?

 
通过观察Init函数的调用的汇编代码,我们发现this会被当作形参传入。
所以this会被放在栈或者寄存器上(不同编译器的实现不同)

3.类的默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下 6 个默认成员函数:初始化、清理、拷贝、复制、取地址、重载
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

3.1构造函数

       大家在使用C语言实现各种数据结构时,是否经常出现忘记调用Init来初始化创建的变量这一情况?

不初始化,会导致程序崩溃或变量中是随机值,发生错误。

为此,我们使用构造函数来解决、优化这个问题。

从此,Init可以退出历史舞台了。

特点:自动调用,无返回值(不需要写void),函数名与名相同。

构造函数虽然名字中带构造二字,但其本意为初始化成员变量(类似于之前Init函数的功能)

而不是给变量开空间。

但由于在用类的名称去实例化一个对象时就会有分配空间的作用,所以在结果上,是既开了空间又初始化了变量。

使用构造函数:

     在主程序中我们只用类名实例化一个类,没有调用与Init有关的其他函数:

                                       

构造函数的重载:

可以写多个构造函数,可以有多种初始化的方法。

如上文中我们实现了无参的构造函数,现在还可以实现带参的构造函数

class Date {
public:
	Date() {
		_year = 2000;
		_month = 1;
		_day = 1;
	}
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void DatePrint() {
		cout << _year << "." << _month << "." << _day << endl;
	}
private:
	int _day;
	int _month;
	int _year;
};

 带参的构造函数在使用时非常特殊:

                                          

d1是被无参构造函数初始化的,d2是被带参构造函数初始化的。 

带参的构造函数在使用时非常特殊,类型+对象名+参数列表。

那为什么无参构造函数在调用时不能加一个空括号呢?

第一,不加空括号,直接类型名+对象名更符合我们印象中的对一个对象的实例化。

第二,加了括号:Date d1();会与定义一个叫作d1、返回类型是Date、没有参数的函数的指令发生冲突。

我们再对构造函数进行优化:

          

使用全缺省的参数设置方式,这样,只需要显式实现一个函数就既满足了无参调用,也满足了含参调用。

两种函数自然构成重载。

不过此处的语句:Date d1;会发生歧义,因为当你决定不传参数时,编译器会不知道该调用哪一个(无参还是不传参数的全缺省)。

所以,一般的惯例下,每一个类都使用全缺省的构造,非常好用。提醒:若将全缺省函数的声明和定义分离,请只在声明处写全缺省的参数,否则会报错。

 如果我们没有显式写构造函数呢?

如果我们不写,编译器会任然以类名为构造函数名,自动生成一个无参构造函数。

class Person {
private:
	int _age;
	char _name[20];
	char _gender[6];
public:
	void PrintPersonInfo();
};

 观察一下初始化成什么样了:

 原因如下:

C++ 把类型分成内置类型 ( 基本类型 ) 和自定义类型。内置类型就是语言提供的数据类
型,如: int/char/指针... ,自定义类型就是我们使用 class/struct/union 等自己定义的类型。
自己生成的构造函数没有规定是否要处理自定义类型成员变量(有的编译器会处理,有的编译器不会处理),对于自定义类型变量才会调用他的 “不传参就能使用的构造,包括无参构造和全缺省参数构造”

注意!!!

        如果没有“不传参就能使用的构造(包括无参构造、全缺省参数构造、自动生成的构造函数)”,会报错。也就是说,只要不是自己只写了显式的需要传参的构造即可。  如果你写了显式的需要传参的构造,就会报错,除非使用“初始化链表”,这在之后的篇章中会讲解
  这是一个类似于递归的方法(但是不需要回溯),最小子问题就是调用无参构造或者只剩内置类型从而不再做处理

自定义类型自动初始化的实例:

之前的C语言实现了用两个栈实现一个队列。

如果我们的类Stack中是有合适的“不传参就能使用的构造”,我们将两个栈当作自己的成员变量                                              

那么,我们只需要实例化一个MyQueue

MyQueue q;

 MyQueue会自动调用其编译器自动生成的无参构造,_pushst和_popst都是自定义类型,所以编译器又可以继续自动调用他们两的“不需要参数的构造”,从而成功初始化两个栈。

(不传参数的构造函数就是默认构造)

自定义类型的尽头是内置类型,为了弥补内置类型类型任然不能有效初始化的问题,C++组委会作出如下补丁:

                                    

如果变量处和构造函数处都写,则:

       每一个被实例化的类,会先将自己的变量按照private中的补丁写法赋值,接着再按照构造函数中的数值去赋值。   

       

总之:不要偷懒,多自己实现构造函数


3.2析构函数

同理,在C语言中,经常忘记调用Destroy(),导致内存泄漏,内存泄漏危害很大,但是不会报错。对象生命周期结束时,C++编译系统系统自动调用析构函数

析构函数特点:

1. 析构函数名是在类名前加上字符 ~
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载(析构没有参数,不能重载(生成的被修饰过的函数名都是一样的,详见主页中的 C++学习笔记(二)),所以只能有一个析构函数 )
4. 对象生命周期结束时, C++ 编译系统系统自动调用析构函数

栈上的变量在栈帧结束时会自动销毁,析构函数的作用主要是资源清理,如(malloc开出的堆上的空间和fopen打开的文件)

我们用以括号匹配的问题来举例:

如果没有使用malloc或者fopen等开辟空间,如我们上述的Date类,就不需要使用析构函数(上文中一直使用的Date类,在销毁时就不需要使用析构函数,变量都开在栈上)

   析构函数相对于构造函数就简单很多。没有参数、没有返回值,在生命周期结束之后自动调用。相似于构造函数,如果我们没有自己实现析构函数,编译器会自动生成析构函数,不会释放内置类型(内置类型本身也会自动释放),调用自定义类型的析构函数。如果有需要显式清理的资源,如在栈或队列中malloc了一个数组,链表中malloc了一个节点,就需要显示清理(注意判空防止对空对象进行free)。


3.3拷贝构造

现在我希望创建一个与d1的值一样的d2,希望进行一次"CV"

拷贝构造,即拷贝初始化,用同类型的对象初始化新对象。

            

1. 拷贝构造函数 是构造函数的一个重载形式(一种特殊的构造)
2. 拷贝构造函数的 参数只有一个 必须是类类型对象的引用 ,使用 传值方式编译器直接报错
因为会引发无穷递归调用(会在后文中解释)
拷贝构造函数 只有单个形参 ,该形参是对本 类类型对象的 引用 ( 一般常用 const 修饰 ) ,在用 已存
在的类类型对象创建新对象时由编译器自动调用

为什么必须使用引用?

若不使用引用,会导致无穷递归。

3.3.1显式实现拷贝构造及无穷递归之谜

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

  (执行拷贝构造之前,依然会先执行声明成员变量时缺省的参数,可通过调试观察顺序)

如果不使用&,而是直接传值:

编译器非常聪明,将这样可能发生的无穷递归视为语法错误:

我们如果不加&,那么就涉及一个传值传参的问题,又由于传值传参对于自定义类型要调用拷贝构造,因此无穷递归产生 

自定义类型的传值传参需要调用拷贝构造,这是一种规定,我们也可以检查如下。

class Date
{
public:
	/*Date()
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}*/
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(Date& d) {
		cout << "调用拷贝构造" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print() {
		cout << _year;
	}
private:
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

void f(Date d) {
	cout << "f()" << endl;
}

int main() {

	Date d1;
	f(d1);//传值调用f()
	return 0;
}

                            

因此,在调用f()之前确实调用了拷贝构造。

规定:自定义传值传参调用拷贝构造

                              

假设我们希望使用拷贝构造将d3按照d2的值进行实例化,那么就需要先将d2的值传给d,因此新的拷贝构造产生:先要将d按照d2的值进行实例化,再次调用新的拷贝构造.........

因此,无穷递归产生。

为什么用引用而不是用指针?

使用指针会造成无穷递归吗?

指针当然不会造成无穷递归(其底层与引用相同)

规定:使用引用而不用指针。拷贝构造的定义就是使用引用,使用指针当然没有问题,但由于定义,使用指针就是一个普通的构造函数

为什么说经常规定要在形参中加const? 

                              

    为了避免赋值顺序给反的问题,我们使用const来进行限制。

                               

我们在Date(const Date& d)的实现后,用d2实例化d3

Date d3(d2);   由此,又用到了C++入门(以c为基础)——学习笔记2-CSDN博客中所讲到的关于权限的知识:

d2是可读可写的,用const修饰d就缩小了d的权限因而d只是可读的,因此在权限层面也是可以的

并且,d3就是此处的this指针:

                               


为了方便使用,也可以直接使用等号来执行拷贝构造:

                           


3.3.2自动生成的拷贝构造 

如果没有手动实现拷贝构造,会自动生成拷贝构造。

默认的拷贝构造函数对象按内存存储按
字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
类似于memset的逻辑,自动生成的拷贝构造会按照一个字节一个字节的拷贝,如果一个即将被拷贝构造的对象中有需要管理的资源(如一个顺序表、一个链表、一棵树等),这样的资源通常只存一个指针在对象中,这样就会把一个同样的指针指向的同一块空间交给两个不同的对象,两个对象都可以分别访问这一个空间,对其增删查改,最后还会调用两次析构,埋下非常大的坑。

导致析构两次:(对同一个空间free两次会导致崩溃)

tips:深拷贝与浅拷贝是cpp中非常经典的概念,正在学习的各位应该当熟练理解。


因此,需要深拷贝的类的拷贝构造需要我们自己显式实现

一定有同学会问:如果该对象中存放的不是int*  ,  而是一个int arr[100]这样的数组呢?

这样的数组是开在每一个对象中的,所以也只需要浅拷贝。 

在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定
义类型是调用其拷贝构造函数完成拷贝的。因此MyQueue等只有两个栈作为变量的类,也不需要自己实现拷贝构造。  

所以,一般情况下:需要写析构的类(有资源管理),就需要显式实现拷贝构造

由于类可能出现的层层嵌套,针对每一层,若当前层有需要直接管理的资源而没有实现相应的深层拷贝构造函数,就会出问题。

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

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

相关文章

Python+selenium的web自动化之元素的常用操作详解

前言 今天呢&#xff0c;笔者想和大家来聊聊pythonselenium的web自动化之元素的常用操作&#xff0c;废话不多说直接进入主题吧 一、常用操作 关键代码&#xff1a; 点击&#xff1a;ele.click()输入内容&#xff1a;ele.send_keys("内容")清空内容&#xff1a;el…

conda配置多版本python

安装conda 以下任选下载 Anaconda Miniconda 配置conda环境变量 比如windows&#xff0c;在配置我的电脑中的环境变量&#xff0c;在系统变量的Path中新增下面内容 需要根据实际目录进行更改 D:\soft\miniconda3 D:\soft\miniconda3\Scripts D:\soft\miniconda3\Library\bi…

嵌入式驱动学习第七周——GPIO子系统

前言 GPIO子系统是用来统一便捷地访问实现输入输出中断等效果。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可以关注本博主并订阅本专栏&#xff0c;一起讨论一起学习。现在关注就是老粉啦&#xff0…

云卓LS-01喊话器说明书-新版中文

一: 概述 LS-01 无人机喊话器适用于搭载无人机进行交通管制、现场指挥、应急救援、人群疏导、防疫宣传、景区安防、鱼塘巡视、林业防控等场景。产品具有喊话、警报、播放多媒体文件等多种功能。喊话器外壳采用尼龙加纤材质&#xff0c;具有抗、抗震、轻便灵活、外观新颖、质量稳…

NXopen C++ 装配体部件遍历 体积质量计算 NewMassProperties

打开装配体&#xff0c;逐一遍历部件测量体积&#xff0c;最后计算出装配体的总的体积和质量 NX1953VS2019 //1、模板文件添加头文件* #include <NXOpen/UnitCollection.hxx> #include <NXOpen/ScCollectorCollection.hxx> #include <NXOpen/Unit.hxx> #inc…

github 双因素验证

环境 华为手机 下载app 华为应用市场下载 输入对应验证码&#xff0c;然后一路下一步即可 联系方式 手机&#xff1a;13822161573 微信&#xff1a;txsolarterms QQ&#xff1a;419396409

实验一:配置IP地址

实验环境 主机A和主机B通过一根网线相连 需求描述 为两台主机配置IP地址&#xff0c;验证IP地址是否生效&#xff0c;验证同一网段的两台主机可以互通&#xff0c;不同网段的主机不能直接互通 一.实验拓扑 二.推荐步骤 1.为两台主机配置P地址&#xff0c;主机A为192.168.1.…

多无人机集群协同避障

matlab2020a正常运行 场景1规划结果 场景2规划结果 场景3规划结果 代码地址&#xff1a; 多无人机集群协同避障效果&#xff08;5架&#xff09;资源-CSDN文库

解决Ant Design Vue使用Modal对话框无法关闭的问题,本地可以关闭对话框但是打包后不能关闭对话框的问题。

首先说为什么会导致这个问题&#xff1a;因为现在vue官方的最新版本是3.4.x&#xff0c;可能是vue最新版本的部分代码与Ant Design不兼容导致的。 解决&#xff1a;所以将vue版本固定在vue3.4以下&#xff0c;就可以了。 1.删除node_modules和package-lock.json&#xff08;如…

8.基础乐理-纯八度

一个音听上去是高还是低&#xff0c;是由音的振动频率决定的&#xff0c;也就是每一秒多少 Hz 所决定的&#xff0c;Hz数越小音听上去就越低&#xff0c;Hz数越大音听上去就越大&#xff0c;钢琴从左到右&#xff0c;Hz数 在逐渐增大&#xff0c;当两个音的振动频率数越接近简单…

VSCode插件分享--免费的ER工具

首先在VSCode里面下载插件 再将插件导入后&#xff0c;添加文本修改后缀名&#xff08;将后缀名修改为.drawio&#xff09;就可以使用了

【CLR】《Cyclical Learning Rates for Training Neural Networks》

WACV-2017 IEEE Winter Conference on Applications of Computer Vision 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method5 Experiments5.1 Datasets and Metrics5.2 CIFAR-10 and CIFAR-1005.3 ImageNet 6 Conclusion&#xff08;o…

Python机器学习实战教程

一、引言 机器学习是人工智能的一个子集&#xff0c;它使用算法来让计算机系统从数据中“学习”并改进其性能&#xff0c;而无需进行明确的编程。Python因其易于学习、强大的库和广泛的应用场景&#xff0c;成为了机器学习的首选语言。本教程旨在帮助读者从零开始学习Python机…

(六)C++自制植物大战僵尸游戏关卡数据讲解

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/xjvbb 游戏关卡数据文件定义了游戏中每一个关卡的数据&#xff0c;包括游戏类型、关卡通关奖励的金币数量、僵尸出现的波数、每一波出现僵尸数量、每一波僵尸出现的类型等。根据不同的游戏类型&#xff0c;定义了不同的通…

顶顶通呼叫中心中间件-回铃音补偿(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-回铃音补偿(mod_cti基于FreeSWITCH) 回铃音的用处 回铃音&#xff1a; 当别人打电话给你时&#xff0c;你的电话响铃了&#xff0c;而他听到的声音叫做回铃音。回铃音是被叫方向主叫方传送&#xff0c;也是彩铃功能的基础。我们平时打电话听到的“嘟 嘟…

掌握CRM+邮箱技巧:销售速度与客户信任双丰收

在千行百业都在谈提效的今天&#xff0c;如果您的销售团队效率较低&#xff0c;恐怕很难过好2024。销售团队提效是个大话题&#xff0c;总的说来就是销售团队需要在正确的时间做正确的事。如何做到&#xff1f;自然要借助CRM工具。过去我们也讲了不少CRM如何辅助销售团队提效的…

外贸高手写的开发信为什么回复率很高

关于开发信这个主题&#xff0c;其实已经算是个烂大街的话题。但是在效仿、参考、摸索开发信这一课题的路上&#xff0c;很多小白还是没摸准要旨&#xff0c;走了不少弯路。这也是为什么小编老话题重提的原因。以下整理了外贸高手写开发信用到的技巧和规律&#xff0c;希望能给…

华为配置通过流策略实现流量统计

配置通过流策略实现流量统计示例 组网图形 图1 配置流策略实现流量统计组网图 设备 接口 接口所属VLAN 对应的三层接口 IP地址 SwitchA GigabitEthernet1/0/1 VLAN 10 - - GigabitEthernet1/0/2 VLAN 20 - - GigabitEthernet1/0/3 VLAN 10、VLAN 20 - - S…

公众号文章的制作方法和步骤分享,纯干货!

无论是个人还是企业&#xff0c;都需要通过公众号来传递自己的声音和价值。如何制作一篇高质量的公众号文章呢&#xff1f;本文伯乐网络传媒将详细介绍公众号文章的制作方法和步骤。 一、文章类型概述 1. 图文教程&#xff1a;以图文结合的方式&#xff0c;向读者传授知识和技…

网络协议——BGP(边界网关协议)全网最详解

1. 什么是AS&#xff1f; AS: 指的是在同一个组织管理下&#xff0c;使用统一选路策略的设备集合&#xff0c;AS取值范围四字节&#xff08; 0~43亿&#xff09; 2. BGP概念 BGP是边界网关协议&#xff0c;用于自治系统间的动态协议路径矢量。基于TCP中应用层协议&#xff0c…