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

【C++】类和对象(中)

文章目录

  • 1. 类的6个默认成员函数
  • 2. 构造函数
    • 概念
    • 特性
  • 3. 析构函数
    • 概念
    • 特性
  • 4. 拷贝构造函数
    • 概念
    • 特征
  • 5. 运算符重载
    • 5.1 前置++和后置++重载
    • 5.2 赋值运算符重载
  • 6. 日期类的实现
  • 7. const成员
  • 8. 取地址及const取地址操作符重载


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

如果一个类中什么成员都没有,简称为空类

class Date {};

其实在空类中并不是什么都没有,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数

在这里插入图片描述

默认成员函数: 用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

当我们平时在写数据结构的代码时,例如在写的代码时,可能会忘记初始化就去进行增删查改操作,在我们写完代码后可能忘记将我们初始化好的栈释放掉。这样会导致我们的代码出现一系列的问题,但我们又无法避免,所以C++在C语言的基础上增加了类的默认成员函数。其中构造函数、析构函数、拷贝构造和赋值运算符重载是我们重点讲解的内容。剩下两个平常用的很少,遇到的时候我们在后续讲解。


2. 构造函数

概念

当我们在使用类来初始化一个对象时,通常都需要调用他的Init公有方法给对象进行初始化。未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?

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

构造函数有如下特性:

  • 函数名与类名相同。
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载。
  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
  • 构造函数对内置类型不做处理,对于自定义类型会调用他的默认构造函数。
  • 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

我们先来上手一个构造函数,来看一下他如何用。

在这里插入图片描述

我们可以看到编译器帮助我们自动调用了构造函数,而构造函数的功能就是帮助我们初始化对象,下面我们来看一下构造函数的几大特性。


特性

💕 (1) 构造函数支持重载和缺省参数

class Date
{
public://无参构造函数Date(){_year = 1949;_month = 10;_day = 1;}//有参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();Date d2(2023,2,5);d2.Print();return 0;
}

在这里插入图片描述

这里我们可以看到构造函数是支持重载的,但是我们也需要注意一点:无参构造和有参全缺省的构造函数不能同时出现,因为在调用的时候会出现歧义。

在这里插入图片描述

最后我们还需要注意的是,当我们在调用无参构造或者全缺省构造来初始化对象时,不要再对象后面带括号,因为这会导致编译器分不清我们是在实例化对象还是再进行函数声明。

💕 (2) 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

在这里插入图片描述

虽然编译器会给我们自动生成一个默认构造函数来完成初始化,但是为什么初始化的结果是一个随机值呢?这是因为构造函数对内置类型不做处理。下面我们会讲。

💕 (3) 构造函数对内置类型不做处理,对于自定义类型会调用他的默认构造函数。

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自定义的类型。

这里我们使用stack类、MyQueue类、Date类三者进行一下对比:

class Date
{
public:void Print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
};class MyQueue {
public:// 默认生成构造函数,对自定义类型成员,会调用他的默认构造函数// 默认生成析构函数,对自定义类型成员,会调用他的析构函数void push(int x) {}//....Stack _pushST;Stack _popST;int _size = 0;
};
int main()
{Date d1;d1.Print();MyQueue q;return 0;
}

在这里插入图片描述

我们可以看到,编译器默认生成的构造函数对不会对内置类型进行处理,但是我们可以看到编译器调用了两次构造和两次析构函数对MyQueue类中的自定义类型进行了处理,而编译器调用的,恰恰是自定义类型中的默认构造函数。

所以,我们不必纠结什么时候自己提供构造函数,什么时候使用编译器提供的默认构造函数,当编译器提供的默认构造函数能够满足我们的需求的时候,我们自己就不用提供构造函数,否则的话我们就需要自己提供构造函数。

注意: C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值

class Date
{
public:void Print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private://在类中声明时为内置类型提供默认值int _year= 1;int _month = 1;int _day = 1;
};

💕 (4) 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

我们知道,默认构造函数有三种:编译器自动提供的无参构造函数、自己定义的无参构造函数、自己定义的全缺省的构造函数

但是,如果我们提供了有参构造函数,编译器就不会提供默认构造函数。


3. 析构函数

概念

与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

这里我们先来简单看一下析构函数的用法:

在这里插入图片描述

我们可以看到,析构函数和构造函数一样,都是由编译器帮助我们自动调用。

析构函数的特性:

  • 析构函数名是在类名前加上字符 ~。
  • 无参数无返回值类型。
  • 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  • 对象生命周期结束时,C++编译系统系统自动调用析构函数
  • 和构造函数一样,编译器生成的默认析构函数,对自定类型成员调用它的析构函数,对内置类型不做处理。

特性

那么我们什么时候需要使用析构函数呢?很显然,如果我们的类中没有进行资源的申请(动态开辟内存,打开文件等操作时),那么这个类在函数调用自动销毁时不需要我们做任何处理。所以,我们不需要写析构函数。

但如果我们定义的类中的成员变量指向了一块动态开辟的内存空间,那么我们就必须自己手动写析构函数,如果不处理就会造成内存泄露。

这里我们以MyQueue类作为例子,可以看到和构造函数一样,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。

在这里插入图片描述


4. 拷贝构造函数

概念

在创建对象时,我们可以创建一个与已存在的对象一摸一样的新对象,那么这个功能就由我们的拷贝构造函数来完成。

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用。

下面我们先来看一下拷贝构造函数的基本用法:

在这里插入图片描述

拷贝构造函数的特性:

  • 拷贝构造函数是构造函数的一个重载形式。 当我们使用拷贝构造实例话一个对象时,编译器不在调用构造函数。
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
  • 若未显示定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
  • 默认的拷贝构造函数对内置类型以字节为单位直接进行拷贝(浅拷贝),对自定义类型调用其自身的拷贝构造函数。

特征

💕 (1) 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

在这里插入图片描述

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

当我们使用传值的方式写拷贝构造函数时,编译器会直接报错,这是因为可能会引发无穷递归。

那么为什么会引发无穷递归呢?原因是当我们创建d2对象时,编译器会自动调用拷贝构造函数,但是传值调用的时候形参是实参的一份临时拷贝,所以形参需要先拷贝一份实参,而拷贝的过程又需要调用拷贝构造函数,所以这样下去就会导致无穷递归。

在这里插入图片描述

所以我们必须使用引用来做形参,这样的话在调用拷贝构造的时候,形参就是实参自己,那么就不需要再拷贝一份实参,拷贝构造函数直接就可以完成对待初始化对象的初始化。当然,我们在写拷贝构造函数的时候,一般都在前面加const,这样就可以避免对原对象的修改。

💕 (2) 若未显示定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

如果我们没有实现拷贝构造函数,编译器给我们自动生成一个默认拷贝构造函数。这个默认的拷贝构造函数对内置类型会完成浅拷贝,但如果我们的内置类型有进行资源的申请(例如:动态开辟内存)时,就会出现严重的问题,下面我们举例来看。

typedef int DataType;
class Stack {
public://构造函数Stack(){cout << "构造函数Stack()" << endl;_array = (DataType*)malloc(sizeof(DataType) * 3);if (!_array){perror("malloc fail::");exit(-1);}_capacity = 3;_top = 0;}//...//析构函数~Stack(){cout << "析构函数~Stack()" << endl;if (_array){free(_array);_array = nullptr;}_top = _capacity = 0;}
private:DataType* _array;int _top;int _capacity;
};
int main()
{Stack st1;Stack st2(st1);return 0;
}

在这里插入图片描述
在这里插入图片描述

当编译器自动生成的默认拷贝构造函数进行初始化栈时,我们发现两个对象中的_array指针竟然指向了同一块空间。这是因为编译器默认生成的拷贝构造函数拷贝数据时,是按照byte进行拷贝的,那么拷贝的时候自然也会将st1的地址拷贝给st2,这就导致了st2和st1指向了同一块空间。当主函数结束的时候,需要调用析构函数来销毁两个对象,但是由于两个对象中的_array都指向了同一块空间,所以导致了同一块空间被释放了两次。

在这里插入图片描述

这种情况不仅析构两次有问题,而且在插入数据时,一个对象的改变必然会覆盖另一个对象的数据,所以这种浅拷贝的问题会造成严重的后果,那么我们应该如何避免这种情况的发生呢?这个时候我们就得写一个深拷贝拷贝构造函数了。

Stack(const Stack& s){_array = (DataType*)malloc(sizeof(DataType) * s._capacity);if (!_array){perror("malloc fail::");exit(-1);}_top = s._top;_capacity = s._capacity;}

在这里插入图片描述

当然了,如果一个类的成员变量都是自定义类型的话,那么默认成员函数就会去调用成员变量的拷贝构造函数。

在这里插入图片描述

所以我们什么时候需要写拷贝构造函数呢?很简单,如果类中有资源申请我们就必须手动实现拷贝构造函数。如果没有的话那就直接使用类提供的默认拷贝构造即可。

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

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

5. 运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

  • 函数名字:关键字operator后面接需要重载的运算符符号
  • 函数原型返回值类型 operator操作符(参数列表)

对于运算符的重载我们需要注意几点:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

我们知道,类中的成员变量一般都是私有的,我们并不能在类外直接修改他们,所以当我们使用重载后的运算符去进行类实例化的对象的操作时,一般都会把运算符在类内进行重载。当我们在类内进行运算符重载时,只需要传递一个一个参数(右操作数),因为左操作数就是编译器给我们自动传递的this指针。这里我们需要注意,当我们将函数放在类内时,不管操作数有几个,this都默认指向第一个操作数。


5.1 前置++和后置++重载

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//重载前置++Date& operator++(){_day += 1;return *this;}//重载后置++Date operator++(int){Date temp(*this);_day += 1;return temp;}
private:int _year;int _month;int _day;
};

这里我们需要注意的是:

  • 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载,C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。
  • 后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1,而temp是临时对象,因此只能以值的方式返回,不能返回引用。

5.2 赋值运算符重载

赋值运算符重载作为类的六个默认成员函数之一,如果用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,所以像日期类这样的类是没有必要自己在去实现赋值运算符重载的了。

在这里插入图片描述

这里我们需要注意的是,Date d2 = d1是调用拷贝构造给d2完成初始化操作的,在创建对象时就会自动调用,而 Date d2;d2 = d1;这是调用赋值重载函数来给已经创建好的对象d2来完成初始化操作的。

所以,对于没有资源申请的类来说,并不需要写赋值重载函数,而对于有资源申请的类来说,我们必须显示的去调用赋值重载函数。否则不仅会导致赋值对象中动态分配的内存空间没有被释放,而且还会导致被赋值的对象指向的空格键析构两次,也就是造成了同一块空间被释放两次内存泄漏两个严重的问题。

赋值运算符重载格式

  • 参数类型: const T&,传递引用可以提高传参效率
  • 返回值类型: T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this : 要复合连续赋值的含义

当然了,如果赋值的对象是被赋值对象本身的话,我们还可以加一个特殊判定,因为虽然程序不会出现任何的问题,但是赋值本身就是一次拷贝构造,会造成消耗。同时,如果需要实现连续赋值,就必须将赋值重载函数返回对象本身。

这里我们可以实现一下日期类的赋值重载函数:

Date& operator=(const Date& d)
{if(this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}

赋值运算符只能重载成类的成员函数不能重载成全局函数

原因是,赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}

在这里插入图片描述


6. 日期类的实现

💕 Date.h

#pragma once
#include<iostream>
using namespace std;
#include<cassert>class Date
{
public:friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);//打印函数void Print()const;//构造函数Date(int year = 1, int month = 1, int day = 1);//获取某个月的天数int GetMonthDay(int year, int month);//重载==运算符bool operator==(const Date &d)const;//重载!=运算符bool operator!=(const Date &d)const;//重载<运算符bool operator<(const Date& d)const;//重载<=运算符bool operator<=(const Date& d)const;//重载>运算符bool operator>(const Date& d)const;//重载>=运算符bool operator>=(const Date& d)const;//重载+=运算符Date& operator+=(int d);//重载+运算符Date operator+(int d)const;//重载-=运算符Date& operator-=(int d);//重载-运算符Date operator-(int d)const;//重载d1-d2运算符int operator-(const Date& d)const;//重载前置++Date& operator++();//重载后置++Date operator++(int);//重载前置--Date& operator--();//重载后置--Date operator--(int);private:int _year;int _month;int _day;
};//重载左移运算符
inline ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}inline istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

💕 Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"//构造函数
Date::Date(int year, int month, int day)
{assert(month < 13 && day <= GetMonthDay(year, month));_year = year;_month = month;_day = day;
}//获取某个月的天数
int Date::GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))){return 29;}return arr[month];
}//打印函数
void Date::Print()const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}//重载==运算符
bool Date::operator==(const Date &d)const
{return (_year == d._year && _month == d._month && _day == d._day);
}//重载!=运算符
bool Date::operator!=(const Date &d)const
{return !(*this == d);
}//重载<运算符
bool Date::operator<(const Date& d)const
{return (_year < d._year)|| ((_year == d._year) && (_month < d._month))|| ((_year == d._year) && (_month == d._month) && (_day < d._day));
}//重载<=运算符
bool Date::operator<=(const Date& d)const
{return (*this == d) || (*this < d);
}//重载>运算符
bool Date::operator>(const Date& d)const
{return !(*this <= d);
}//重载>=运算符
bool Date::operator>=(const Date& d)const
{return !(*this < d);
}//重载+=运算符
Date& Date::operator+=(int d)
{if (d < 0){return *this -= -d;}_day += d;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month > 12){_year++;_month = 1;}}return *this;
}//重载+运算符
Date Date::operator+(int d)const
{Date tmp(*this);tmp += d;return tmp;
}//重载-=运算符
Date& Date::operator-=(int d)
{if (d < 0){return *this += -d;}_day -= d;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}//重载-运算符
Date Date::operator-(int d)const
{Date tmp(*this);tmp -= d;return tmp;
}//重载d1-d2运算符
int Date::operator-(const Date& d)const
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int cout = 0;while (min != max){++min;++cout;}return cout * flag;
}//重载前置++
Date& Date::operator++()
{*this += 1;return *this;
}//重载后置++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}//重载前置--
Date& Date::operator--()
{*this -= 1;return *this;
}//重载后置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}

这里我们还可以扩展两个函数:

(1)将一年中的第几天转换成日期

void DaysToDate()
{int year = 0;int days = 0;cin >> year >> days;static int Day[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };if (((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))&& (days > 60)){--days;}int month = 1;while (days > Day[month]){days -= Day[month];month++;}printf("%04d-%02d-%02d\n", year, month, days);
}

(2)计算日期是一年中的第几天

//计算日期是一年中的第几天
int Date::DayOfYear() const
{static int Day[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };int n = Day[_month - 1] + _day;if (_month > 2 && ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0)))++n;return n;
}

7. const成员

const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改

在这里插入图片描述

💕 const对象不能调用非const成员函数:

在这里插入图片描述

aa是一个const修饰的对象,他只有可读的权限,但是成员函数Print()却不被const修饰,所以它既具有可读,又具有可写的权限,const A* 类型的this指针传递给一个A* 类型的this指针时,会导致权限的放大,所以编译器会报错。我们可以在Print函数括号的右边加const,将this指针修改为const A*类型。

💕 非const对象可以调用const成员函数:

在这里插入图片描述

非const对象具有可读和可写的权限,A* 类型的this指针传递给一个const A* 类型的this指针时,是属于权限的缩小,所以没有任何问题。

💕 非const成员函数内可以调用其它的const成员函数

在这里插入图片描述

_Print()函数在调用Print()函数时,A* 类型的this指针被转换成了const A* 类型的this指针,属于权限的缩小,所以可以成功,但是,如果要是const成员函数调用其它的非const成员函数时,const A* 的this指针向A*类型的this指针转换时,属于权限的放大,编译器会报错。


8. 取地址及const取地址操作符重载

取地址操作符重载也属于我们类的六个默认成员函数之一,所以平常当我们访问对象的地址时,类也会给我们提供对应的的默认成员函数,所以一般情况下我们都不需要对取地址操作符重载。

当然了我们也可以写一下:

class A
{
public:void Print() const{cout << _a << endl;}A* operator&(){return this;}const A* operator&() const{return this;}private:int _a = 10;
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容!


相关文章:

【C++】类和对象(中)

文章目录1. 类的6个默认成员函数2. 构造函数概念特性3. 析构函数概念特性4. 拷贝构造函数概念特征5. 运算符重载5.1 前置和后置重载5.2 赋值运算符重载6. 日期类的实现7. const成员8. 取地址及const取地址操作符重载1. 类的6个默认成员函数 如果一个类中什么成员都没有&#x…...

js下载文件

url为文件的src地址 url必须符合同源策略或者url的接口地址允许跨域&#xff0c;否则浏览器会报跨域错误 axios.get(data.url ,{ responseType: ‘blob’, }) .then( response>{ let blob new Blob([response.data]); let url window.URL.createObjectURL(blob); // 创建 …...

ESP8266 + STC15+ I2C OLED带网络校时功能的定时器时钟

ESP8266 + STC15+ I2C OLED带网络校时功能的定时器时钟 📍相关篇《ESP8266 + STC15基于AT指令通过TCP通讯协议获取时间》 📌ESP8266 AT固件基于安信可AT固件,相关刷AT固件可以参考《NodeMCU-刷写AT固件》 🔖STC15 单片机采用的是:STC15F2K60S2 晶振频率采用内部:22.11…...

计算机入门基础知识大全

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽…...

Python程序出现错误怎么办?

Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。 异常处理: 本站Python教程会具体介绍。 断言(Assertions):本站Python教程会具体介绍。 python标准异常 异常名称 描述 BaseException 所有异常…...

【Vue3】v-if和v-for优先级

&#x1f388;博客主页&#xff1a;&#x1f308;我的主页&#x1f308; &#x1f388;欢迎点赞 &#x1f44d; 收藏 &#x1f31f;留言 &#x1f4dd; 欢迎讨论&#xff01;&#x1f44f; &#x1f388;本文由 【泠青沼~】 原创&#xff0c;首发于 CSDN&#x1f6a9;&#x1f…...

Windows上实现 IOS 自动化测试

本文介绍如何使用tideviceWDAairtest/facebook-wda实现在Windows上进行IOS APP自动化测试 环境准备 Windows Python环境 Python 3.6 WebDriverAgent安装 下载最新的项目到Mac&#xff1a;https://github.com/appium/WebDriverAgent $ git clone https://github.com/appiu…...

Linux云服务器下怎么重置MySQL8.0数据库密码

文章目录一、修改my.cnf配置文件为mysql免登陆二、免密登陆mysql三.给root用户重置密码1、首先查看当前root用户相关信息&#xff0c;在mysql数据库的user表中2、把root密码置为空3、退出mysql&#xff0c;删除/etc/my.cnf文件中添加进去的skip-grant-tables 重启mysql服务4、使…...

JVM调优

JVM调优-VisualVmVisualVm/ Jconsule远程连接第一种方式第二种方式&#xff1a;java 11开启远程GC连接如果还连不上考虑防火墙拦截了端口firewall-cmd --list-all,查看一下并暴露对应端口连接配置VisualVm界面简介采集GC信息的一些命令垃圾回收器切换VisualVm/ Jconsule远程连接…...

【配电网规划】SOCPR和基于线性离散最优潮流(OPF)模型的配电网规划( DNP )(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

锦正茂EM3电磁铁的技术参数

产品特点&#xff1a; ※U形结构、视野开阔、磁场强度高、磁场强度大小调节方便 ※体积小、重量轻、占空比小、结构紧凑、磁场性能更佳 ※电磁铁的工作气隙调节轻便灵活&#xff0c;极头处设有螺纹&#xff0c;更换极头装卸方便 ※可选配工作间隙刻度指示 ※小气隙时用于铁…...

Go最新版下载 Go1.20版新特性

Go官方正式发布了Go1.20稳定版 该版本依然保持 Go1 兼容性&#xff0c;可以升级到 Go1.20&#xff0c;而不需要做任何代码改动。 可以使用你任何喜欢的方式升级&#xff1a; 比如&#xff1a; go install golang.org/dl/go1.20latest 具体的可以参考官网教程&#xff1a; ht…...

Pywirt:一款基于Python的Windows安全应急响应工具

关于Pywirt Pywirt是一款基于Python开发的网络安全工具&#xff0c;该工具专门针对Windows操作系统设计&#xff0c;可以帮助广大研究人员使用winrm并通过在Windows操作系统上收集各种信息来加快安全事件应急响应的速度。 该工具已在Windows 10操作系统上进行过完整测试。 功…...

KDZD832 智能蓄电池活化仪

一、产品概述 KDZD832 智能蓄电池活化仪&#xff08;2V-24V 一体机&#xff0c;适用于 2V、6V、12V/24V 蓄电池&#xff0c;以下简称活化仪&#xff09;&#xff0c;是专用于日常维护中对落后蓄电池处理的便携式产品&#xff0c;它具有四种独立的使用方式&#xff1a;电池放电…...

纯css实现loading加载中(多种展现形式)

前言 现如今网页越来越趋近于动画&#xff0c;相信大家平时浏览网页或多或少都能看到一些动画效果&#xff0c;今天我们来做一个有意思的动画效果&#xff0c;纯 css 实现 loading 加载中&#xff08;多种展现形式&#xff09;&#xff0c;下面一起看看吧。 1. 常规 loading 实…...

【面试题】2023 vue高频面试知识点汇总

一、MVVM原理在Vue2官方文档中没有找到Vue是MVVM的直接证据&#xff0c;但文档有提到&#xff1a;虽然没有完全遵循MVVM模型&#xff0c;但是 Vue 的设计也受到了它的启发&#xff0c;因此在文档中经常会使用vm(ViewModel 的缩写) 这个变量名表示 Vue 实例。为了感受MVVM模型的…...

跨境电商选品重要吗?

选品很重要&#xff01;跨境电子商务选择的核心要求&#xff1a;优质商品&#xff0c;价格优势&#xff0c;符合跨境销售特点&#xff0c;满足目标海外市场需求&#xff0c;突出自身特色竞争优势。跨境电商是如何选择产品的&#xff1f;这个问题也很流行&#xff0c;应该考虑以…...

SpringBoot

这里写目录标题1.入门程序1.1 spring-boot-starter-parent1.2 启动器1.3 EnableAutoConfiguration(重要)1.4 如何注册多个Controller?1.5 引导类2.完整的SpringBoot项目2.1 启动类2.1.1 创建一个启动类2.1.2 扩展: SpringBootConfiguration2.2 使用配置类定义组件2.3 SpringBo…...

python--turtle

前言 就随便练练&#xff0c;学习一下turtle库的使用 正文 1.语法学习 import turtle #导入库 turtle.showturtle() #画笔显示箭头 turtle.write("我是大帅逼") #写下字符串 turtle.forward(300) …...

NodeJS的后端Express项目部署到Ubuntu服务器,为前端提供API服务

之前参与的web3项目后端是用NodeJS开发的&#xff0c;因为可以共用NPM库&#xff0c;采用的Express框架&#xff0c;第一次弄&#xff0c;记录下大致的部署过程如下&#xff1a; 1、服务器上安装NodeJS sudo apt-get install nodejs 2、安装全局NPM工具&#xff0c;node_mod…...

作为研发如何使用Github Api?

文章目录使用步骤账号创建进行开发者相关设置API操作演示Github API好处推荐的Github API&#x1f31f;个人主页: 个人主页 &#x1f6b5;‍♀️个人介绍:每天进步一点点&#xff0c;生活变得好一点点。 &#x1f4cc;作为一位开发&#xff0c;不管是非工作的还是工作中的人士&…...

Java volatile学习

面试题&#xff1a; 1、请谈谈你对volatile的理解&#xff1f; volatile是Java虚拟机提供的轻量级的同步机制1.保证可见性2.不保证原子性3.禁止指令重排 2、JMM你谈谈?Java内存模型 3、你在哪些地方用到过volatile?单例模式CAS底层代码 目录 一、概述 1、可见性 2、原子性…...

用神经网络分类上和下

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 做一个网络&#xff0c;输入为3个点&#xff0c;训练集A,B各有4张图片。让B的4张图片全是0.排列组合A&#xff0c;记录迭代次数平均值的变化。收敛误差为7e-4&#xff0c;每个网络收敛199次。 其中得到一组数据 差值结构 1-A-B 迭代次…...

VS Code 1.75 发布!

欢迎使用 2023 年 1 月版的 Visual Studio Code。希望您喜欢此版本中的许多更新&#xff0c;其中一些主要亮点包括&#xff1a;配置文件、VS Marketplace 签名、辅助功能改进、更轻松地调整多视图大小、树视图搜索历史、新的 Git 命令等等。让我们一起看看吧&#xff01; 配置文…...

Vue2仿网易云风格音乐播放器(附源码)

Vue2仿网易云风格音乐播放器1、整体效果2、使用技术3、实现内容4、源码5、使用图片1、整体效果 2、使用技术 使用了HTML5 CSS3进行页面布局及美化使用Vue2进行数据渲染与页面交互使用Axios发送http请求获取数据 3、实现内容 实现了搜索歌曲功能&#xff0c;输入歌手或歌曲关…...

Spring相关面试题

文章目录请谈一下你对 spring 的理解&#xff1f;说一下 Spring 的核心是什么&#xff1f;请谈 一下你对 Spring IOC 和 和 AOP 的理解&#xff1f;请说一下 Spring 的 的 Bean 作用域&#xff1f;请谈一下Spring中bean对象的生命周期&#xff1f;Spring中的事务是如何实现的 &…...

操作符详解(上篇)

前言小伙伴们大家好&#xff0c;随着对c的不断学习今天我们将来学习操作符。在初始c语言中也介绍过操作符但也只是点到即可&#xff0c;今天我们将详细了解操作符。操作符分类&#xff1a;算术操作符移位操作符位操作符赋值操作符单目操作符关系操作符逻辑操作符条件操作符逗号…...

采样电路的3个组成部分

采样电路的使用实际上是电路的一个闭环控制过程&#xff0c;也可以理解为一个负反馈过程&#xff0c;采集的信号被传送到主控制芯片进行调整。今天就来为您介绍一下采样电路的三个组成部分分析&#xff01;一起来看看吧&#xff01; 这里的采样实际上分为电流采样、电压采样、…...

ffmpeg硬解码与软解码的压测对比

文章目录ffmpeg硬解码与软解码的压测一、基本知识二、压测实验1. 实验条件及工具说明2. 压测脚本3. 实验数据结果ffmpeg硬解码与软解码的压测 一、基本知识 本文基于intel集显进行压测 软解码&#xff1a;cpu对视频进行解码硬解码&#xff1a;显卡或者多媒体处理芯片对视频进…...

操作符——“C”

各位CSDN的uu们你们好呀&#xff0c;今天&#xff0c;总算是要到我们的操作符啦&#xff0c;在C语言中&#xff0c;操作符是一个极为复杂的东西&#xff0c;下面&#xff0c;就让我们进入操作符的世界吧 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符…...

wordpress 插入文章/河北网站建设案例

KUKA机器人码垛程序怎么写(案例)注&#xff1a;本文章文字、图片部分来自网络版权归原作者&#xff0c;侵删。工博士提供了KUKA&#xff0c;Yaskawa&#xff0c;ABB&#xff0c;Kawasaki和FANUC等各种新型机器人。我们相信&#xff0c;我们真正地在协助第四次工业革命的进步&am…...

免费创办网站/html网页制作app

大小&#xff1a;55.24MB语言&#xff1a;简体分类&#xff1a; 网页辅助版本&#xff1a; 电脑版立即下载 查看详情本文将给大家介绍如何使用八爪鱼采集器采集分页列表页面上的信息&#xff0c;并附带给大家讲一下ajax延时设置。目的是让大家了解怎么创建循环翻页并能正常采集…...

沈阳网站建设推广/西安网站制作推广

博客今天发表想写点近来看的智能机器的小册子&#xff0c;感觉深刻的是整个step的规划&#xff0c;就像一般软件项目开发的过程与整体规划。主体是跨步倾斜两个电机的过程先后判断&#xff0c;智能机器人亦步亦趋的行走着&#xff0c;采用at89s52控制过程简单&#xff0c;实用性…...

贵阳网站设计与开发怎么做/服装店营销策划方案

为什么80%的码农都做不了架构师&#xff1f;>>> 仅仅为了获取函数名&#xff0c;就在函数体中嵌入硬编码的字符串&#xff0c;这种方法单调乏味还易导致错误&#xff0c;不如看一下怎样使用新的C99特性&#xff0c;在程序运行时获取函数名吧。 对象反射库、调试工…...

平面设计培训学校一年学费/关键词优化搜索引擎

通过创建表方式和 数据向导方式都可以成功创建数据 文件&#xff0c;操作员可以随意选择自己习惯的方式。总之&#xff0c;能坚守数据文件放数据的原则&#xff0c;就不会出问题了。当回到“ 参数属性 页面”中后&#xff0c;发现数据已经准备好了&#xff0c;而且原来灰色的区…...

域名备案要先做网站的吗/合肥做网站推广

笔者原以为是个挺容易个事儿, 毕竟是微软自家的产品安装在自家的操作系统上, 没想到还是让我费了半天劲. 写在这里吧, 方便其他的朋友. 具体步骤 1. 准备好Windows Server 2012 R2 RTM的一台虚拟机, 准备SharePoint 2013 RTM的安装包, 和SP1的安装包. 2. 制作slipstream安装文…...