03-类和对象(上)
一、类的概述
1.类的引入
类的封装:将数据和方法封装在一起,加以权限区分,用户只能通过公共方法访问私有数据。
为什么要将数据和方法封装在一起呢,而且还要通过公共方法才能访问私有数据?
- C语言中数据和方法分开可能产生的问题:
// 定义一个狗结构体
struct Dog
{char name[32];
};// 定义一个人结构体
struct Person
{char name[32];
};// 定义一个狗的方法
void dogRun(struct Dog *dog)
{printf("%s正在用四条腿跑\n", dog->name);
}// 定义一个人的方法
void personRun(struct Person *per)
{printf("%s正在用两条腿跑\n", per->name);
}void test01()
{struct Dog laifu = {"来福"};struct Person changwei = {"常威"};dogRun(&laifu);personRun(&changwei);dogRun((struct Dog *)&changwei);
}
- 运行结果
来福正在用四条腿跑
常威正在用两条腿跑
常威正在用四条腿跑
- 可以看到,如果将数据和方法分开的话,由于对函数和数据的使用不规范,可能造成非常尴尬的局面。
2.类的封装
- 定义类的关键字为:class;
- 对类的数据加以权限区分:public 公有数据 、protected 受保护数据 、private 私有数据;
- public 修饰的数据和方法,类外可以直接访问;
- private、protected 修饰的数据和方法,类外不可以访问;
- 没有涉及继承时,private 和 protected 没有任何区别;
- 权限限制只是针对类外访问而言的,类的内部没有权限的区分,因此可以通过类的成员函数访问类中的任意数据;
- 一般建议,数据为私有,方法为公有。
- 代码演示
// 定义一个类
class Data
{
private:int a;
protected:int b;
public:int c;// 定义一个成员函数,用于初始化数据void initData(int a1, int b1, int c1){a = a1;b = b1;c = c1;}// 定义一个成员函数,打印所有变量的值void showData(){printf("a = %d,b = %d, c = %d\n",a , b, c);}
};void test02()
{// 实例化一个对象Data data1;data1.initData(11, 22, 33);// 访问data1中的数据// printf("a = %d\n", data1.a); // 报错,数据私有,不允许访问// printf("b = %d\n", data1.b); // 报错,数据受保护,不允许访问printf("c = %d\n", data1.c); // 访问成功,数据公有// 通过成员函数访问data1.showData();
}
- 运行结果
c = 33
a = 11,b = 22, c = 33
- 说明:
- 定义类的时候和结构体一样,是不占用空间的,实例化对象的时候才会开辟空间;
- 可以看到,我们在类的外部访问私有和受保护数据的时候,访问失败,访问公共数据,访问成功;
- 但是无论公有私有还是公共数据,通过类的成员函数进行读写操作都能成功,这也证明了在类的内部没有权限区分;
- 因此以后,想要访问类私有属性,就需要通过公共方法来访问。
3.如何设计一个类
因为类是封装了数据和方法,因此在设计类前我们要想好需要有哪些数据,然后要对这些数据进行哪些操作。
3.1案例一
- 案例1:定义一个 Person 类,包括人的姓名和年龄数据,包含初始化方法,对每个数据的读写操作,显示所有信息,同时限制年龄为合理范围。
// 定义一个 Person 类
class Person
{
private:char name[32];int age;
public:// 定义函数,初始化对象void initPerson(char *new_name, int new_age){if (new_age >= 0 && new_age <= 120){age = new_age;}else{cout << "请输入有效年龄" << endl;return;}strcpy(name, new_name);}// 定义函数,获取name的值char *getName(void){return name;}// 定义函数,获取age的值int getAge(void){return age;}// 定义函数,修改namevoid setName(char *new_name){strcpy(name, new_name);}// 定义函数,修改agevoid setAge(int new_age){if (new_age >= 0 && new_age <= 120){age = new_age;}else{cout << "请输入有效年龄" << endl;return;}}// 定义函数,打印所有信息void showPerson(){cout << "name = " << name << ", age = " << age << endl;}
};void test03()
{// 创建一个Person对象Person jack;jack.initPerson("jack", 20);cout << "name = " << jack.getName() << endl;cout << "age = " << jack.getAge() << endl;jack.setName("rose");jack.setAge(18);jack.showPerson();
}
- 运行结果
name = jack
age = 20
name = rose, age = 18
3.2案例二
- 案例2:设计立方体类(Cube),立方体的长宽高为 a b c ,求出立方体的面积和体积,分别用全局函数和成员函数判断两个立方体是否相等。
// 定义一个立方体类
class Cube
{
private:int a;int b;int c;
public:// 定义函数,初始化长宽高void initCube(int a1, int b1, int c1);// 定义函数,设置长宽高void setA(int a1);void setB(int b1);void setC(int c1);// 定义函数,获取长宽高int getA();int getB();int getC();// 定义函数,计算立方体的面积int getArea();// 定义函数,计算立方体的体积int getVolume();// 定义成员函数,判断两个立方体是否相等bool cmpCube(Cube &other_cube);
};void Cube::initCube(int a1, int b1, int c1) {a = a1;b = b1;c = c1;
}void Cube::setA(int a1) {a = a1;
}void Cube::setB(int b1) {b = b1;
}void Cube::setC(int c1) {c= c1;
}int Cube::getA() {return a;
}int Cube::getB() {return b;
}int Cube::getC() {return c;
}int Cube::getArea() {int area = (a * b + a * c + b * c) * 2;return area;
}int Cube::getVolume() {int volume = a * b *c;return volume;
}bool Cube::cmpCube(Cube &other_cube) {if ((a == other_cube.getA()) && (b == other_cube.getB()) && (c == other_cube.getC()))return true;return false;
}// 定义全局函数,判断两个立方体是否相等
bool cmpCube1(Cube &cube1, Cube &cube2)
{if ((cube1.getA() == cube2.getA()) && (cube1.getB() == cube2.getB()) && (cube1.getC() == cube2.getC()))return true;return false;
}void test04()
{// 创建一个立方体对象Cube cube1;cube1.initCube(2, 3, 5);cout << "立方体1面积:" << cube1.getArea() << ", 立方体1体积:" << cube1.getVolume() << endl;// 创建另一个立方体对象Cube cube2;cube1.initCube(1, 4, 8);cout << "立方体2面积:" << cube1.getArea() << ", 立方体2体积:" << cube1.getVolume() << endl;// 判断两个立方体是否相等// if (cmpCube1(cube1, cube2)) // 全局函数判断if (cube1.cmpCube(cube2)) // 成员函数判断{cout << "两个立方体完全一样" << endl;}else{cout << "两个立方体不一样" << endl;}
}
- 运行结果
立方体1面积:62, 立方体1体积:30
立方体2面积:88, 立方体2体积:32
两个立方体不一样
- 说明:
- 上面定义类的时候,将类的成员函数在类中声明,类外定义。注意,类外定义的函数得加上作用域的限制,标明这个成员函数是属于哪一个类的;
- 这里在比较两个立方体是否相等的时候,返回值用到了 bool 类型,表示真假值,在C语言中没有用过,c++阶段学习;
- 当使用全局函数比较两个立方体时,需要传两个参数,但使用成员函数比较两个立方体大小的时候,只需要传另外一个立方体就行。
3.3案例三
- 案例3:设计一个圆形类(Circle),和一个点类(Point),计算点和圆的关系。 假如圆心坐标为x0, y0, 半径为 r,点的坐标为 x1, y1,判断点在圆上、圆外还是园内。
// 定义一个点类
class Point
{
private:int x;int y;
public:// 初始化点坐标void initPoint(int x1, int y1){x = x1;y = y1;}// 获取点的横纵坐标int getX(){return x;}int getY(){return y;}
};// 定义一个圆类
class Circle
{
private:int r;Point p;
public:// 初始化圆void initCircle(int r0, int x0, int y0){// 初始化圆半径r = r0;// 初始化圆心p.initPoint(x0, y0);}// 定义函数,判断点和圆的位置(外 上 内 对应 1 0 -1)int getRelation(Point &p1){// 定义变量分别表示半径的平方,横坐标、纵坐标差值的平方int R = r*r;int X = (p1.getX() - p.getX()) * (p1.getX() - p.getX());int Y = (p1.getY() - p.getY()) * (p1.getY() - p.getY());// 判断位置if (R == X + Y)return 0;else if (R < X + Y)return 1;elsereturn -1;}
};void test05()
{// 创建一个点对象Point p;p.initPoint(3, 4);// 创建一个圆对象Circle c;c.initCircle(5, 0, 0);int ret = c.getRelation(p);switch(ret){case 0:cout << "点在圆上" << endl;break;case 1:cout << "点在圆外" << endl;break;case -1:cout << "点在圆内" << endl;break;default:cout << "未得出结果" << endl;break;}
}
- 运行结果
点在圆上
- 说明:上面的案例中用到了对象的嵌套,圆对象里面又嵌套了一个点对象。
二、构造析构
1.构造函数
1.1构造函数的概述
我们前面定义类的时候,都会设置一个给对象初始化的函数,创建对象以后再手动调用,其实 c++ 类里有可以主动调用的初始化函数,叫做构造函数。
构造函数:类实例化对象的时候自动调用。构造函数的本质功能就是初始化对象中的数据成员。
1.2构造函数的定义
构造函数名和类名称相同,没有返回值类型,连 void 都不可以,可以有参数,可以重载,因为创建对象是在类外创建,构造函数在创建对象的时候调用,相当于要在类外调用构造函数,因此构造函数权限为 public。
- 代码演示
// 定义一个类
class Data
{
private:int a;int b;
public:Data(){a = 0;b = 0;cout << "无参的构造函数被调用" << endl;}Data(int a1 ,int b1){a = a1;b = b1;cout << "两个参数的构造函数被调用" << endl;}
};void test06()
{// Data obj; // error: no matching function for call to 'Data::Data()'Data obj;Data obj1(1, 2);
}
- 运行结果
无参的构造函数被调用
两个参数的构造函数被调用
- 说明:
- 我们前面没有学习构造函数的时候,可以直接
Data obj
来实例化对象,而这里,我们定义了两个参数的构造函数以后,没有定义无参的构造函数,再去用Data obj
实例化对象会报错:error: no matching function for call to 'Data::Data()
,可以看到错误提示信息是 Data 类中没有 Data() 函数; - 上面问题的原因是,如果不提供任何构造函数,编译器会为类提供一个默认的无参的构造函数,因此不定义任何构造函数的时候,通过
Data obj
实例化对象,其实是调用了默认的无参构造; - 但定义了构造函数以后,
Data obj
创建对象失败是因为,用户提供任何一个构造函数都会屏蔽默认无参的构造函数; - 因此,我们在定义构造函数的时候,最好定义一个无参构造。
- 我们前面没有学习构造函数的时候,可以直接
1.3构造函数的调用
构造函数的调用形式,本质上也就是我们创建对象的形式,因为创建对象时主动触发构造函数调用。
- 代码演示
// 定义一个类
class Data
{
private:int a;int b;
public:Data(){a = 0;b = 0;cout << "无参的构造函数被调用" << "a = " << a << " b = " << b << endl;}Data(int a1){a = a1;b = 0;cout << "一个参数的构造函数被调用" << "a = " << a << " b = " << b << endl;}Data(int a1 ,int b1){a = a1;b = b1;cout << "两个参数的构造函数被调用" << "a = " << a << " b = " << b << endl;}
};void test06()
{// 隐式调用cout << "--------------------隐式调用--------------------" << endl;Data obj;Data obj1(1);Data obj2(2, 3);// 显示调用cout << "--------------------显式调用--------------------" << endl;Data obj3 = Data();Data obj4 = Data(4);Data obj5 = Data(5, 6);// 为什么有参的隐式调用都加(),无参的不加cout << "--------------------无参加()--------------------" << endl;Data obj6();cout << "--------------------隐式转换--------------------" << endl;Data obj7 = 7;cout << "--------------------匿名对象--------------------" << endl;Data();Data(8);Data(9, 10);
}
- 运行结果
--------------------隐式调用--------------------
无参的构造函数被调用a = 0 b = 0
一个参数的构造函数被调用a = 1 b = 0
两个参数的构造函数被调用a = 2 b = 3
--------------------显式调用--------------------
无参的构造函数被调用a = 0 b = 0
一个参数的构造函数被调用a = 4 b = 0
两个参数的构造函数被调用a = 5 b = 6
--------------------无参加()--------------------
--------------------隐式转换--------------------
一个参数的构造函数被调用a = 7 b = 0
--------------------匿名对象--------------------
无参的构造函数被调用a = 0 b = 0
一个参数的构造函数被调用a = 8 b = 0
两个参数的构造函数被调用a = 9 b = 10
- 说明:
- 上面的演示,构造函数的调用有两种方式:隐式调用、显示调用,推荐使用隐式调用;
- 无参的构造在隐式调用的时候是不加 () 的,因为加了会产生冲突,如:
Data obj6()
,加了 () 以后就不是创建对象了,而是声明了一个名为 obj6 的函数,且函数的返回值是一个 Data 类型的对象; - 当类中有一个参数的构造函数的时候,
Data obj7 = 7
这种写法会触发一个参数构造函数的隐式转换,不推荐这种写法; - 创建对象的时候,可以定义一个变量名来接收创建的对象,当没有定义变量接收的时候,这里创建的对象叫匿名对象,匿名对象没有变量保存,也没有被使用的话,创建完就会释放。
2.析构函数
2.1析构函数的概述
析构函数:当对象生命结束的时候,系统自动调用析构函数,完成对象的清理工作。
构造函数:先为对象开辟空间,然后调用构造函数完成初始化。
析构函数:先调用析构函数,然后释放对象自身的空间。
- 析构函数注意点:
- 如果不提供析构函数,系统会自动提供一个空的析构函数;
- 析构函数并不是清理对象自身的空间(由系统自动释放),而是清理指针成员指向的堆区空间(避免内存泄漏);
- 如果类中有指针成员且指向堆区,必须实现析构函数手动释放堆区空间。
2.2析构函数的定义
析构函数:~类名
称为析构函数名,析构函数没有返回值类型,连 void 都不可以,不能有参数,因此不能被重载。
- 代码演示:还是上面的代码,加一个析构函数,看现象
// 定义一个类
class Data
{
private:int a;int b;
public:// 定义构造函数Data(){a = 0;b = 0;cout << "无参的构造函数被调用" << "a = " << a << " b = " << b << endl;}Data(int a1){a = a1;b = 0;cout << "一个参数的构造函数被调用" << "a = " << a << " b = " << b << endl;}Data(int a1 ,int b1){a = a1;b = b1;cout << "两个参数的构造函数被调用" << "a = " << a << " b = " << b << endl;}// 定义析构函数~Data(){cout << "调用析构函数" << "a = " << a << " b = " << b << endl;}
};void test06()
{// 隐式调用cout << "--------------------隐式调用--------------------" << endl;Data obj;Data obj1(1);Data obj2(2, 3);// 显示调用cout << "--------------------显式调用--------------------" << endl;Data obj3 = Data();Data obj4 = Data(4);Data obj5 = Data(5, 6);// 为什么有参的隐式调用都加(),无参的不加cout << "--------------------无参加()--------------------" << endl;Data obj6();cout << "--------------------隐式转换--------------------" << endl;Data obj7 = 7;cout << "--------------------匿名对象--------------------" << endl;Data();Data(8);Data(9, 10);cout << "--------------------析构函数--------------------" << endl;
}
- 运行结果
--------------------隐式调用--------------------
无参的构造函数被调用a = 0 b = 0
一个参数的构造函数被调用a = 1 b = 0
两个参数的构造函数被调用a = 2 b = 3
--------------------显式调用--------------------
无参的构造函数被调用a = 0 b = 0
一个参数的构造函数被调用a = 4 b = 0
两个参数的构造函数被调用a = 5 b = 6
--------------------无参加()--------------------
--------------------隐式转换--------------------
一个参数的构造函数被调用a = 7 b = 0
--------------------匿名对象--------------------
无参的构造函数被调用a = 0 b = 0
调用析构函数a = 0 b = 0
一个参数的构造函数被调用a = 8 b = 0
调用析构函数a = 8 b = 0
两个参数的构造函数被调用a = 9 b = 10
调用析构函数a = 9 b = 10
--------------------析构函数--------------------
调用析构函数a = 7 b = 0
调用析构函数a = 5 b = 6
调用析构函数a = 4 b = 0
调用析构函数a = 0 b = 0
调用析构函数a = 2 b = 3
调用析构函数a = 1 b = 0
调用析构函数a = 0 b = 0
- 说明:
- 根据上面代码运行的结果可以看出,匿名对象,在定义以后没有变量保存其值,匿名对象也没有被使用,定义完立马就释放了;
- 而非匿名对象,这里的对象是在同一作用域定义的前提下,函数调用(当前复合语句)结束,调用析构函数,且满足先定义的后释放,后定义的先释放。
2.3析构顺序
前面的案例中,同级别的对象,先创建的后释放,当对象在不同作用域定义的时候呢?
- 代码演示
// 定义一个类
class Data
{
private:int a;
public:Data(){a = 0;cout << "无参的构造函数被调用" << "a = " << a << endl;}Data(int a1){a = a1;cout << "有参的构造函数被调用" << "a = " << a << endl;}~Data(){cout << "析构函数被调用" << "a = " << a << endl;}
};// 在全局创建对象
Data obj1(1);
Data obj2(2);
void test07()
{Data obj3(3);{Data obj4(4);Data obj5(5);}Data obj6(6);
}
- 运行结果
有参的构造函数被调用a = 1
有参的构造函数被调用a = 2
有参的构造函数被调用a = 3
有参的构造函数被调用a = 4
有参的构造函数被调用a = 5
析构函数被调用a = 5
析构函数被调用a = 4
有参的构造函数被调用a = 6
析构函数被调用a = 6
析构函数被调用a = 3
析构函数被调用a = 2
析构函数被调用a = 1
- 说明:
- 上面的演示可以看出:对象的创建是按照代码执行的顺序依次执行的;
- 但是析构函数,会先遵循作用域的变量释放顺序,和前面学习的普通变量的释放顺序一样,即当前复合语句结束,变量就释放了。最先结束的符合语句里的对象最先释放,全局的对象最后释放。满足上述对象释放顺序的前提下,还满足同一作用域,先创建的后释放。
3.拷贝构造
3.1拷贝构造函数的定义
- 代码演示
// 定义一个类
class Data
{
private:int a;int b;
public:// 定义构造函数Data(){a = 0;b = 0;cout << "无参的构造函数被调用" << endl;}Data(int a1, int b1){a = a1;b = b1;cout << "有参的构造函数被调用" << endl;}// 定义拷贝构造函数Data(const Data &obj){a = obj.a;b = obj.b;cout << "拷贝构造函数被调用" << endl;}// 显示对象信息的函数void showData(){cout << "a = " << a << " b = " << b << endl;}
};void test08()
{// 先创建一个对象Data obj1(11, 22);// 再创建一个对象,并将旧对象赋值给它Data obj2 = obj1;obj2.showData();
}
- 运行结果
有参的构造函数被调用
拷贝构造函数被调用
a = 11 b = 22
- 说明:
- 如果用户没有提供拷贝构造函数,系统会提供默认拷贝构造函数,默认拷贝构造是浅拷贝,对应成员为普变量的话,浅拷贝足够了。但如果类中有指针成员且指向堆区, 必须实现拷贝构造函数,深拷贝;
- 拷贝构造函数定义和普通构造函数一样,类名作为函数名,只是参数不一样,参数
const Data &obj
中,obj 为形参名,加 & 表示引用传递,因为实参是一个对象,对象占用空间一般很大,引用传参节约空间。同时,参数要加 const 修饰,不允许函数内部修改传入的对象的数据。
3.2拷贝构造函数的调用
拷贝构造函数和构造函数一样,不需要手动调用,而是在特定的情况下自动触发调用。
拷贝构造函数是:创建一个新对象的时候,通过旧对象给新对象初始化,会自动调用拷贝构造函数,会自动将旧对象的引用作为实参传入拷贝构造函数,将旧对象的数据拷贝给新对象。
3.2.1普通对象作为函数的参数
普通对象作为函数的参数的时候,会调用拷贝构造。
- 代码演示
// 定义一个类
class Data {
private:int a;int b;
public:// 定义构造函数Data() {a = 0;b = 0;cout << "无参的构造函数被调用" << endl;}Data(int a1, int b1) {a = a1;b = b1;cout << "有参的构造函数被调用" << endl;}// 定义拷贝构造函数Data(const Data &obj) {a = obj.a;b = obj.b;cout << "拷贝构造函数被调用" << endl;}
};void func(Data obj){}
// void func(Data &obj){}void test09() {// 创建一个对象Data obj1(11, 22);// 普通对象作为函数的参数func(obj1);
}
- 运行结果
有参的构造函数被调用
拷贝构造函数被调用
- 说明:
- 可以看到,上面的代码中,我们并没有像前面一样创建一个新对象,然后将旧对象的值赋给新对象,却发送了拷贝构造。其实这里也发生了旧对象赋值给新对象的动作,只是比较隐晦;
- 前面学习函数的时候知道,函数在定义的时候不占用空间,在调用的时候才占用空间,因为函数调用的时候会为形参开辟空间,然后定义一个形参指定类型的变量,将实参赋值给形参变量,而这里的形参就是一个新创建的 Data 类型的对象,实参是一个旧的对象,那么调用函数传参的过程就是一个旧对象给新对象赋值的过程,因此会调用拷贝构造;
- 为了防止拷贝构造被调用,一般将形参设置为引用传递,引用传递只是给旧对象起了个别名,没有旧对象给新对象赋值的过程,不会调用拷贝构造。
3.2.2普通对象作为函数的返回值
普通对象作为函数的返回值,外部通过一个新对象去接收,按照我们前面的理解,是将一个旧对象返回,赋值给新对象,理论上会发生拷贝构造,但事实并不一定如此,不同的高级编译器会有不同的结果。在 visual studio 里面会发生拷贝构造,但是在 Qt 或者 CLion 里面不会,具体可以在自己习惯使用的编译器里验证。
- 这里演示 CLion 的
// 定义一个类
class Data {
private:int a;int b;
public:// 定义构造函数Data() {a = 0;b = 0;cout << "无参的构造函数被调用" << endl;}Data(int a1, int b1) {a = a1;b = b1;cout << "有参的构造函数被调用" << endl;}// 定义拷贝构造函数Data(const Data &obj) {a = obj.a;b = obj.b;cout << "拷贝构造函数被调用" << endl;}void showData(){cout << "a = " << a << " b = " << b << endl;}// 析构函数~Data(){cout << "析构函数被调用" << endl;}
};Data func()
{Data obj1(11, 22);cout << &obj1 << endl;return obj1;
}void test09() {Data obj2 = func();obj2.showData();cout << &obj2 << endl;
}
- 运行结果
有参的构造函数被调用
0xc718fffd88
a = 11 b = 22
0xc718fffd88
析构函数被调用
- 说明
- 根据上面的运行结果,可以看到,只调用了一次构造函数,没有调用拷贝构造,而且明明创建了两个对象,但是结果却只析构了一次;
- 打印两个对象的内存地址,发现内存地址相同,结合上面的分析,可以知道,这两个变量本质上是指向同一个空间;
- 因此,可以得出结论, func 函数调用完成以后,局部变量 obj1 被删除了,但它指向的空间并没有被释放,而是由 obj2 指向了同一个空间,等到 obj2 释放的时候,空间才会释放。
3.2.3无参、有参、拷贝构造总结
- 如果用户提供了有参构造或拷贝构造会屏蔽默认的无参构造;
- 但用户提供有参构造或无参构造不会屏蔽默认拷贝构造,只有用户提供拷贝构造,才会屏蔽默认拷贝构造;
- 如果类中有指针成员且指向堆区,必须实现析构函数和拷贝构造函数(深拷贝);
- 构造函数实例化对象自动调用,先开辟空间,后调用构造函数,可以重载;
- 析构函数,对象结束的时候自动调用,先调用析构函数,后释放对象自身空间,不能重载。
相关文章:

03-类和对象(上)
一、类的概述 1.类的引入 类的封装:将数据和方法封装在一起,加以权限区分,用户只能通过公共方法访问私有数据。 为什么要将数据和方法封装在一起呢,而且还要通过公共方法才能访问私有数据? C语言中数据和方法分开可…...

PCL点云库入门——PCL库点云滤波算法之统计滤波(StatisticalOutlierRemoval)
1、算法原理 统计滤波算法是一种利用统计学原理对点云数据进行处理的方法。它主要通过计算点云中每个点的统计特性,如均值、方差等,来决定是否保留该点。算法首先会设定一个统计阈值,然后对点云中的每个点进行分析。如果一个点的统计特性与周…...

【机器学习】Kaggle实战信用卡反欺诈预测(场景解析、数据预处理、特征工程、模型训练、模型评估与优化)
构建信用卡反欺诈预测模型 建模思路 本项目需解决的问题 本项目通过利用信用卡的历史交易数据,进行机器学习,构建信用卡反欺诈预测模型,提前发现客户信用卡被盗刷的事件。 项目背景 数据集包含由欧洲持卡人于2013年9月使用信用卡进行交的…...

【RISC-V CPU debug 专栏 4 -- RV CSR寄存器介绍】
文章目录 Overview1. CSR寄存器访问指令2. 为何CSR地址不是4字节对齐(1) CSR寄存器空间是独立的地址空间(2) 节省编码空间(3) 对硬件实现的简化 3. CSR的物理大小和对齐无关4. RISC-V 中的 GPR 寄存器及其作用GPR 的详细用途CSR(控制状态寄存器)与 GPR 的…...

Object.defineProperty() 完整指南
Object.defineProperty() 完整指南 1. 基本概念 Object.defineProperty() 方法允许精确地添加或修改对象的属性。默认情况下,使用此方法添加的属性是不可修改的。 1.1 基本语法 Object.defineProperty(obj, prop, descriptor)参数说明: obj: 要定义…...

postgresql函数创建
postgresql的函数创建 1.创建函数的基本语法: CREATE [OR REPLACE] FUNCTION function_name(parameter_list) RETURNS return_type AS $$ BEGIN -- 函数体 END; $$ LANGUAGE language_name;2.创建函数时传入参数示例:add_user tbl_user表 | id | username | …...

ECMAScript 变量
文章目录 前言一、ECMAScript 变量二、var 关键字1、var 声明作用域2、var 声明提升(hoist)三、let 关键字四、const 关键字🔰 总结前言 任何语言的核心所描述的都是这门语言在最基本的层面上如何工作,涉及 语法、操作符、数据类型以及内置功能,在此基础之上才可以构建复…...

CAN总线波形中最后一位电平偏高或ACK电平偏高问题分析
参考:https://zhuanlan.zhihu.com/p/689336144 有时候看到CAN总线H和L的差值波形的最后一位电平会变高很多,这是什么原因呢? 实际上这是正常的现象,最后一位是ACK位。问题描述为:CAN总线ACK电平偏高。 下面分析下原因…...

【C++】22___STL常用算法
目录 一、常用遍历算法 二、常用查找算法 2.1 find 2.2 其它查找算法 三、常用排序算法 3.1 sort 3.2 其它排序算法 四、拷贝 & 替换 4.1 copy 4.2 其它算法 五、常用的算数生成算法 5.1 accumulate 5.2 fill 六、常用集合算法 6.1 set_intersection 6…...

意静明和-十成
十成 责任(健康)、使命(事业)、信念(意义)、自律(排诱)、自修(知识)、总结(四为)、执行(一事不拖)、人情&…...

easyui textbox使用placeholder无效
easyui textbox使用placeholder无效 在easyui 的textbox控件,请使用data-options 设定 示例 <input type text class easyui-textbox data-options "prompt:请输入您的邮箱"/>...

flux中的缓存
1. cache,onBackpressureBuffer。都是缓存。cache可以将hot流的数据缓存起来。onBackpressureBuffer也是缓存,但是当下游消费者的处理速度比上游生产者慢时,上游生产的数据会被暂时存储在缓冲区中,防止丢失。 2. Flux.range 默认…...

代码重定位详解
文章目录 一、段的概念以及重定位的引入1.1 问题的引入1.2 段的概念1.3 重定位 二、如何实现重定位2.1 程序中含有什么?2.2 谁来做重定位?2.3 怎么做重定位和清除BSS段?2.4 加载地址和链接地址的区别 三、散列文件使用与分析3.1 重定位的实质…...

活动预告 | Microsoft 365 在线技术公开课:让组织针对 Microsoft Copilot 做好准备
课程介绍 通过Microsoft Learn免费参加Microsoft 365在线技术公开课,建立您需要的技能,以创造新的机会并加速您对Microsoft云技术的理解。参加我们举办的“让组织针对 Microsoft Copilot for Microsoft 365 做好准备” 在线技术公开课活动,学…...

从0到机器视觉工程师(一):机器视觉工业相机总结
目录 相机的作用 工业相机 工业相机的优点 工业相机的种类 工业相机知名品牌 光源与打光 打光方式 亮暗场照明 亮暗场照明的应用 亮暗场照明的区别 前向光漫射照明 背光照明 背光照明的原理 背光照明的应用 同轴光照明 同轴光照明的应用 总结 相机的作用 相机…...

Docker安装(Docker Engine安装)
一、Docker Engine和Desktop区别 Docker Engine 核心组件:Docker Engine是Docker的核心运行时引擎,负责构建、运行和管理容器。它包括守护进程(dockerd)、API和命令行工具客户端(docker)。适用环境&#…...

数组的深度监听deep
场景:组件提供的emit事件可能被占用,在不能使用事件提交的情况下,就要上watch数组监听了,但是是发现只有在数组的长度发生变化的时候才会触发监听,这怎么行。。。。。 对于对象数组的深度监听,如果没有正确…...

点击锁定按钮,锁定按钮要变成解锁按钮,然后状态要从待绑定变成 已锁定(升级版)
文章目录 1、updateInviteCodeStatus2、handleLock3、InviteCodeController4、InviteCodeService5、CrudRepository 点击锁定按钮,锁定按钮要变成解锁按钮,然后状态要从待绑定变成 已锁定:https://blog.csdn.net/m0_65152767/article/details…...

UniApp 性能优化策略
一、引言 在当今数字化时代,移动应用的性能成为影响用户留存与满意度的关键因素。UniApp 作为一款热门的跨平台开发框架,以一套代码适配多端的特性极大提升了开发效率,但同时也面临着性能优化的挑战。优化 UniApp 性能,不仅能够让…...

【Linux报告】实训六 重置超级用户密码
实训六 重置超级用户密码 2018编写-今日公布 【练习一】忘记root密码 步骤一:开启或重启系统,并且要在五秒之内按任何键; 步骤二:按任意键,停止进入系统,按【e】键,跳转新页面,再…...

smolagents:一个用于构建代理的简单库
HF推出 smolagents,一个非常简单的库,它能够解锁语言模型的代理功能。以下是它的简要介绍: from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModelagent CodeAgent(tools[DuckDuckGoSearchTool()], modelHfApiModel())agent…...

通过Dockerfile来实现项目可以指定读取不同环境的yml包
通过Dockerfile来实现项目可以指定读取不同环境的yml包 1. 挂载目录2. DockerFile3. 运行脚本deploy.sh4. 运行查看日志进入容器 5. 接口测试修改application-dev.yml 6. 优化Dockerfile7. 部分参数解释8. 优化不同环境下的日志也不同调整 Dockerfile修改部署脚本 deploy.sh重新…...

云手机 —— 手机矩阵的 “超级外挂
如何打造手机矩阵 打造手机矩阵主要包括以下几个步骤: 1.确定目标与需求:首先,明确打造手机矩阵的目的和需求,是为了进行电商运营、自媒体推广、任务管理还是其他目的。这将决定后续的手机数量、操作系统选择以及应用安装等。 2.选择手机与操作系统:根据…...

OpenCV的TickMeter计时类
OpenCV的TickMeter计时类 1. TickMeter是一个计时的类1.1 计算耗时1.2 计算循环的平均耗时和FPS1.3 function 2. 案例 1. TickMeter是一个计时的类 https://docs.opencv.org/4.x/d9/d6f/classcv_1_1TickMeter.html#details 1.1 计算耗时 TickMeter tm;tm.start();// do some…...

蓝桥杯JAVA刷题--001
文章目录 题目需求2.代码3.总结 题目需求 2.代码 class Solution {public String convertDateToBinary(String date) {if (date null || date.length() ! 10 || date.charAt(4) ! - || date.charAt(7) ! -) {throw new IllegalArgumentException("输入的日期格式不正确&…...

免费又开源:企业级物联网平台的新选择 ThingsPanel
在开源领域,选择合适的开源协议是开发者和企业能否充分利用平台的关键。ThingsPanel,作为一个专注于物联网的开源平台,近日将协议从 AGPLv3 改为更开放的 Apache 2.0。这一改变对开发者和用户意味着什么? 为什么协议要从 AGPLv3 转…...

鸿蒙开发:文本合成语音
前言 Android开发的同学都知道,在Android当中,实现一段文字合成语音播放,可以使用系统提供的对象TextToSpeech来很快的实现,如果不用系统自带的,也可以使用三方提供的,比如讯飞的语音合成等等,总…...

雷军:科技传奇的逐梦之旅
亲爱的小伙伴们😘,在求知的漫漫旅途中,若你对深度学习的奥秘、Java 与 Python 的奇妙世界,亦或是读研论文的撰写攻略有所探寻🧐,那不妨给我一个小小的关注吧🥰。我会精心筹备,在未来…...

LeetCode - 初级算法 数组(删除排序数组中的重复项)
免责声明:本文来源于个人知识与公开资料,仅用于学术交流。 删除排序数组中的重复项 这篇文章讨论如何从一个非严格递增的数组 nums 中删除重复的元素,使每个元素只出现一次,并返回新数组的长度。因为数组是排序的,只要是相同的肯定是挨着的,所以我们需要遍历所有数组,然…...

2024年度培训运维总结
目前带的弟子跟我实战训练运维的总就业率基本可以稳定在 100% 左右(因为我不带小白和没学历和大龄,悟性低都筛掉了还能找我说明条件够了) 以下是弟子私教训练营的最新就业数据(2024年12月25日): 面试辅导…...