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

【c++深入系列】:类和对象详解(下)

🔥 本文专栏:c++
🌸作者主页:努力努力再努力wz

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

💪 今日博客励志语录你的人生剧本,不是父母的续集,不是子女的前传,更不是朋友的外传——你是自己故事的主角

★★★ 本文前置知识:

类和对象(中)

类和对象(上)


看完本文,你会学到:

1.知道什么是初始化列表以及如何使用初始化列表及其注意的相关细节

2.知道什么是静态成员变量以及如何使用静态成员变量及其注意的相关细节

3.知道什么是友元函数以及如何使用友元函数及其注意的相关细节

4.知道什么是匿名对象以及如何使用匿名对象及其注意的相关细节


那么你准备好在类和对象的知识海洋中遨游了吗?

初始化列表

1.什么是初始化列表

那么我们之前我们知道刚实例化创建好的对象完成自身非静态的成员变量的初始化的工作是交给构造函数来完成,而其中对于构造函数来说,其完成对于成员变量的初始化则是交给初始化列表来完成的,所以这里我们就得完善一下构造函数的构成了,那么构造函数是由两部分所构成,分别是初始化列表以及构造函数体这两部分所构成,那么首先在具体讲解初始化列表怎么使用之前,我们先来见一见初始化列表的样子,那么以日期类为例:

class date
{
private:
int _year;
int _month;
int _day;
public:
date(int year,int month,int day):_year(year),_month(month),_day(day){}
};

那么date构造函数声明下面紧跟着的就是初始化列表,那么初始化列表的定义就是第一个成员变量前面加上冒号然后括号后面就是该成员变量初始化的值,然后后面的成员变量依次用逗号来隔开

2.初始化列表怎么使用

那么之前没讲初始化列表之前,那么我们对于成员变量的所谓的初始化操作都是定义在构造函数体中,那么现在有了初始化列表的概念之后,那么我们如果自己定义的构造函数没有显示的定义初始化列表,那么同样编译器会隐式的为该构造函数定义一个初始化列表,并且编译器执行构造函数的顺序是先执行初始化列表再执行构造函数体内的内容,所以成员变量在执行构造函数体的内容之前其实已经完成了初始化了,但是只不过编译器提供的默认的构造函数的初始化列表对于内置类型它不会做任何处理,而对于自定义类型则会调用其默认的构造函数

class date
{private:int _year;int _month;int _day;public://编译器默认生成的构造函数date()_year(),_month(),_day(){}
};

所以我们之前在构造函数体内所定义的访问成员变量的各种行为,其实是对成员变量进行赋值或者说修改而不是对成员变量进行初始化,那么有的小伙伴可能就会有疑问,那么既然按照你这么说,那么初始化列表已经完成了构造函数的初始化工作,那么还要构造函数体干嘛,那么这个构造函数体岂不是显得冗余?

实则不然,那么初始化列表的工作可以概括为给对象中的各个非静态的成员变量赋一个初始值,但是初始化列表只能给成员变量设置初始值,但是它却无法检查初始值是否有效,所以此时就需要构造函数体来完成这个工作,比如检查成员变量的初始值是否有效,那么我们以栈为例:

那么假设我们通过动态数组的方式来实现栈,那么栈对应的类中的成员变量就得包括一个指针,指向在堆上开辟申请的一个动态数组的首元素的地址以及一个追踪栈顶的变量top和记录目前栈的最大容量的变量maxsize,那么我们可以在初始化列表中调用malloc函数,其会在堆上申请一片连续的空间然后返回首元素的地址,但是malloc函数可能会出现开辟失败的情况,那么此时它会返回一个空指针,所以这时就需要我们在构造函数体内定义一个检查的逻辑,避免后序通过对象来访问该空指针

class stack
{private:int* ptr;int top;int maxsize;public:stack(int default):ptr((int*)malloc(default*sizeof(int))),top(-1),maxsize(default){if(ptr==nullptr){perror("malloc fail");return;}}}

所以初始化列表是来完成对于成员变量的初始化,而构造函数体可以用来定义对于初始值的检查等一系列逻辑


其次如果我们的类如果中有自定义类型的成员变量,并且该自定义类型的成员变量对应的类中没有提供无参数或者全缺省的构造函数,那么我们就得在初始化列表中为该自定义类型的带参的构造函数提供参数

#include<iostream>
using namespace std;
class student //student类没有无参数以及全缺省的构造函数
{
private:const char* _name;int _age;
public:student(const char* name, int age):_name(name), _age(age){}int getage(){return _age;}
};
class person
{
private:int _id;student a;
public:person(int id=10001):_id(id){}
};
int main()
{person s1;return 0;
}

在这里插入图片描述

所以我们得在初始化列表中显示调用自定义类型的带参数的构造函数:

#include<iostream>
using namespace std;
class student
{
private:const char* _name;int _age;
public:student(const char* name, int age):_name(name), _age(age){}int getage(){return _age;}
};
class person
{
private:int _id;student a;
public:person(int id=10001):_id(id),a("WangZ",20){}student& getstu(){return a;}
};
int main()
{person s1;cout << s1.getstu().getage() << endl;return 0;
}

在这里插入图片描述

3.初始化列表的相关细节补充

我们知道编译器执行构造函数,那么会首先执行初始化列表的内容然后再执行构造函数体里面的内容,那么其中对于初始化列表的执行顺序则是按照你类中成员变量的声明的顺序来执行而不是按照你初始化列表的顺序来执行,那么比如按照上文的stack类,由于maxsize代表着数组的最大长度,那么有些小伙伴可能在malloc的时候,那么它直接就是用maxsize的值来作为在堆上申请开辟的数组长度,但是根据上文的stack类的成员变量声明的顺序,那么maxsize的声明是在指针ptr之后,那么意味着ptr会先进行初始化然后再执行maxsize的初始化,那么此时maxsize的值就是随机值,所以得注意初始化列表的执行顺序。所以不建议在初始化列表中用其中一个成员变量的值来初始化另一个成员变量

class stack
{private:int* ptr;int top;int maxsize;public:stack(int defaultsize):ptr((int*)malloc(maxsize*sizeof(int))),top(-1),maxsize(defaultsize){if(ptr==nullptr){perror("malloc fail");return;}}};

其次就是关于成员变量的类型是引用的时候,由于引用在声明的时候就得有定义,那么意味着类中有引用的话,那么我们就得显示在初始化列表中对引用进行定义

其中在用初始化列表来初始化引用的时候就得注意,因为初始化列表是构造函数的一部分,那么通过初始化列来初始化引用的时候,那么必然会调用构造函数,而调用函数会为该函数在栈上开辟一份空间,那么而初始化列表就是将形参的值给赋值拷贝给成员变量,那么引用在构造函数初始化的时候就会绑定到给形参上,那么一旦构造函数调用结束,那么形参随着函数栈帧一起被销毁,那么此时引用却依然指向那片被销毁的空间,那么我们通过对象来访问该引用指向的内容必然是随机值,也就会出现野引用问题

所以为了避免这种情况发生,那么我们的形参就得设置为引用,那么这样引用就能够指向到外部的空间而不是函数内的局部变量

class person 
{privatechar* _name;int _age;int& _heigeht;public:person(char* name,int age,int& height):_name(name),_age(age),__height(height){}
}

最后就是对于const属性的成员变量,那么也得在初始化列表显示的初始化,因为const修饰的变量必须在声明的时候就进行初始化

静态成员变量

为什么会有静态成员变量

那么我们可以通过类来描述现实生活中的一个个实体,比如我们要描述一个学生,那么我们可以定义一个student类,那么这些按照student类实例化出的对象可能具有共同的属性,比如他们都来自同一个学校,那么我们就没必要为每一个实例化的对象都分配一个空间给该成员变量来记录这个属性,而是将这个共同的属性给提取出来,作为类共享,可以减小对象的空间,那么这就是静态成员变量所应用的场景

静态成员变量怎么用

那么我们在类中定义的成员变量默认的属性都是非静态的,那么要在类中声明一个静态成员变量,则需要static关键字:

class student
{private:char* name;int age;int heightstatic int classnum;//声明静态成员变量
}

静态成员变量的相关使用细节

那么一旦成员变量被定义成静态,那么它便不是属于某个实例化的对象,而是整个类或者说按照这个类实例化出来的所有对象所共享的,而我们知道当对象被创建出来的那一刻,会调用构造函数来完成对象中非静态的成员变量的初始化,所以构造函数是来完成非静态成员变量的初始化的,构造函数又分为初始化列表以及构造函数体两部分,那么静态成员变量一定无法通过初始化列表来完成初始化,那是给非静态的成员变量来初始化,假设允许的话,那么每次创建一个对象,便会调用一次构造函数,那么意味着就会覆盖一次静态成员变量的内容,所以通过初始化列表来初始化初始化静态成员变量是不现实的,由于无法用初始化列表,那么自然我们也无法在类中声明静态成员变量的时候提供缺省值:

#include<iostream>
using namespace std;
class student
{
public:char* _name;int _age;static int _classnum = 10;
};
int main()
{student a;return 0;
}

在这里插入图片描述

所以我们只能在类外部定义静态成员变量:

class student
{private:char* _name;int _age;static int _classnum;
}
student::classnum=10;

而对于static修饰的变量,那么它的空间是在静态区上开辟,并且在程序开始的时候便会为static修饰的变量分配空间,所以其生命周期和程序的生命周期一样长,相比于非静态的成员变量,非静态的成员变量只有当对象被创建的时候,在对象的内部分配空间,所以也就意味着我们访问静态的成员变量可以直接指定类域去访问

#include<iostream>
using namespace std;
class student
{public:const char* _name;int _age;static int _classnum;student(const char* name,int age):_name(name),_age(age){}int getage(){return _age;}
};
int student::_classnum=10;
int main()
{cout<<student::_classnum<<endl;return 0;
}

在这里插入图片描述

其次我们也可以通过对象去访问静态成员变量,

#include<iostream>
using namespace std;
class student
{public:const char* _name;int _age;static int _classnum;student(const char* name,int age):_name(name),_age(age){}int getage(){return _age;}
};
int student::_classnum=10;
int main()
{student a("WangZ",20);cout<<a._classnum<<endl;return 0;
}

在这里插入图片描述

静态成员变量也会被访问限定符所修饰,所以一旦静态变量是私有属性,那么我们就无法通过对象以及在类外面指定类域来访问,那么此时我们有两种方式可以访问私有属性的静态成员变量,第一种方式则是通过成员函数,因为成员函数能够访问类的所有包括静态以及非静态的成员变量:

#include<iostream>
using namespace std;
class student
{private:const char* _name;int _age;static int _classnum;public:student(const char* name,int age):_name(name),_age(age){}int getage(){return _age;}int getstatic_val(){return _classnum;}
};
int student::_classnum=15;
int main()
{student a("WangZ",20);cout<<a.getstatic_val()<<endl;return 0;
}

在这里插入图片描述

第二中方式则是我们可以定义一个静态的成员函数,那么静态的成员函数和普通的成员函数不同的是,那么它是属于类共享,而不是某一个对象特有,那么意味着我们通过对象去调用该函数,那么编译器不会隐式的传递一个this指针

并且对于静态成员函数来说,那么非静态成员函数可以调用静态的成员函数,而对于静态的成员函数来说,那么它要调用非静态的成员函数,那么我们就得在静态函数体内部去定义一个对象,然后通过对象来调用非静态的成员函数:

#include<iostream>
using namespace std;
class student
{private:const char* _name;int _age;static int _classnum;public:student(const char* name,int age):_name(name),_age(age){}int getage(){return _age;}static int getstatic_val(){student a("WangZ",19);int age=a.getage();return age;}
};
int student::_classnum=15;
int main()
{cout<<student::getstatic_val()<<endl;return 0;
}

在这里插入图片描述


最后我们再来探讨静态的成员函数与j静态的普通函数的区别,其中静态的成员函数可以做到声明和定义分离,但是对于静态的普通函数来说,那么它无法做到声明和定义分离,也就是静态函数的声明保存在头文件中,静态函数的定义保存在源文件中,这是因为静态函数的属性是内部链接,而在编译阶段,编译器是以一个文件作为独立的编译单元进行编译,那么所谓内部链接,那么只能是该静态函数的声明和定义在一个文件中,而不能分离,然后它会记录进该局部的符号表中,并且被标记为内部链接,那么在链接阶段生成全局符号表会被忽略除去,所以一旦静态的普通函数只有声明而没有定义便会报错

友元函数

1.什么是友元函数

那么我们知道要访问类中的成员变量,我们目前有两种方式可以访问类中的成员变量,第一种方式就是实例化一个对象,然后通过该对象来访问成员变量,但是前提是成员变量是公有属性,其次则是通过成员函数去访问类中的所有的成员变量,那么还有第三种方式,便是通过友元函数来访问类中的所有成员变量,那么友元函数其实本质上就是一个普通的函数,但是它和普通的函数的区别就是,一般的普通函数无法访问类中私有属性的成员变量,但是其对于友元函数则开了一个后门,允许友元函数访问其类中私有属性的成员变量,但是前提是你得在类中声明该普通函数是该类的友元函数

2.友元函数怎么使用

那么我们想让外部的普通函数作为该类的友元函数,那么就得在类声明该友元函数,那么就得使用friend关键字:

#include<iostream>
using namespace std;
class student
{
private:friend void hellostudent(student& d1); //声明友元函数const char* _name;int _age;
public:student(const char* name, int age):_name(name), _age(age){}const char* getname(){return _name;}
};
void hellostudent(student& d1)
{cout << "hello " << d1.getname() << endl;
}
int main()
{student s1("WangZ", 20);hellostudent(s1);return 0;
}

在这里插入图片描述

3.友元函数的相关使用细节

那么友元函数其实就是一个普通函数,那么我们如果要通过调用友元函数来访问成员变量的话,那么调用友元函数不会调用成员函数那样,会隐式的传递一个this指针,所以我们在设置友元函数的参数的时候,一定要显示的设置一个对象的引用或者指针


其次友元函数在类中的声明不会受到访问限定符的修饰,不会存在所谓的私有属性的友元函数声明者一说,


并且我们友元函数还可以拓展到友元类上,那么被声明为友元类的类,那么可以访问该类的所有的成员变量,但是注意友元类是具有单向的,也就是人家允许你到他家来做客,但是你却没有邀请他到你家做客,所以友元类可以访问该类的所有成员变量,但是该类无法访问友元类的成员变量:

#include<iostream>
using namespace std;
class dog
{friend class student; //声明友元类
private:const char* _name;
public:dog(const char* name):_name(name){}const char* getname(){return _name;}
};
class student
{
private:const char* _name;int _age;
public:student(const char* name, int age):_name(name), _age(age){}const char* getname(){return _name;}void hellodog(dog& d1){cout << "hello " << d1.getname() << endl;}
};
int main()
{student s1("WangZ", 20);dog d1("wangcai");s1.hellodog(d1);return 0;
}

在这里插入图片描述

最后我们其他类的成员函数也可以申请为友元函数,但是声明的时候得注意级域作用限定符:

class A
{friend void B::aceess();
};
class B
{public:void accsee()
};

匿名对象

1.什么是匿名对象

那么我们实例化创建出的对象一般都是带有名字的,所以我们可以在之后的代码中通过对象名来访问该对象中的成员变量或者成员函数,而匿名对象,那么顾名思义,就是我们在创建对象的时候,没有指定对象名,那么创建出来的该对象是没有对象名的

2.匿名对象怎么使用

知道了什么是匿名对象之后,那么如何创建匿名对象呢:

class A
{.....
};
int main()
{A();
}

那么这就是创建匿名对象的一个方式,对象的类型后面直接跟一个括号,而声明有名对象的时候,那么类型和括号之间的内容便是对象名,那么匿名对象就直接把对象名这一部分给剔除了,其中我们要注意的就是如果匿名对象对应的类中没有无参或者全缺省的构造函数,那么我们在创建匿名对象的时候,那么我们就得在括号里传递参数,因为既然是创建对象,那么肯定会调用构造函数,那么如果只有带参数的构造函数,那么就得我们创建的时候显示传递参数:

#include<iostream>
using namespace std;
class student
{
private:const char* _name;int _age;
public:student(const char* name, int age):_name(name), _age(age){}const char* getname(){return _name;}void hellostudent()
{cout << "hello " << _name << endl;
}
};
int main()
{student ();return 0;
}

在这里插入图片描述

3.匿名对象的相关使用细节

那么知道了什么是匿名对象以及匿名对象如何创建,那么我们就得知道匿名对象的使用场景,那么由于匿名对象没有对象名,那么我们无法在之后的代码中访问到匿名对象的成员变量,所以匿名对象的生命周期就在创建它的那一刻,也就是说,一旦执行完创建匿名对象的代码,到下一行代码的时候,那么匿名对象就会被销毁了,所以匿名对象的第一个使用场景,便是调用成员函数,如果你不想为对象开辟空间,但是想调用成员函数,那么可以通过创建一个临时的匿名对象,然后通过匿名对象来调用成员函数:

#include<iostream>
using namespace std;
class student
{
private:const char* _name;int _age;
public:student(const char* name, int age):_name(name), _age(age){}const char* getname(){return _name;}void hellostudent()
{cout << "hello " << _name << endl;
}
};
int main()
{student ("WangZ",20).hellostudent();return 0;
}

在这里插入图片描述

其次如果某个函数的参数涉及到对象的传值拷贝,那么你也可以创建一个临时的匿名对象,然后通过这个匿名对象拷贝赋值给形参,然后拷贝结束之后,匿名对象就会被销毁了:

#include<iostream>
using namespace std;
class dog
{
public:const char* _name;dog(const char* name):_name(name){}
};
class student
{
private:const char* _name;int _age;
public:student(const char* name, int age):_name(name), _age(age){}const char* getname(){return _name;}void hellostudent(){cout << "hello " << _name << endl;}void hellodog(const dog& d1){cout << "hello " << d1._name << endl;}
};
int main()
{student s1("WangZ", 20);s1.hellodog(dog("WangWang"));return 0;
}

在这里插入图片描述

这里我们要注意的就是如果我们引用指向绑定的是一个匿名对象,那么匿名对象是具有常性的,那么我们只能通过常引用来指向该匿名对象,并且一旦匿名对象被常引用给指向,那么该匿名对象的生命周期就会被延长,那么和常引用的生命周期一样长

综合类和对象的知识点实现一个date类

最后我们要综合之前的类和对象的知识,来完成一个date类,其中要实现日期的加减天数以及日期的比较以及两个日期的相减等功能

date.h:

#pragma once
#include<iostream>
using namespace std;
class date
{
private:int _year;int _month;int _day;
public:date(int year, int month, int day);int getyear() const;int getmonth() const;int getday()const;date(const date& d1);bool operator== (const date& d1);bool operator> (const date& d1);bool operator>=(const date& d1);bool operator<(const date& d1);date operator+(int days);date& operator+=(int days);date operator-(int days);int  operator-(const date& d1);date operator++();date& operator-=(int days);int Getmonthday(int year, int month);};
ostream& operator<<(ostream& out, const date& d1);

date.cpp:

#include"date.h"
date::date(int year = 2025, int month = 5, int day = 31):_year(year), _month(month), _day(day)
{}
date::date(const date& d1)
{_year = d1._year;_month = d1._month;_day = d1._day;
}
bool date::operator==(const date& d1)
{return _year == d1._year && _month == d1._month && _day == d1._day;
}
bool date::operator>(const date& d1)
{if (_year > d1._year){return true;}else if (_year == d1._year){if (_month > d1._month){return true;}else if (_month == d1._month){if (_day > d1._day){return true;}}}return false;
}
bool date::operator>=(const date& d1)
{return (*this) > d1 || (*this) == d1;
}
bool date::operator<(const date& d1)
{return !((*this) >= d1);
}
int date::Getmonthday(int year, int month)
{int arr[12] = { 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;}else{return arr[month - 1];}}
date& date::operator+=(int days)
{_day += days;int curdays = Getmonthday(_year, _month);while (_day > curdays){_day -= curdays;_month++;if (_month == 13){_month = 1;_year++;}curdays = Getmonthday(_year, _month);}return *this;
}
date date::operator+(int days)
{date temp = *this;temp += days;return temp;
}
date date::operator-(int days)
{date temp = *this;temp -= days;return temp;}
date& date::operator-=(int days)
{if (days < 0){return *this += (-days);}while (days > 0){if (_day > days){_day -= days;break;}else{days -= _day;_month--;if (_month == 0){_month = 12;_year--;}_day = Getmonthday(_year, _month);}}return *this;
}
date date:: operator++()
{(*this) += 1;return *this;
}
int date:: operator-(const date& d1)
{int num = 0;date max;date min;if (*this < d1){max = d1;min = *this;}else{max = *this;min = d1;}while (!(min == max)){++min;num++;}return num;
}
int date::getyear() const
{return _year;
}
int date::getmonth() const
{return _month;
}
int date::getday() const
{return _day;
}
ostream& operator<<(ostream& out, const date& d1)
{out << d1.getyear() << " " << d1.getmonth() << " " << d1.getday();return out;
}

main.cpp:

#include"date.h"
int main()
{date d0(2025, 5, 10);date d1(2025, 5, 31);if (d0 < d1){cout << "d1 is max" << endl;}else{cout << "d0 is max" << endl;}cout << (d0 + 10) << endl;cout << (d1 - 20) << endl;return 0;}

在这里插入图片描述

结语

那么这就是本期关于类和对象的全部介绍了,那么学习完了类和对象之后,那么恭喜耐心看到这里的读者,那么你顺利逾越了c++的一座大山,那么之后我会讲解内存管理以及模版,那么这两个内容学完之后,你会见识到c++的好玩之处,那么我会持续更新,希望你多多关注,如果本文有帮组到你,还请三连加关注哦,你的支持就是我创作的最大的动力!
在这里插入图片描述

相关文章:

【c++深入系列】:类和对象详解(下)

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 你的人生剧本&#xff0c;不是父母的续集&#xff0c;不是子女的前传&#xff0c;更不是朋友的外传——你是自己故事的主角 ★★★ 本文前…...

浅谈「分词」:原理 + 方案对比 + 最佳实践

在文本搜索、自然语言处理、智能推荐等场景中&#xff0c;「分词」 是一个基础但至关重要的技术点。无论是用数据库做模糊查询&#xff0c;还是构建搜索引擎&#xff0c;分词都是提高效率和准确度的核心手段。 &#x1f50d; 一、什么是分词&#xff1f; 分词&#xff08;Tok…...

第十八:GC 垃圾回收

2.1 三色标记# 灰色&#xff1a;对象已被标记&#xff0c;但这个对象包含的子对象未标记黑色&#xff1a;对象已被标记&#xff0c;且这个对象包含的子对象也已标记&#xff0c;gcmarkBits对应的位为1&#xff08;该对象不会在本次GC中被清理&#xff09;白色&#xff1a;对象…...

【微机及接口技术】- 第七章 可编程定时/计数器

文章目录 第一节 定时/计数器的概述一、定时与计数二、定时方法 第二节 可编程定时/计数器8254一、8254-2的基本功能二、8254的内部结构和外部引脚三、8254 的工作方式1. 方式0&#xff1a;计数到零产生中断方式2. 方式1&#xff1a;硬件可重触发单稳方式3. 方式2&#xff1a;速…...

MES生产工单管理系统,Java+Vue,含源码与文档,实现生产工单全流程管理,提升制造执行效率与精准度

前言&#xff1a; MES生产工单管理系统是制造业数字化转型的核心工具&#xff0c;通过集成生产、数据、库存等模块&#xff0c;实现全流程数字化管理。以下是对各核心功能的详细解析&#xff1a; 一、生产管理 工单全生命周期管理 创建与派发&#xff1a;根据销售订单或生产计…...

【区块链安全 | 第三十五篇】溢出漏洞

文章目录 溢出上溢示例溢出漏洞溢出示例漏洞代码代码审计1. deposit 函数2. increaseLockTime 函数 攻击代码攻击过程总结修复建议审计思路 溢出 算术溢出&#xff08;Arithmetic Overflow&#xff09;&#xff0c;简称溢出&#xff08;Overflow&#xff09;&#xff0c;通常分…...

【自记录】ubuntu命令行下禁用指定声卡

设备上内置了一块声卡&#xff0c;出于某些原因我希望禁用他。 通过arecord -l可以查看到该设备 $ arecord -l **** List of CAPTURE Hardware Devices **** card 0: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]Subdevices: 1/1Subdevice #0: subdevice…...

设计模式 Day 4:观察者模式(Observer Pattern)深度解析

在经历了前三天的对象创建型设计模式学习之后&#xff0c;今天我们开始进入行为型设计模式的探索之旅。行为型模式聚焦于对象之间的通信机制与协作方式&#xff0c;其中最经典且应用最广泛的就是——观察者模式&#xff08;Observer Pattern&#xff09;。本文将用8000字篇幅&a…...

`QTabWidget` 的标签页头设置样式,可以通过在 QSS 文件中定义 `QTabBar::tab` 的样式

要为 QTabWidget 的标签页头设置样式&#xff0c;可以通过在 QSS 文件中定义 QTabBar::tab 的样式来实现。以下是完整的代码示例和 QSS 文件内容&#xff0c;展示如何为标签页头设置背景颜色、文本颜色、悬停效果和选中效果。 ### **代码示例** cpp #include <QApplication…...

低代码开发革命:用 ZKmall开源商城可视化逻辑编排实现业务流程再造

ZKmall开源商城通过可视化逻辑编排引擎与低代码开发范式&#xff0c;重新定义了企业级电商业务流程的构建与优化方式。本文将从技术架构、核心能力、实践案例及行业价值等维度&#xff0c;解析其如何以"低代码流程引擎"组合拳实现业务流程再造的革命性突破。 一、低代…...

CAN外设

目录 1. CAN外设结构 1.1 CAN外设发送流程 1.2 CAN外设接收流程 1.3 发送接受配置位 2. CAN外设过滤器 2.1 过滤器配置 2.2 测试模式 2.3 工作模式 2.4 过滤器对应中断 2.5 错误处理和离线恢复 1. CAN外设结构 以STM32F103为例。以下是它的内部结构框图。 其具体发…...

(七)安卓开发中的状态列表图形(StateListDrawable)详解

在安卓开发中&#xff0c;**状态列表图形&#xff08;StateListDrawable&#xff09;**是一种非常实用的资源&#xff0c;它允许开发者根据视图的不同状态&#xff08;如按下、聚焦、选中等&#xff09;来动态显示不同的图像或颜色。这种机制在创建交互式用户界面时尤为重要&am…...

2023年蓝桥杯第十四届CC++大学B组真题及代码

目录 1A&#xff1a;日期统计 解析代码_暴力_正解 2B&#xff1a;01串的熵 解析代码_暴力_正解 3C&#xff1a;冶炼金属 解析代码_暴力_正解 4D&#xff1a;飞机降落 解析代码_暴力dfs_正解 5E&#xff1a;接龙数列 解析代码_dp_正解 6F&#xff1a;岛屿个数 解析代…...

odo18实施——销售-仓库-采购-制造-制造外包-整个流程自动化单据功能的演示教程

安装模块 安装销售 、库存、采购、制造模块 2.开启外包功能 在进入制造应用点击 配置—>设置 勾选外包&#xff0c;点击保存 添加信息 一、添加客户信息 点击到销售应用 点击订单—>客户 点击新建 创建客户1&#xff0c;及其他客户相关信息&#xff0c;点…...

c++造轮子之REACTOR实战

本文实现的为单reactor 多线程(base) 非核心库 InetAddress 这个库简单而言 无疑是设置ip地址和端口 class InetAddress { public:struct sockaddr_in addr;socklen_t addr_len;InetAddress();InetAddress(const char* ip, uint16_t port);~InetAddress(); };具体而言: Ine…...

【Easylive】Elasticsearch搜索组件详解

【Easylive】项目常见问题解答&#xff08;自用&持续更新中…&#xff09; 汇总版 一、Elasticsearch基础介绍 Elasticsearch(简称ES)是一个分布式、RESTful风格的搜索和分析引擎&#xff0c;基于Apache Lucene构建。在视频平台中&#xff0c;它主要用于&#xff1a; 全…...

基于AT89C51单片机的加减乘除液晶计算机设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/90574816?spm1001.2014.3001.5503 功能介绍&#xff1a; 可进行最高四位数的加减乘除运算&#xff0c;除法运算保留小数点后四位&#xff1b;4*4矩阵按键输入&…...

先进制造aps专题三十三 开源aps产品,frepple和dream对比分析

开源的两个aps产品&#xff0c;frepple和dream对比分析 frepple开源的基本不能用&#xff0c;第一它甘特图没开源&#xff0c;而且甘特图不允许你手工个修改&#xff0c;你想把它当成手工甘特图用也不行&#xff0c;第二&#xff0c;算法强制倒排&#xff0c;很少企业是倒排 …...

Vue3.2 项目打包成 Electron 桌面应用

本文将详细介绍如何将基于 Vue3.2 的项目打包成 Electron 桌面应用。通过结合 Electron 和 Vue CLI 工具链&#xff0c;可以轻松实现跨平台桌面应用的开发与发布。 1. 项目结构说明 项目主要分为以下几个部分&#xff1a; electron/main.js&#xff1a;Electron 主进程文件。…...

第16届蓝桥杯单片机模拟试题Ⅰ

试题 代码 sys.h #ifndef __SYS_H__ #define __SYS_H__#include <STC15F2K60S2.H> //onewire.c float getT(); //sys.c extern unsigned char UI; extern bit touch_mode; extern float jiaozhun; extern float canshu; extern float temper; void init74hc138(unsigned…...

ES:geoip_databases

如何查看 .geoip_databases 的内容 在Elasticsearch中&#xff0c;.geoip_databases 是一个特殊的索引&#xff0c;用于存储GeoIP数据库文件。这些文件通常用于地理信息的丰富&#xff08;GeoIP enrichment&#xff09;。以下是如何查看和管理这些数据库文件的方法&#xff1a…...

企业级开发SpringBoost玩转Elasticsearch

案例 Spring Boot 提供了 spring-data-elasticsearch 模块&#xff0c;可以方便地集成 Elasticsearch。 下面我们将详细讲解如何在 Spring Boot 中使用 Elasticsearch 8&#xff0c;并提供示例代码。 1. 添加依赖: 首先&#xff0c;需要在 pom.xml 文件中添加 spring-data-e…...

边缘计算网关作用

一、数据采集与预处理 边缘计算网关作为物联网系统的“数据入口”&#xff0c;能够连接各种传感器和设备&#xff0c;实时采集数据。在数据传输到云端之前&#xff0c;它会对数据进行清洗、过滤和聚合&#xff0c;剔除重复、无效或冗余的信息&#xff0c;只将有价值的数据上传…...

利用本地 Express Web 服务解决复杂的 Electron 通信链路的问题

背景 Web 服务对前端同学来说并不陌生&#xff0c;你们开发其他前端界面请求的后端接口就是 Web 服务&#xff0c;你们 npm run dev启动的也是一个本地的 Web 服务&#xff0c;前端的 js&#xff0c;html&#xff0c;css 都有从这个服务上拉取到的资源。 我们在开发 Electron…...

《自然-计算科学》诚邀您投稿计算社会科学研究(computational social science)

李升伟 编译 近年来&#xff0c;运用计算方法和工具来深化对社会科学长期议题理解的"计算社会科学"发展迅猛。这一增长主要得益于社交媒体数据、移动通信数据、数字化图书与历史档案、医疗记录等海量数据的涌现&#xff0c;这些资源不仅为验证现有社会科学理论提供了…...

【SPSS/EXCEl】主成分分析构建__综合评价指数

学习过程中实验操作的记录 1.数据准备和标准化&#xff1a; (1)区分正负相关性:判断每个因子是正向指标还是负向指标,计算每个的最大值和最小值 (2) 标准化: Min-Max标准化 Min-Max标准化&#xff08;最大最小值法&#xff09;&#xff1a; 将数据映射到指定的区间&#xff…...

#node.js后端项目的部署相关了解

熟悉 Spring Boot 的 java -jar 启动方式&#xff0c;那咱们就用类比 实战方式&#xff0c;来彻底搞懂&#xff1a; &#x1f680; Node.js 后端项目的 部署 & 启动方式 ✅ 和 Spring Boot 的 java -jar xxx.jar 一样&#xff0c;Node.js 也可以一句命令启动&#xff0c;而…...

程序化广告行业(69/89):DMP与PCP系统核心功能剖析

程序化广告行业&#xff08;69/89&#xff09;&#xff1a;DMP与PCP系统核心功能剖析 在数字化营销浪潮中&#xff0c;程序化广告已成为企业精准触达目标受众的关键手段。作为行业探索者&#xff0c;我深知其中知识的繁杂与重要性。一直以来&#xff0c;都希望能和大家一同学习…...

基于Python的二手房数据挖掘与可视化深度分析

一、技术框架与数据概况 1.1 技术栈构成 import pandas as pd # 数据操作(v1.3.5) import numpy as np # 数值计算(v1.21.6) from pyecharts.charts import * # 交互式可视化(v1.9.1) from sklearn.preprocessing import StandardScaler # 数据标准化(可选扩展) …...

linux第三次作业

1、将你的虚拟机的网卡模式设置为nat模式&#xff0c;给虚拟机网卡配置三个主机位分别为100、200、168的ip地址 2、测试你的虚拟机是否能够ping通网关和dns&#xff0c;如果不能请修改网关和dns的地址 3、将如下内容写入/etc/hosts文件中&#xff08;如果有多个ip地址则写多行&…...

C#编写HttpClient爬虫程序示例

要写一个使用C#和HttpClient的爬虫程序。首先&#xff0c;我需要了解HttpClient的基本用法。HttpClient是用来发送HTTP请求和接收响应的类&#xff0c;对吧&#xff1f;我记得在C#中使用它的时候需要注意一些事情&#xff0c;比如最好使用单例实例&#xff0c;而不是频繁创建和…...

关于Spring MVC在无注解情况下通过参数名匹配获取请求参数的详细说明,包含代码示例和总结表格

以下是关于Spring MVC在无注解情况下通过参数名匹配获取请求参数的详细说明&#xff0c;包含代码示例和总结表格&#xff1a; 1. 核心机制 Spring MVC通过参数名匹配实现无注解参数绑定&#xff1a; 条件&#xff1a;方法参数名需与请求参数&#xff08;查询参数、表单参数&a…...

数智读书笔记系列027:《医疗健康大数据治理》构建智慧医疗的核心基石

一、图书介绍: 1.1 书籍基本信息 在当今数字化技术飞速发展的背景下,医疗行业正经历着前所未有的变革。信息化、智能化、数据驱动的趋势正在深入到医疗服务的各个环节,推动着医疗健康大数据成为医疗行业发展的核心资产。在这样的时代背景下,《医疗健康大数据治理》这本书应…...

Wayland介绍

Wayland 是一种现代化的显示服务器协议&#xff0c;旨在替代传统的 X Window System&#xff08;X11&#xff09;&#xff0c;为 Linux 和类 Unix 系统提供更高效、安全的图形显示管理。以下是其核心要点&#xff1a; 1. 基本概念 显示服务器协议&#xff1a;Wayland 定义了客户…...

dockerTeskTop安装dify及使用deepseek

配置 在这之前&#xff0c;要把模型运行一起&#xff0c;我这里是 PS C:\Users\Administrator> ollama run deepseek-r1:8b 模型名称一定要写对 如果添加失败&#xff0c;参考 dify 1.0.1无法在ollama下新增LLM模型 - 何辉煌 - 博客园...

解释 Git 的基本概念和使用方式

Git 是一个分布式版本控制系统&#xff0c;用于跟踪文件的变化并协作开发项目。下面是 Git 的一些基本概念和使用方式&#xff1a; 仓库&#xff08;Repository&#xff09;&#xff1a;Git 仓库是用来存储项目文件的地方&#xff0c;可以在本地计算机上创建一个本地仓库&#…...

【区块链安全 | 第三十三篇】备忘单

文章目录 备忘单操作符优先级备忘单ABI 编码和解码函数bytes 和 string 的成员Address 的成员区块与交易属性校验和断言数学和加密函数合约相关类型信息函数可见性说明符修饰符备忘单 操作符优先级备忘单 以下是操作符的优先级顺序,按评估顺序列出: 优先级描述操作符1后缀递…...

MyBatis的缓存、逆向工程、使用PageHelper、使用PageHelper

一、MyBatis的缓存 缓存&#xff1a;cache 缓存的作用&#xff1a;通过减少IO的方式&#xff0c;来提高程序的执行效率。 mybatis的缓存&#xff1a;将select语句的查询结果放到缓存&#xff08;内存&#xff09;当中&#xff0c;下一次还是这条select语句的话&#xff0c;直…...

GS+:地统计分析与空间插值工具

大家好&#xff0c;今天为大家介绍的软件是GS&#xff1a;一款用于地统计分析与空间数据处理的软件。与ArcGIS相比的话&#xff0c;它更适合专注于地质统计学分析的用户&#xff0c;尤其是需要对半方差函数进行深入分析和调整的场景下面。我们将从软件的主要功能、支持的系统、…...

C++类型转换详解

目录 一、内置 转 内置 二、内置 转 自定义 三、自定义 转 内置 四、自定义 转 自定义 五、类型转换规范化 1.static_case 2.reinterpret_cast 3.const_cast 4.dynamic_cast 六、RTTI 一、内置 转 内置 C兼容C语言&#xff0c;在内置类型之间转换规则和C语言一样的&am…...

scala-集合2

可变数组 定义变长数组 val arr01 ArrayBuffer[Any](3, 2, 5) &#xff08;1&#xff09;[Any]存放任意数据类型 &#xff08;2&#xff09;(3, 2, 5)初始化好的三个元素 &#xff08;3&#xff09;ArrayBuffer 需要引入 scala.collection.mutable.ArrayBuffer 案例实操 Arra…...

Clang编译器优化选项

Clang 作为 C/C 编译器&#xff0c;提供了丰富的优化选项&#xff0c;以下是主要的优化相关选项分类和说明&#xff1a; 1. 优化级别&#xff08;通用选项&#xff09; 选项说明-O0默认级别&#xff0c;禁用所有优化&#xff0c;用于调试。-O1基础优化&#xff08;代码大小和执…...

Java文件流操作 - 【Guava】IO工具

引言 Guava 使用术语 流来表示可关闭的&#xff0c;并且在底层资源中有位置状态的 I/O 数据流。字节流对应的工具类为 ByteSterams&#xff0c;字符流对应的工具类为 CharStreams。 Guava 中为了避免和流直接打交道&#xff0c;抽象出可读的 源 source 和可写的 汇 sink 两个概…...

C语言中单链表操作:查找节点与删除节点

一. 简介 前面学习了C语言中创建链表节点&#xff0c;向链表中插入节点等操作&#xff0c;文章如下&#xff1a; C语言中单向链表&#xff1a;创建节点与插入新节点-CSDN博客 本文继续学习c语言中对链表的其他操作&#xff0c;例如在链表中查找某个节点&#xff0c;删除链表…...

mapbox基础,加载栅格图片到地图

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️raster 栅格图层 api二、🍀使用本地载…...

Linux红帽:RHCSA认证知识讲解(十 二)调试 SELinux,如何管理 SELinux 的运行模式、安全策略、端口和上下文策略

Linux红帽&#xff1a;RHCSA认证知识讲解&#xff08;十 二&#xff09;调试 SELinux&#xff0c;如何管理 SELinux 的运行模式、安全策略、端口和上下文策略 前言一、SELinux 简介二、SELinux 的运行模式2.1 查看和切换 SELinux 模式 三、SELinux 预设安全策略的开关控制四、管…...

模糊斜率熵Fuzzy Slope entropy+状态分类识别!2024年11月新作登上IEEE Trans顶刊

引言 2024年11月&#xff0c;研究者在测量领域国际顶级期刊《IEEE Transactions on Instrumentation and Measurement》&#xff08;IF 5.6&#xff0c;JCR 1区&#xff0c;中科院二区&#xff09;上发表科学研究成果&#xff0c;以“Optimized Fuzzy Slope Entropy: A Comple…...

【MATLAB】将数据保存在mat文件中 save/load/matfile

MAT文件为MATLAB格式的二进制文件 save()函数 save - 将工作区变量保存到文件中 save(filename) 将当前工作区中的所有变量保存在 MATLAB 格式的二进制文件&#xff08;MAT 文件&#xff09;filename 中。如果 filename 已存在&#xff0c;save 会覆盖该文件。 save(filena…...

009_抽象类和接口

抽象类和接口 final关键字常量 单例模式&#xff08;设计模式&#xff09;枚举类抽象类抽象类的注意事项、特点使用抽象类的好处模版方法设计模式 接口接口的好处接口的注意事项 final关键字 final关键字是最终的意思&#xff0c;可以修饰类、方法、变量。 修饰类&#xff1a;…...

【数据结构】排序算法(下篇·开端)·深剖数据难点

前引&#xff1a;前面我们通过层层学习&#xff0c;也就了解了Hoare大佬的排序思想&#xff0c;今天我们学习的东西可能稍微有点难度&#xff0c;因此我们必须学会思想&#xff0c;我很受感慨&#xff0c;因此借此分享一下&#xff1a;【用1520分钟去调试】&#xff0c;如果我们…...