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

C++之面向对象

目录

对象与类

类的语法:

C++中class与struct的区别:

通过类实例化对象的方式

具体案例

类作用域与分文件编写

创建circle.h头文件

创建源文件circle.cpp

创建all.cpp来作为程序的入口

封装

封装的意义

访问权限符

成员属性私有化

优点

具体案例

对象的初始化和清理

构造函数

析构函数

具体案例

构造函数的分类及调用

构造函数的分类

具体案例

构造函数的调用

括号法

显示法

隐式转换法

匿名对象

拷贝构造函数的调用时机

构造函数的调用规则

默认情况下C++编译器至少给一个类添加3个函数

构造函数的调用规则

初始化列表

深拷贝与浅拷贝

类对象作为类成员

静态成员

前言:

静态成员的访问方式

静态成员变量

静态成员函数

成员变量和成员函数分开存储

this指针

定义

this指针的用途

空指针访问成员函数

const修饰成员

常函数:

常对象

友元

前言:

友元的三种实现

全局函数做友元

类做友元

成员函数做友元

运算符重载

重载运算符方式

加号运算符重载 

通过成员函数进行重载 

通过全局函数进行重载

左移运算符重载

通过全局函数重载左移运算符

递增运算符重载

赋值运算符重载

C++编译器至少会给一个类添加4个函数

关系运算符重载

重载==号

函数调用运算符重载

前言:

具体案例 

继承

类与类之间的继承关系

继承语法:

继承的经典案例

继承的方式种类

理解结构图

继承中的对象属性

继承中构造和析构的顺序

继承同名成员处理方式

继承中同名静态成员的处理方法

C++中的多继承

语法:

菱形继承

经典案例

菱形继承带来的问题

虚继承

前言:

多态

多态的分类

静态多态和动态多态的区别

动态多态的满足条件

案例分析

动态多态的原理剖析

纯虚函数和抽象类

抽象类特点

虚析构和纯虚析构

虚析构和纯虚析构共性与区别

语法:

经典案例

总结:

对象与类

  • C++中认为万事万物皆对象,对象上有其属性和行为
  • 属性可以理解为对象的成员变量
  • 行为可以理解为对象所拥有的方法
  • 类中的属性和行为统一称为成员
  • 我们把具有相同性质的对象抽象为一个类

类的语法:

class 类名{访问权限1:属性1;行为1;访问权限2:属性2;行为2;
};

C++中class与struct的区别:

  • struct默认权限为public
  • class默认权限为private

注意:

  • C++中的类用关键字class修饰
  • 在C++中结构体内也可以使用权限修饰符,也可以有成员变量以及成员方法

通过类实例化对象的方式

语法:类名 对象名;

注意:实例化对象之后就可以通过对象名.成员名的方式来访问类中的属性及方法 

具体案例

#include <iostream>
using namespace std;
const double PI = 3.14;
//创建圆类
class Circle {
//访问权限
public://属性int r;//行为double calculateZC() {return 2 * PI * r;}
};
void main() {//通过圆类来实例化圆的对象Circle c;c.r = 10;cout << "圆的周长为:" << c.calculateZC() << endl;system("pause");
}

类作用域与分文件编写

创建circle.h头文件

//防止头文件重复包含
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
const double PI = 3.14;
//创建圆类
class Circle {
public://设置半径void setR(double newr);//访问半径double getR();//求圆的周长double calculateZC();
private://属性int r;
};

注意:头文件中只写函数声明,不写函数实现。 

创建源文件circle.cpp

#include "circle.h"
//设置半径(circle作用域下的成员函数)
void Circle::setR(double newr) {r = newr;
}
//访问半径
double Circle::getR() {return r;
}
//求圆的周长
double Circle::calculateZC() {return 2 * PI * r;
}

注意:源文件中只写函数的实现,在使用时需要引入对应的头文件,并用成员作用域的方式(类名::函数名)明确要实现的是哪个类的哪个方法

创建all.cpp来作为程序的入口

#include "circle.h"
void main() {//通过圆类来实例化圆的对象Circle c;//设置圆的半径c.setR(10);cout << "圆的半径为:" << c.getR() << endl;cout << "圆的周长为:" << c.calculateZC() << endl;system("pause");
}

注意:使用时需要导入circle.h头文件

C++面向对象的三大特性:封装、继承、多态。

封装

封装的意义

  • 将属性和行为作为一个整体来表现生活中的事物
  • 将属性和行为通过权限进行控制

访问权限符

  • public:公共权限(成员类内可以访问该属性,类外同样可以访问该属性)
  • protected:保护权限(该属性在类内可以访问,类外不可以访问)
  • private:私有权限(该属性在类内可以访问,类外不可以访问)

protected与private的区别:后面我们会讲到继承,在继承中若父类的属性访问权限修饰符为protected,那么子类就可以访问该父类属性; 若父类的属性访问权限修饰符为private,那么子类就不可以访问该父类属性; 

成员属性私有化

优点

  • 将成员属性设置成私有化,可以自己控制读写权限
  • 对于写的权限,我们可以检测数据的有效性(在方法内加条件)

具体案例

#include <iostream>
using namespace std;
//创建person类
class Person
{
public://设置姓名void setName(string name) {m_Name = name;}//读取姓名string getName() {return m_Name;}//读取年龄int getAge() {return m_Age;}//更改爱人void setLover(string lover) {m_Lover = lover;}
private://姓名——可读可写string m_Name="lili";//年龄——只读int m_Age=18;//爱人——只写string m_Lover="lan";
};
void main() {Person people;string my=people.getName();cout << "我的名字为:" << my << endl;people.setName("Dong");my=people.getName();cout << "我的名字为:" << my << endl;system("pause");
}

注意:被private修饰的属性已经无权限访问,只能通过public修饰的方法对该属性进行间接访问

对象的初始化和清理

  • 一个对象或者变量没有初始的状态,那么对其使用的后果是未知的
  • 使用完一个对象或者变量,没有及时清理,那么也会造成一定的安全问题

前言:C++利用了构造函数和析构函数来解决以上问题,这两个函数将被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要求我们做的事情,因此,若我们不提供构造和析构那么编译器会提供。但是编译器提供的构造和析构函数都是空实现

构造函数

语法:类名(){} 

构造函数作用:主要作用是创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用

注意:

  • 构造函数没有返回值,也不写void
  • 函数名称与类名相同
  • 构造函数可以有参数,因此可以发生重载
  • 程序在调用对象时自动调用构造,无需手动调用,而且只会调用一次

析构函数

语法:~类名(){} 

注意:

  • 析构函数没有返回值,也不写void
  • 函数名称与类名相同,并且在名称前加~
  • 析构函数不可以有参数,不可以发生重载
  • 程序在自动销毁前会自动调用析构函数,无需手动调用,而且只会调用一次 
  • 若属性创建在堆区,那么析构函数在执行delete的语句后方可调用

析构函数作用:主要作用在于对象销毁前系统自动调用,执行一些清理工作

具体案例

#include <iostream>
using namespace std;
class Person {
public:Person() {cout << "Person构造函数的调用" << endl;}~Person() {cout << "Person析构函数的调用" << endl;}
};
void main() {Person p;system("pause");
}

构造函数的分类及调用

构造函数的分类

  • 按照参数:有参构造和无参构造
  • 按照类型:普通构造和拷贝构造

具体案例

class Person {
public:Person() {cout << "Person无参构造函数的调用" << endl;}Person(string name) {p_Name = name;cout << "Person的含参构造函数的调用" << endl;}Person(string name,int age,string sex) {p_Name = name;p_Age = age;p_Sex = sex;cout << "Person的全参构造函数的调用" << endl;}Person(const Person& p) {p_Name = p.p_Name;p_Age = p.p_Age;p_Sex = p.p_Sex;cout << "Person的拷贝构造函数的调用" << endl;}
private:string p_Name;int p_Age;string p_Sex;
};

注意:若不写构造函数那么编译器会自动调用自己的无参构造,若自己写了构造函数,那么编译器提供的构造函数就不可用。

构造函数的调用

  • 括号法
  • 显示法
  • 隐式转换法

括号法

//括号法
void kuoHaoFa() {Person p;			//默认构造函数的调用Person p1("lili");	//含参构造函数的调用Person p2(p1);		//拷贝构造函数的调用
}

注意:调用默认构造函数的时候不要加()——因为Person p();编译器会认为它是一个函数声明,不会认为是在创建对象

显示法

//显示法
void xianShiFa() {//默认构造函数的调用Person p;//含参构造函数的调用Person p1 = Person("lili");
}

注意:

  • 该参数列表是为了调用含参构造所用的
  • 不要利用拷贝函数来初始化一个匿名对象——Person(p)==Person p

隐式转换法

//隐式转换法
void YinShiFa() {//默认构造函数的调用Person p;//含参构造函数的调用string name = "lili";//等同:Person p1=Person("lili");Person p1 = name;
}

注意:隐式转换只有在构造函数有单个形参的情况下才可以进行 

匿名对象

匿名对象:类名(参数列表);

匿名对象特点:当执行结束后,系统会立即回收掉匿名对象

拷贝构造函数的调用时机

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 以值传递的方式给函数传参(实参传递给形参时会调用拷贝构造函数,拷贝一个新的对象传给形参)
  • 以值的方式返回局部对象(会将返回的对象(局部对象)拷贝一份传给接受的变量)
#include <iostream>
using namespace std;
class Person {
public:Person() {cout << "Person无参构造函数的调用" << endl;}Person(const Person& p) {p_Name = p.p_Name;p_Age = p.p_Age;p_Sex = p.p_Sex;cout << "Person的拷贝构造函数的调用" << endl;}
private:string p_Name;int p_Age;string p_Sex;
};
//以值传递的方式给函数传参,会调用到拷贝构造函数
void doWork(Person p) {cout << "dowork函数" << endl;
}
//以值的方式返回局部对象,会调用到拷贝构造函数
Person doWork1() {cout << "dowork1函数" << endl;Person p1;return p1;
}
void main() {Person p;doWork(p);Person p2=doWork1();system("pause");
}

构造函数的调用规则

默认情况下C++编译器至少给一个类添加3个函数

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认的拷贝构造函数,对属性进行拷贝

构造函数的调用规则

  • 若用户定义了有参构造函数,那么C++不再提供默认的无参构造,但是会提供默认的拷贝构造
  • 若用户定义了拷贝构造函数,那么C++将不再提供其他的构造函数

初始化列表

作用:C++提供了初始化列表语法:用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)……{}

#include <iostream>
using namespace std;
class Person {
public://初始化列表来初始化属性Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c) {cout << "m_A=" <<a<< endl;cout << "m_B=" <<b<< endl;cout << "m_C=" <<c<< endl;cout << "初始化列表函数" << endl;}int m_A;int m_B;int m_C;
};
void main() {//测试初始化列表Person p(10,20,30);system("pause");
}

深拷贝与浅拷贝

浅拷贝:浅拷贝就是对象的数据之间的简单赋值;原始的数据就占用一份空间,而两个指针会共同指向原始数据所占的地址

深拷贝:深拷贝相对于浅拷贝他会在堆内存中另外申请空间来储存数据,两个指针分别会指向两块不同的内存空间

#include <iostream>
using namespace std;
class Person {
public:Person(int age) {m_Age = new int(age);}//浅拷贝带来问题,堆区内存重复释放~Person() {if (m_Age != NULL) {delete m_Age;m_Age = NULL;}}//重载赋值运算符Person& operator=(Person& p) {//先判断是否有属性在堆区,若有先释放干净再深拷贝if (m_Age != NULL) {delete m_Age;m_Age = NULL;}//深拷贝m_Age =new int(*p.m_Age);return *this;}int* m_Age;
};
void main() {Person p1(18);cout << "p1的年龄为:" << *(p1.m_Age) << endl;Person p2(20);//若不进行赋值运算符重载则为浅拷贝p2 = p1;cout << "p2的年龄为:" << *(p2.m_Age) << endl;
}

注意:

  • C++中默认的拷贝构造函数就是浅拷贝
  • 若属性是在堆区开辟的,那么一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
  • 当拷贝一个对象的时候若需要拷贝这个对象所引用的对象,则是深拷贝,否则为浅拷贝

类对象作为类成员

前言:C++类中的成员可以是另一个类的对象,我们称该成员为对象成员

class A{}
class B{A a;
}

注意:B类中有对象A作为成员,A为对象成员

#include <iostream>
using namespace std;
class Phone {
public:string p_Name;Phone(string name) {p_Name = name;}
};
class Person {
public:Person(string name, string pname):m_Name(name),m_Phone(pname) {cout << "初始化列表" << endl;}string m_Name;Phone m_Phone;
};
void main() {Person p("lili", "apple");cout << "name:" << p.m_Name << endl;cout << "pname:" << p.m_Phone.p_Name << endl;system("pause");
}

注意:当其他类对象作为本类的成员,构造时候先构造本类的对象,再构造自身;先析构自身再析构本类的对象

静态成员

前言:

  • 静态成员就是在成员变量或成员函数上加上关键字static,称为静态成员
  • 静态成员不属于某个对象,所有的对象都共享同一份数据
  • 静态成员也是具有访问权限的,也遵循权限访问的规则

静态成员的访问方式

  • 通过对象进行访问(对象.静态成员
  • 通过类名进行访问(类名::静态成员

静态成员变量

  • 所有对象共享同一份数据
  • 在编译阶段分配内存
  • 类内声明,类外初始化
#include <iostream>
using namespace std;
class Person {
public://类内声明m_Astatic int m_A;
};
//类外初始化
int Person::m_A = 23;
void main() {Person p;Person p1;p1.m_A = 200;//通过对象进行访问cout << "m_A:" << p.m_A << endl;//200//通过类名进行访问cout << "m_A:" << Person::m_A << endl;//200system("pause");
}

静态成员函数

  • 所有的对象共享同一份函数
  • 静态成员函数只能访问静态成员变量
#include <iostream>
using namespace std;
class Person {
public:static void func() {//若访问非静态成员变量后则会报错m_A = 100;cout << "静态成员方法func的调用,m_A=" <<m_A<< endl;}static int m_A;
};
int Person::m_A = 23;
void main() {Person p;//通过对象进行访问p.func();//通过类名进行访问Person::func();system("pause");
}

注意:静态成员函数不可以访问非静态的成员变量,主要原因是静态成员所有的对象共享一份,若访问对应的非静态成员变量则不清楚访问的是哪个对象的成员变量

成员变量和成员函数分开存储

class Person {
public:int m_A;				//非静态成员变量,属于类的对象上,占用类对象的空间static int m_B;			//静态成员变量,不在类的对象上,不占用类对象的空间void func() {}			//非静态成员函数,不在类的对象上,不占用类对象的空间static void func1(){}	//静态成员函数,不在类的对象上,不占用类对象的空间
};

注意:

  • 空对象所占用的内存为1,C++编译器也会给每个空对象分配一个字节空间,是为了区分空对象所占内存的位置,每个空对象也应该有个独一无二的地址
  • 成员变量和成员函数是分开存储的,成员函数以及静态成员不存在类的对象上,非静态成员变量存在于类的对象上

this指针

问题:C++中的成员变量与成员函数分开存储,每个非静态成员函数只会诞生出一份函数实例,那么该如何区分是哪个对象调用这个函数呢

定义

含义:this指针指向被调用的成员函数所属的对象

注意:

  • this指针是隐含每一个非静态成员函数内的一种指针
  • this指针不需要定义,直接使用即可

this指针的用途

  • 当形参和成员变量同名时,可用this指针来进行区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this;
#include <iostream>
using namespace std;
class Person {
public://解决名称冲突Person(int age) {this->age = age;}int age;Person& PersonAdd(Person& p) {this->age += p.age;//返回调用该函数的对象return *this;}
};
void main() {Person p(18);Person p1(12);cout << "p的age:" << p.age << endl;p1.PersonAdd(p).PersonAdd(p);cout << "p1的age:" << p1.age << endl;system("pause");
}

注意:若PersonAdd函数以Person为返回值返回,那么返回的就不是哪个特定地址的person对象了,而是经过拷贝构造函数拷贝的新对象,因此一定要以引用的方式(Person&)返回

空指针访问成员函数

前言:C++中空指针也可以调用成员函数的,但是需要注意有没有用到this指针

#include <iostream>
using namespace std;
class Person {
public:void showClassName() {cout << "this is Person class" << endl;}void showPersonAge() {cout << "age=" << this->m_Age << endl;}int m_Age=18;static string m_Name;
};
string Person::m_Name = "lili";
void main() {Person *p=NULL;//调用正常,因为没有访问具体对象内的资源p->showClassName();//调用正常,空指针可以调用被static修饰的静态常量cout << "name=" << p->m_Name << endl;//调用失败,因为空指针不能访问对象内的资源cout << "age=" << p->m_Age << endl;//调用失败,因为空指针访问了具体对象p->showPersonAge();system("pause");
}

注意:空指针也可以访问类资源,以及不存在于对象上的资源,但是不可以访问相关对象上的资源

const修饰成员

常函数:

  • 成员函数后加const后我们就称该函数为常函数
  • 常函数不可以修饰成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改
class Person {
public://常函数void showPerson() const {//常函数内m_A不可以修改//this->m_A = 100;//加了mutable关键字后即使在常函数中也可以修改该值this->m_B = 300;}int m_A;mutable int m_B;
};

注意:

  • this指针的本质:指针常量,指针的指向不可以修改(就是指向本类对象)。 
  • 在成员函数后面加const本质上就是修饰该函数的this指针,进而使得this指针指向的值也不可以修改

常对象

  • 声明对象前加const,那么就称该对象为常对象
  • 常对象只能调用常函数,以及修改被mutable修饰的成员属性
#include <iostream>
using namespace std;
class Person {
public://常函数void showPerson() const {//常函数内m_A不可以修改//this->m_A = 100;//加了mutable关键字后即使在常函数中也可以修改该值this->m_B = 300;}int m_A;mutable int m_B;
};
void main() {//常对象const Person p;p.m_B = 39;cout << "m_B:" << p.m_B << endl;//39//常对象只能调用常函数p.showPerson();cout << "m_B:" << p.m_B << endl;//300system("pause");
}

友元

前言:

  • 在程序里,有些私有属性也想让类外的一些特殊的函数或者类进行访问,那么就需要友元技术
  • 友元的目的:让一个函数或者类访问另一个类的私有成员
  • 友元的关键字为friend

友元的三种实现

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元

全局函数做友元

#include <iostream>
using namespace std;
class Building {//全局函数做友元(说明goodGay函数是该类的好朋友,可以访问该类的私有属性)friend void goodGay(Building* building);
public:Building() {m_Room = "客厅";m_BedRoom = "卧室";}//客厅string m_Room;
private://卧室string m_BedRoom;
};
//全局函数
void goodGay(Building *building) {cout << "好基友全局函数,正在访问:" << building->m_BedRoom << endl;
}
void main() {Building building;goodGay(&building);
}

注意:友元的声明不需要放在权限修饰符内

类做友元

#include <iostream>
using namespace std;
class GoodGay {
public:Building* building;GoodGay() {building = new Building;}void visit() {cout << "好基友的类正在访问:" << building->m_BedRoom << endl;};
};
class Building {//类做友元,GoodGay类可以访问Building类的私有属性friend class GoodGay;
public:Building() {m_Room = "客厅";m_BedRoom = "卧室";}string m_Room;
private:string m_BedRoom;
};
void main() {GoodGay g;g.visit();
}

成员函数做友元

#include <iostream>
using namespace std;
//告诉编译器有该类,解决visit访问不到类内属性的问题
class Building;
class GoodGay {
public://让visit函数可以访问到Building的私有成员void visit();//让visit1函数不可以访问到Building的私有成员void visit1();GoodGay();
private:Building *building;
};
class Building {//成员方法做友元friend void GoodGay::visit();
public:Building();string m_Room;
private:string m_BedRoom;
};
Building::Building() {m_Room = "客厅";m_BedRoom = "卧室";
}
GoodGay::GoodGay() {building = new Building;
}
void GoodGay::visit() {cout << "好基友的类正在访问:" << building->m_BedRoom << endl;
}
void GoodGay::visit1() {cout << "好基友的类正在访问building->m_BedRoom,但是访问不到!" << endl;
}
void main() {GoodGay g;g.visit();g.visit1();
}

注意:声明成员函数为友元时必须确定该函数是存在的

运算符重载

概念:对已有的运算符进行重新定义,赋予其另一种功能,以适应不同的数据类型

重载运算符方式

  • 通过成员函数重载
  • 通过全局函数重载

加号运算符重载 

通过成员函数进行重载 

语法:将方法名改为——operator运算符 

作用:实现两个自定义数据类型相加的运算

说明:p=p1+p2        <=>        p=p1.operator+(p2)

#include <iostream>
using namespace std;
class Person {
public:int m_A;int m_B;Person operator+(Person& p) {Person addP;addP.m_A = this->m_A + p.m_A;addP.m_B = this->m_B + p.m_B;return addP;}
};
void main() {Person p;p.m_A = 20;p.m_B = 11;Person p1;p1.m_A = 30;p1.m_B = 9;Person p2 = p + p1;cout << "p2的m_A:" << p2.m_A << "p2的m_B:" << p2.m_B << endl;//m_A:50	m_B:20
}

注意:这里的p+p1等价于p.operator+(p1)

通过全局函数进行重载

说明:p=p1+p2        <=>        p=operator+(p1,p2)

#include <iostream>
using namespace std;
class Person {
public:int m_A;int m_B;
};
Person operator+(Person& p,Person& p1) {Person addP;addP.m_A = p.m_A + p1.m_A;addP.m_B = p.m_B + p1.m_B;return addP;
}
//运算符重载的函数重载
Person operator+(Person& p, int a) {Person addP;addP.m_A = p.m_A + a;addP.m_B = p.m_B + a;return addP;
}
void main() {Person p;p.m_A = 20;p.m_B = 11;Person p1;p1.m_A = 30;p1.m_B = 9;Person p2 = p + p1;cout << "p2的m_A:" << p2.m_A << "p2的m_B:" << p2.m_B << endl;//m_A:50	m_B:20Person p3 = p + 10;cout << "p3的m_A:" << p3.m_A << "p3的m_B:" << p3.m_B << endl;//m_A:30	m_B:21
}

注意:

  • 运算符重载也可以发生函数重载(根据传入的参数类型不同,运算符重载的作用也不同)
  • 对于内置的数据类型的表达式的运算符是不可能改变的(必须是自定义的数据类型)

左移运算符重载

作用:可以输出自定义的数据类型

注意:我们通常不会使用成员函数重载左移运算符,因为无法实现cout在左侧(因为要用自动自定义的对象调用导致自定义的对象始终在左边,也就是p<<cout)

通过全局函数重载左移运算符

注意:我们想要cout在左边,自定义对象在右边,因此cout在第一个参数,自定义对象在第二个参数

#include <iostream>
using namespace std;
class Person {
public:int m_A;int m_B;
};
//等价于只是重写cout<<Person;
//return cout来实现链式访问
ostream& operator<<(ostream& cout,Person& p) {cout << "m_A:" << p.m_A << "\nm_B:" << p.m_B;return cout;
}
void main() {Person p;p.m_A = 20;p.m_B = 30;cout << p << endl;
}

递增运算符重载

作用:可以通过递增运算符重载,进而实现自己的整形数据

前置++:MyInteger& operator++();

后置++:MyInteger operator++(int);

注意:参数列表中的int参数用来区分前置++还是后置++(int在这里是占位符的意思)

#include <iostream>
using namespace std;
class MyInteger {friend ostream& operator<<(ostream& cout, MyInteger a);
public:MyInteger() {m_Num = 1;}//重载前置++运算符,返回引用是为了一直对一个对象进行操作MyInteger& operator++() {//前置++需要先进行++运算,再将自身做一个返回m_Num++;return *this;}//重载后置++运算符//加上int参数,那么编译器就会文伟这是后置运算符的重载了//后置++不可以返回值为引用类型,因为是临时的值,用完后就会释放MyInteger operator++(int) {//记录当前结果MyInteger temp = *this;//后递增m_Num++;//最后将记录的结果返回return temp;}
private:int m_Num;
};
//重载左移运算符
ostream& operator<<(ostream& cout, MyInteger a) {cout << "m_Num:" << a.m_Num;return cout;
}
void main() {MyInteger myInt;cout << myInt++ << endl;//1cout << ++myInt << endl;//3
}

注意:前置递增返回的是引用,后置递增返回的是值

赋值运算符重载

C++编译器至少会给一个类添加4个函数

  • 默认的构造函数(无参构造)
  • 默认的析构函数(里面不能有参数)
  • 默认的拷贝构造函数,会对属性内的值进行拷贝
  • 赋值运算符operator=来对属性进行值拷贝

说明:p1=p2 <=> p1.operator=(p2) 

#include <iostream>
using namespace std;
class Person {
public:Person(int age) {m_Age = new int(age);}//浅拷贝带来问题,堆区内存重复释放~Person() {if (m_Age != NULL) {delete m_Age;m_Age = NULL;}}//重载赋值运算符Person& operator=(Person& p) {//先判断是否有属性在堆区,若有先释放干净再深拷贝if (m_Age != NULL) {delete m_Age;m_Age = NULL;}//深拷贝m_Age =new int(*p.m_Age);return *this;}int* m_Age;
};
void main() {Person p1(18);cout << "p1的年龄为:" << *(p1.m_Age) << endl;Person p2(20);//若不进行赋值运算符重载则为浅拷贝p2 = p1;cout << "p2的年龄为:" << *(p2.m_Age) << endl;
}

关系运算符重载

作用:重载关系运算符,可以让两个自定义类型的对象进行对比操作

重载==号

说明:p1==p2 <=> p1.operator==(p2)

#include <iostream>
using namespace std;
class Person {
public:Person(string name,int age) {m_Name = name;m_Age = age;}//重载==号bool operator==(Person& p) {if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {return true;}else {return false;}}string m_Name;int m_Age;
};
void main() {Person p1("lili",18);Person p2("lili",18);if (p1 == p2) {cout << "p1和p2是相等的" << endl;}else {cout << "p1和p2是不相等的" << endl;}
}

函数调用运算符重载

前言:

  • 函数调用运算符()也可以进行运算符重载
  • 由于重载后的使用方式非常类似于函数的调用,因此也称为仿函数
  • 仿函数没有固定写法,非常灵活
  • 仿函数后面也可以接收返回值 

说明:对象(参数) <=> 对象.operator()(参数)

具体案例 

#include <iostream>
using namespace std;
class MyAdd {
public://重载函数调用运算符int operator()(int a,int b){return a + b;}
};
void main() {MyAdd myAdd;int add=myAdd(100, 200);cout << "add:" << add << endl;//匿名函数对象cout << "add:" << MyAdd()(200,600) << endl;
}

继承

类与类之间的继承关系

总结:我们发现下级别的成员除了具有上一级的特性,同时还有自己的特性,这个时候我们就考虑用继承

继承语法:

class 子类 : 继承方式 父类{
子类特有的代码;
}

注意:这里面的父类也称基类,主要是子类所复用的类,这里的子类也称为派生类 

继承的经典案例

#include <iostream>
using namespace std;
class Father {
public:void walk() {cout << "walk long" << endl;}
private:void jump() {cout << "jump up" << endl;}
};
class Child:public Father {
public:void run() {cout << "run fast" << endl;}
};
void main() {Child c;c.run();c.walk();
}

总结:

  • 子类具有父类的所有属性资源(父类的私有资源不可访问),同时也可以有自己独立的资源
  • 继承的好处:减少重复的代码,提高代码的复用

继承的方式种类

  • 公共继承
  • 保护继承
  • 私有继承

理解结构图

总结:

  • 对于父类私有成员,不管子类用哪种继承方式,子类都不可访问
  • 对于公共继承方式,子类按照父类的属性修饰符来继承该属性
  • 对于保护继承,子类将父类的所有非私有属性按照保护权限修饰符的方式进行继承
  • 对于私有继承,子类将父类的所有属性按照私有属性修饰符的方式进行继承

继承中的对象属性

#include <iostream>
using namespace std;
class A {
public:int m_A;
protected:int m_B;
private:int m_C;
};
class B:public A {
public:int m_D;
};
void main() {B b;//B对象的大小为:16cout << "B对象的大小为:" << sizeof(B) << endl;
}

总结:子类继承了父类的所有非静态成员属性,当然也包括父类的私有属性,只是父类的私有属性子类不可见

继承中构造和析构的顺序

#include <iostream>
using namespace std;
class A {
public:A() {cout << "父类构造函数" << endl;}~A() {cout << "父类析构函数" << endl;}
};
class B:public A {
public:B() {cout << "子类构造函数" << endl;}~B() {cout << "子类析构函数" << endl;}
};
void main() {B b;
}

总结:构造子类对象时会先构造他的父类对象后再构造自身对象,清理对象时会先清理自身对象再清理父类对象(主要原因:父类属性被子类所复用,父类属性是子类属性的一部分,必须要保证子类属性的完整性)

继承同名成员处理方式

问题:当子类与父类出现同名成员,如何通过子类对象访问到子类或父类中的同名数据呢

#include <iostream>
using namespace std;
class A {
public:int m_A = 100;void sayHello() {cout << "A-hello" << endl;}void sayHello(string a) {cout << "A-hello"<<a << endl;}
};
class B:public A {
public:int m_A = 200;void sayHello() {cout << "B-hello" << endl;}
};
void main() {B b;//访问子类同名成员,直接访问即可cout << "子类的m_A=" << b.m_A << endl;//200b.sayHello();//访问父类同名成员,需要添加作用域cout << "父类的m_A=" << b.A::m_A << endl;//100b.A::sayHello();b.A::sayHello(" world");
}

注意:

  • 子类对象可以直接访问到子类中的同名成员(子类对象.同名属性名)
  • 子类对象加作用域可以访问到父类的同名成员(子类对象.父类::同名属性名)
  • 若子类中出现了和父类同名的成员函数,子类的同名成员函数会隐藏掉父类中所有的同名成员函数(包括有参数和无参数的函数),若想要访问到父类中被隐藏的同名成员函数,那么需要加上作用域

继承中同名静态成员的处理方法

前言:静态成员和非静态成员出现同名,处理方式一致

#include <iostream>
using namespace std;
class A {
public:static int m_A;static int m_B;static void sayHello() {cout << "A-hello" << endl;}static void sayHello(string a) {cout << "A-hello"<<a << endl;}
};
//类外初始化
int A::m_A = 100;
int A::m_B = 500;
class B:public A {
public:static int m_A;static void sayHello() {cout << "B-hello" << endl;}
};
int B::m_A = 200;
void main() {B b;//访问子类同名成员,直接访问即可cout << "子类的m_A=" << b.m_A << endl;//200cout << "子类的m_A=" << B::m_A << endl;//200//子类访问父类的静态属性cout << "父类的m_B=" << B::m_B << endl;//500b.sayHello();//访问父类同名成员,需要添加作用域cout << "父类的m_A=" << b.A::m_A << endl;//100cout << "父类的m_A=" << B::A::m_A << endl;//100b.A::sayHello();b.A::sayHello(" world");
}

总结:

  • 子类可以直接访问父类的静态成员属性(我的理解:静态成员属于类资源,而子类属于父类)
  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需要加作用域
  • 若子类中出现了和父类同名的成员静态函数,子类的同名成员静态函数会隐藏掉父类中所有的同名成员静态函数(包括有参数和无参数的函数),若想要访问到父类中被隐藏的同名成员静态函数,那么需要加上作用域
  • 同名静态成员处理方式和静态成员处理方式一样,只不过有两种访问方式(通过对象和通过类名)

C++中的多继承

前言:在C++中允许一个类继承多个类

语法:

class 子类 : 继承方式 父类1,继承方式 父类2{
子类特有的代码;
}

注意:多继承可能会引发父类中的同名成员出现,需要加作用域区分

#include <iostream>
using namespace std;
class A {
public:int m_A = 10;
};
class B{
public:int m_A = 20;
};
class C :public B, public A {
public:int m_A = 30;
};
void main() {C c;cout << "c对象的大小为:" << sizeof(c) << endl;//12cout << "A中m_A的值为:" << c.A::m_A << endl;cout << "B中m_A的值为:" << c.B::m_A << endl;cout << "C中m_A的值为:" << c.m_A << endl;
}

菱形继承

含义:两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承被称为菱形继承或者钻石继承

经典案例

菱形继承带来的问题

  • 羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据时会产生二义性(比如究竟使羊的动物属性A还是使用驼的动物属性A——这种可以通过作用域区分)
  • 羊驼继承来自动物的数据继承了两份,其实我们应该清楚,这样的数据我们只需要一份即可

虚继承

前言:

  • 在继承前(public前)加关键字virtual变为虚继承来解决菱形继承出现的多份数据问题
  • 被虚继承的类称为虚基类
  • 当发生虚继承之后,那么子类所拥有的相同父类的虚基类属性(该属性必须是从虚基类上继承下来的,不能是自己的)将只存在一份
#include <iostream>
using namespace std;
class Animal {
public:int m_A = 10;
};
class Sheep :virtual public Animal {};
class Tuo :virtual public Animal {};
class YangTuo :public Sheep, public Tuo {};
void main() {YangTuo n;//不是虚继承时cout << "羊驼访问羊的m_A数据:" << n.Sheep::m_A << endl;cout << "羊驼访问驼的m_A数据:" << n.Tuo::m_A << endl;//虚继承时cout << "羊驼访问羊的m_A数据:" << n.m_A << endl;
}

注意:当菱形继承出现时,两个父类拥有相同的数据,所以要访问特定的父类数据应该加作用域加以区分,但是若是虚继承,所继承的父类虚基类的属性将只存在一份,可以直接通过子类对象访问

多态

多态的理解:一个事物的多种形态

多态的分类

  • 静态多态:函数重载和运算符重载(复用函数名)
  • 动态多态:派生类和虚函数实现运行时多态

虚函数:在父类的函数的返回值类型前加virtual关键字,那么父类对应的函数就会变成一个虚函数,对应的函数地址可以实现晚绑定

静态多态和动态多态的区别

  • 静态多态的函数地址早就绑定——编译阶段确认函数的地址
  • 动态多态的函数地址晚绑定——运行阶段确认函数地址

动态多态的满足条件

  • 有继承关系
  • 子类重写父类的虚函数

动态多态的使用:父类的指针或者引用指向子类的对象 

函数重写:子类重写父类的函数,其函数的返回值类型,函数名与参数列表完全相同

案例分析

#include <iostream>
using namespace std;
class Animal {
public://函数地址早绑定void say() {cout << "动物在说话" << endl;}//虚函数,可以实现函数地址晚绑定virtual void run() {cout << "动物在疾跑" << endl;}
};
class Cat:public Animal {
public:void say() {cout << "猫在喵喵叫" << endl;}void run() {cout << "小猫在散步" << endl;}
};
//执行说话的函数
void doWork(Animal& animal) {animal.say();//动物在说话animal.run();//小猫在散步
}
void main() {Cat cat;doWork(cat);
}

注意:

  • 在C++中允许父子之间的类型转换(父类引用指向子类对象)
  • 对于父类普通函数的话,用父类引用指向子类对象。函数地址早绑定,在编译阶段就已经确认了函数地址,若调用对应的函数将会是父类的函数
  • 对于父类的虚函数(函数返回值类型前加关键字virtual),用父类引用指向子类对象。函数地址晚绑定,函数地址在运行阶段确认,若调用对应的函数将会是子类重写父类对应的函数
  • C++规定,当一个函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数,因此在子类中重新声明该虚函数时,virtual关键字可以加也可以不加。

动态多态的原理剖析

#include <iostream>
using namespace std;
class Animal1 {
public:void say() {cout << "动物在说话" << endl;}
};
class Animal2{
public:virtual void say() {cout << "动物在说话" << endl;}
};
void main() {cout << "Animal1所占内存空间:" << sizeof(Animal1) << endl;//1cout << "Animal2所占内存空间:" << sizeof(Animal2) << endl;//4
}

解释:因为成员函数的存储并不在类的对象上,所以Animal1为一个空对象,所占内存大小为1,而Animal2所占内存空间大小为4,其实Animal2中的4字节空间为一个vfptr(虚函数表指针)该vfptr会指向一个虚函数表(vftable);表的内部存放的是Animal1的虚函数地址(&Animal1::say);当Cat继承了Animal1后,那么便将父类的所有属性均拿过来一份,也就有了父类的那个虚拟函数表指针vfptr,该指针指向了子类的虚函数表(vftable)当子类重写了父类虚函数方法,那么子类的方法将会将子类的虚函数表中的(父类原有的)方法进行覆盖;当父类引用指向子类对象Animal1& animal=cat对象(右值里面有子类的虚函数表);然后在通过animal调用对应的虚函数,将会调用子类虚函数表中的虚函数(因为右值赋予左面的变量)。

纯虚函数和抽象类

前言:在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类的重写内容,因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual 返回值类型 函数名(参数列表)=0;

注意:当类中有了纯虚函数(只要有一个),那么这个类也称抽象类

抽象类特点

  • 抽象类无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

虚析构和纯虚析构

前言:在多态使用时,若子类中有属性开辟到了堆区,那么父类指针在释放时无法调用子类的析构代码

解决方式:将父类中的析构函数改为虚析构或纯虚析构

虚析构和纯虚析构共性与区别

虚析构和纯虚析构共性

  • 可以解决父类指针释放子类对象
  • 都需要具体的实现

虚析构和纯虚析构区别

  • 若父类中的的析构函数是纯虚析构,那么该类也属于抽象类,无法实例化对象

语法:

虚析构:virtual ~类名(){}

纯虚析构:virtual ~类名()=0;

纯虚析构类外实现:类名::~类名(){} 

注意:纯虚析构需要声明,也需要实现(类外实现),因为由于内存的释放导致析构函数会用到

经典案例

#include <iostream>
using namespace std;
class Animal {
public://纯虚函数virtual void say() = 0;//纯虚析构函数virtual ~Animal() = 0;
};
//纯虚析构函数实现
Animal::~Animal() {cout << "Animal的纯虚析构函数" << endl;
}
class Cat :public Animal {
public:~Cat() {if (m_Name != NULL) {cout << "Cat的析构函数调用了" << endl;delete m_Name;m_Name = NULL;}}Cat(string name) {m_Name = new string(name);}virtual void say() {cout << *m_Name<<"小猫喵喵的叫!" << endl;}string* m_Name;
};
void main() {Animal* animal = new Cat("Tom");animal->say();delete animal;
}

理解:在堆区中通过父类指针指向子类对象,那么父类的指针在释放时(delete释放指针所在的内存空间)无法调用到子类的析构代码,只会调用到父类的析构代码,这时,只要你将父类的析构函数改为虚析构或纯虚析构,那么delete语句调用的时候便会因为多态的重写原理调用到子类的析构函数,但是由于析构对象时会先析构自身对象再析构父类对象,最终导致子类以及父类对象的析构函数都调用了。

总结:

  • 虚析构或纯虚析构就是用来解决父类指针释放子类对象
  • 若子类中没有堆区数据,可以不写为虚构函数或纯虚析构
  • 拥有纯虚析构函数的类也是抽象类

相关文章:

C++之面向对象

目录 对象与类 类的语法&#xff1a; C中class与struct的区别&#xff1a; 通过类实例化对象的方式 具体案例 类作用域与分文件编写 创建circle.h头文件 创建源文件circle.cpp 创建all.cpp来作为程序的入口 封装 封装的意义 访问权限符 成员属性私有化 优点 具体…...

Windows-》CMD命令

CMD命令【1】Windows-》CMD命令1.mstsc&#xff1a;打开远程桌面连接。2.services.msc&#xff1a;打开本地服务设置。3.notepad&#xff1a;打开记事本。4.control&#xff1a;打开控制面板。5.regedit&#xff1a;打开注册列表编辑器。6.compmgmt.msc---设备管理器。&#xf…...

秒级使网站变灰,不改代码不上线,如何做到?

注意&#xff1a;文本不是讲如何将网站置灰的那个技术点&#xff0c;那个技术点之前汶川地震的时候说过。 本文不讲如何实现技术&#xff0c;而是讲如何在第一时间知道消息后&#xff0c;更快速的实现这个置灰需求的上线。 实现需求不是乐趣&#xff0c;指挥别人去实现需求才…...

vue教程

vue window本地保存Local Storage 保存&#xff1a;window.localStorage.setItem(名,值); window.localStorage.setItem(token,backdata.data[2]); 查询&#xff1a;window.localStorage.getItem(名); window.localStorage.getItem(token); 删除&#xff1a;window.localStor…...

认识哈希表

作者&#xff1a;~小明学编程 文章专栏&#xff1a;Java数据结构 格言&#xff1a;目之所及皆为回忆&#xff0c;心之所想皆为过往 目录 为什么我们需要哈希表&#xff1f; 哈希表的原理 什么是哈希值 冲突 负载因子 解决冲突 闭散列 开散列/哈希桶 代码实现 不考虑…...

Vue学习:Hello小案例

使用Vue的目的&#xff1a;构建用户界面&#xff08;需要使用容器 摆放这个界面的内容&#xff09; favicon.ico:1 GET http://127.0.0.1:5500/favicon.ico 404 (Not Found) 没有页签图标 在者服务器中 http://127.0.0.1:5500没有/favicon.ico 强制刷新网页&#xff1a;s…...

IDEA创建Java Web项目

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;JAVA开发者…...

C++11中可变参数模板使用

在看同事编写的代码&#xff0c;发现有如下的代码&#xff0c;因为没用过&#xff0c;所以查了一下这是什么语法&#xff0c;通过查询资料知道了这是C11中增加的可变参数模板。 template<class T, class ...Args> bool GetValue(T &value, Args &&...args) c…...

MySQL 中的 sql_mode 选项以及配置

MySQL 中的 sql_mode 选项以及配置 目录MySQL 中的 sql_mode 选项以及配置一、查询与设置 sql_mode1、查询 sql_mode2、设置 sql_mode&#xff08;1&#xff09;通过命令设置&#xff08;2&#xff09;在配置文件中设置二、sql_mode 支持的模式1、ANSI 模式&#xff08;宽松模式…...

【C语言航路】第六站:指针初阶

目录 一、指针是什么 二、指针和指针类型 1.指针类型的意义 2.指针-整数 3.指针解引用 三、野指针 1.野指针的成因 &#xff08;1&#xff09;指针未初始化 &#xff08;2&#xff09;指针越界访问 &#xff08;3&#xff09;指针指向的空间释放 2.如何规避野指针 &a…...

从 JPA 2.x 迁移到 3.0

我最近收到了很多关于JPA 3.0的问题&#xff0c;由于EclipseLink和Hibernate现在提供了对它的全面支持&#xff0c;现在是时候仔细看看规范的最新更新了。作为从Java EE到Jakarta EE转换的一部分&#xff0c;Java Persistence API&#xff08;JPA&#xff09;更名为Jakarta Per…...

Allegro如何锁定器件操作指导

Allegro如何锁定器件操作指导 Allegro上可以锁定器件,避免误操作被移动,具体操作如下 选择fix命令 Find选择Symbols 框选需要锁定的器件 可以看到器件被锁住了 除了这个方法之外,还有另外一种方法锁定器件,选择edit-property Find选择Symbols...

第三章:SpringBoot的配置文件-核心技术

目录 1、文件类型 1.1、properties 1.2、yaml 1.2.1、简介 1.2.2、基本语法 1.2.3、数据类型 1.2.4、示例 2、配置提示 1、文件类型 1.1、properties 同以前的properties用法 1.2、yaml 1.2.1、简介 YAML 是 "YAML Aint Markup Language"&#xff08;YA…...

Vue中的计算属性

计算属性&#xff1a;实际上是把vm中的属性进行计算加工&#xff0c;最后能够返回给页面一个结果&#xff0c;它是实时的&#xff0c;所以不能做异步操作。 细想一下&#xff0c;其实methods方法也能实现1中描述的现象&#xff0c;但是计算属性最大的优势是缓存&#xff01;&a…...

HTML+CSS个人电影网页设计——电影从你的全世界路过(4页)带音乐特效

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…...

业务开发时,接口不能对外暴露的解决方案

1.内外网接口微服务隔离 将对外暴露的接口和对内暴露的接口分别放到两个微服务上,一个服务里所有的接口均对外暴露,另一个服务的接口只能内网服务间调用。 该方案需要额外编写一个只对内部暴露接口的微服务,将所有只能对内暴露的业务接口聚合到这个微服务里,通过这个聚合…...

「点燃我,温暖你」用Python制作一个动态爱心效果

最近「点燃我&#xff0c;温暖你」这部剧非常火&#xff0c;讲述的是程序员的爱情故事。 其中陈飞宇饰演的男主李峋&#xff0c;在剧中用程序做出的爱心跳动效果&#xff0c;非常炫。 网上各个大佬也是纷纷给出看法&#xff0c;综合就是不太可能用C语言来实现的。 大概率是AE…...

初识猿如意开发工具

嗨&#xff0c;大家好&#xff0c;我是异星球的小怪同志 一个想法有点乱七八糟的小怪 如果觉得对你有帮助&#xff0c;请支持一波。 希望未来可以一起学习交流。 一、初遇猿如意 第一次听说猿如意开发工具&#xff0c;于是抱着试试的心态&#xff0c;开始下载尝试。 首先是…...

PyTorch 2.0发布了,一行代码提速76%

PyTorch 官方&#xff1a;我们这次的新特性太好用了&#xff0c;所以就直接叫 2.0 了。 前段时间&#xff0c;PyTorch 团队在官方博客宣布 Pytorch 1.13 发布&#xff0c;包含 BetterTransformer 稳定版等多项更新。在体验新特性的同时&#xff0c;不少人也在期待下一个版本的推…...

栈的基本操作

一&#xff0c;自定义函数 #include<bits/stdc.h> using namespace std; const int N 1000;//N是常量 int s[N 1];//栈 int top;//栈顶的位置(栈内有多少个元素) //入栈 void push(int x){ //非满栈 if(top < N){ //将栈顶上移并存入 top…...

windows内核编程-文件操作

文章目录前言背景知识点介绍字符串内存分配与回收文件操作自旋锁win内核编程-日志实现文件操作的封装日志功能的实现简单的测试日志功能其他前言 本文完整代码见&#xff1a;demo/12-win-driver-log 我们必须先搞定&#xff0c;DebugView可以看到DbgPrintDbgPrint的输出。否则…...

Hadoop原理与技术——Linus命令行基本操作

点击链接查看文档 一、实验目的 Hadoop运行在Linux系统上&#xff0c;因此&#xff0c;需要学习实践一些常用的Linux命令。本实验旨在熟悉常用的Linux操作&#xff0c;为顺利开展后续其他实验奠定基础。 二、实验环境 Windows 10 VMware Workstation Pro虚拟机 Hadoop环境 J…...

目标检测算法——YOLOv5/YOLOv7改进之结合特征提取网络RFBNet(涨点明显)

>>>深度学习Tricks,第一时间送达<<< 🚀🚀🚀NEW!!!魔改YOLOv5/v7目标检测算法来啦 ~ 计算机视觉——致力于目标检测领域科研Tricks改进与推荐 | 主要包括Backbone、Neck、Head、普通注意力机制、自注意力机制Transformer、Swin Transformer v2,各…...

anaconda使用系列教程--4)环境迁移

概述 跨平台尽量避免&#xff0c;比如windows和linux就不要跨平台&#xff0c;就在linux之间跨还是可以的 直接copy整体环境文件&#xff0c;适合于无法联网或网速不佳的新环境 anaconda最好是同版本的 迁移方法 使用requirement文件 A机器&#xff1a; pip freeze > …...

Kotlin高仿微信-第57篇-VIP管理列表

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…...

贪心算法-- 纪念品分组

问题描述元旦快到了&#xff0c;校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念品价值 相对均衡&#xff0c;他要把购来的纪念品根据价格进行分组&#xff0c;但每组最多只能包括两件纪念品&#xff0c;并且每组纪念品的价格之和不能超过一个给…...

java-php-net-python-代驾网站计算机毕业设计程序

java-php-net-python-代驾网站计算机毕业设计程序 java-php-net-python-代驾网站计算机毕业设计程序本源码技术栈&#xff1a; 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 开发软件&#xff1a;idea eclipse 前端技术&#xff1a;Layui、HTML、CSS、JS、JQ…...

简洁自增ID实现方案

简介 从数据库性能角度考虑&#xff0c;我们经常需要数字型的自增主键&#xff0c;有时候我们并不想用像MySQL自带的自增&#xff0c;因为从1开始很数据位数不一样&#xff0c;对有点强迫症的人来说&#xff0c;不是很友好。 另外&#xff0c;别人也容易根据这种从1开始的自增…...

JMeter入门教程(14)——场景设计

1.JMeter中场景设计是通过线程组来实现的 如图&#xff1a; 控制面板中各元素介绍&#xff1a; 名称&#xff1a;可以随意设置&#xff0c;最好有业务意义。 注释&#xff1a;可以随意设置&#xff0c;可以为空。 在取样器错误后要执行的动作&#xff1a;其中的某一个请求出错后…...

SVM 用于将数据分类为两分类或多分类(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…...

转行软件测试我后悔了

很多时候&#xff0c;都在想当初做的转行软件测试行业的决定是对的吗&#xff1f;现在后悔还来得及吗&#xff1f; 记得在求职的时候&#xff0c;面试官经常问我&#xff1a;“为什么要选择软件测试工作?” 而我也会经常说一堆自己有的没的优势去应付。 工作这么久了&#x…...

k8s教程(15)-pod之亲和性与互斥性调度

文章目录01 引言02 pod亲和性调度与互斥性调度2.1 拓扑域2.2 举例2.2.1 参照目标pod2.2.2 pod的亲和性调度2.2.3 pod的互斥性调度03 其它04 文末01 引言 声明&#xff1a;本文为《Kubernetes权威指南&#xff1a;从Docker到Kubernetes实践全接触&#xff08;第5版&#xff09;》…...

浅谈降维实操,一种用于处理特征的方式——后附Python代码

&#x1f466;&#x1f466;一个帅气的boy&#xff0c;你可以叫我Love And Program &#x1f5b1; ⌨个人主页&#xff1a;Love And Program的个人主页 &#x1f496;&#x1f496;如果对你有帮助的话希望三连&#x1f4a8;&#x1f4a8;支持一下博主 降维实操前言线性降维低…...

深度学习中的正则化——L1、L2 和 Dropout

正则化是一组技术&#xff0c;可以防止神经网络中的过度拟合&#xff0c;从而在面对来自问题域的全新数据时提高深度学习模型的准确性。 在本文中&#xff0c;我们将介绍最流行的正则化技术&#xff0c;称为 L1、L2 和 dropout。 文章目录1. 回顾&#xff1a;过拟合2.什么是正则…...

数据结构(王卓)(4)附:链表的销毁与清空

销毁 Status 销毁单链表(LinkList L) {LinkList p;while (L){p L;L->next;delete p;}return OK; } 运行逻辑&#xff1a; &#xff08;1&#xff09;:设定一个指针&#xff0c;让指针指向链表的头指针L &#xff08;2&#xff09;&#xff1a;让头指针等于头指针里面指向下…...

C++图书管理系统(管理员-读者)

C图书管理系统&#xff08;管理员-读者&#xff09; 一、设计一款文字式交互的图书管理系统&#xff0c;要求具备注册登录、浏览图书、借还图书等基本功能&#xff1b; 二、要求以外部文件的形式存储书籍信息、馆藏记录、借阅记录、用户信息等。【可参考提供的书籍清单】 三…...

永磁同步电机恒压频比(V/F)开环控制系统Matlab/Simulink仿真分析及代码生成到工程实现

文章目录前言一、 恒压频比&#xff08;V/F&#xff09;控制原理二、永磁同步电机恒压频比开环控制系统Matlab/Simulink仿真分析2.1.仿真电路分析2.1.1.恒压频比控制算法2.1.2.输出处理2.1.3.主电路2.2.仿真结果分析2.2.1.设定目标转速为1200r/min2.2.1.设定目标转速为变化值三…...

基于JSP网上书城的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…...

java计算机毕业设计-学生宿舍故障报修管理信息系统-源程序+mysql+系统+lw文档+远程调试

java计算机毕业设计-学生宿舍故障报修管理信息系统-源程序mysql系统lw文档远程调试 java计算机毕业设计-学生宿舍故障报修管理信息系统-源程序mysql系统lw文档远程调试本源码技术栈&#xff1a; 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 开发软件&#xf…...

vue学习笔记——简单入门总结(四)

文章目录1.Vue3的特性和变化1.1.创建vue3项目1.2.分析main.js变化&#xff1a;1.3.setup--组合式api的开端1.4.ref函数和reactive函数&#xff1a;1.5.watch监视属性1.5.watchEffect函数1.6.vue3生命周期&#xff1a;1.Vue3的特性和变化 1.1.创建vue3项目 1.这里我们使用脚手架…...

数据结构与算法—数组栈和链表栈

数据结构与算法—数组栈和链表栈 &#x1f308;一览众山小数据结构与算法—数组栈和链表栈栈介绍栈图解栈实现数组实现栈实现思路实现代码单链表实现栈实现思路(图解)实现代码栈总结栈力扣栈介绍 栈,存储货物或供旅客住宿的地方,可引申为仓库、中转站&#xff0c;所以引入到计算…...

【应用】Kubernetes

Kubernetesk8s 安装流程安装前配置安装 docker 以及 k8s 组件初始化 k8s 集群测试使用&#xff1a;安装 kubernetes-dashboardk8s 基础概念k8s 核心技术概念k8s 内部组件k8s 中的 IP 地址K8S 服务暴露的三种方式k8s 安装流程 k8s-masterk8s-node01k8s-node02192.168.86.140192…...

GEE:关系、条件和布尔运算

ee.Image对象具有一组用于构建决策表达式的关系、条件和布尔运算方法。这些方法可以用来掩膜、绘制分类地图和重新赋值。 本文记录了在GEE&#xff08;Google Earth Engine&#xff09;平台上的关系运算符和布尔运算符&#xff0c;分别应用到了三个不用的场景&#xff08;筛选低…...

Java项目:SSM的KTV管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 主要功能包括&#xff1a; 登陆页,房间信息,房间管理,开取房间,房间查询,会员管理,食物管理,查看订单,查询订单,查看房间消费使用情况等功能。…...

包装类-Wrapper

包装类的分类 针对八种基本数据类型相应的引用类型-包装类有了类的特点&#xff0c;就可以调用对应的类中的方法 装箱和拆箱 Java是一种面向对象的编程语言&#xff0c;学习Java时就被明确灌输了一个概念&#xff1a;OOP&#xff0c;即面向对象编程。一切皆对象。但是基本…...

计算机网络——运输层【重点】

运输层概述 概念 进程之间的通信 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。当网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时&…...

ADSP-21569/ADSP-21593的开发入门(上)

作者的话 ADI的SHARC系列DSP&#xff0c;目前已经出到5系列了&#xff0c;最新的一颗叫2159x&#xff0c;按照ADI的说法&#xff0c;这颗DSP的性能是21569的两倍&#xff0c;但又能和21569做P2P的替换&#xff0c;所以下面我们就以21593为例&#xff0c;写一点资料&#xff0c…...

【Android App】在线直播之搭建WebRTC的服务端(图文解释 简单易懂)

有问题或需要源码请点赞关注收藏后评论区留言私信~~~ 一、WebRTC的系统架构 WebRTC&#xff08;网页即时通信&#xff09;是一个支持浏览器之间实时音视频对话的新型技术&#xff0c;WebRTC体系由应用于实时通信的编程接口和一组通信协议组成&#xff0c;已成为互联网流媒体通…...

x265 帧间预测

帧间编码入口函数&#xff1a; 从 Analysis::compressCTU 是ctu编码的入口函数&#xff0c;根据 slice 类型判断是 I 还是 BP&#xff0c;如果是BP则执行帧间编码函数 Analysis::compressInterCU_rdx_x&#xff1a;&#xff1a;/*压缩分析CTU过程&#xff1a;1.为当前CTU加载QP…...

【sciter】安全应用列表控件总结

一、效果图 二、功能点 实现电脑文件拖拽进入到安全桌面,读取文件路径,生成应用。可以配置允许拖拽进入安全桌面的文件应用。点击添加图标,可以添加应用到安全桌面中。在安全桌面列表中每一个应用实现双击、失去焦点,获取焦点、右键事件在安全桌面列表中每一个应用可以实现…...

宝马3系5系X3X5等多款车型价格历史新低,抄底时机来了

最近有一张宝马行情的照片,很多粉丝问我真的不真,我来一个一个车型来解读一下,看完就知道豪华车的价格这到底是怎么了?首先X1,31.69的车现在只要18.69万,这价格肯定是有条件的,但是这一款可不是三缸的那款,是四缸2.0T的,iD9的系统,你说新款X1这车外观没问题把,后排空…...

再破记录!东风风神L7无补能2162.8公里往返穿越羌塘

想象一下驰骋在高速公路上,周围是辽阔的羌塘无人区,一望无际的荒野,野生动物自由穿梭,神秘而充满挑战。这条路就是传说中的216国道,无疑是最无人居住的地区道路。从新疆民丰到西藏改则,800公里无人区没有加油站,这是对车辆极限性能和团队冒险精神的双重考验!最近,在这…...

2.0T275马力,油耗6.3L能加92,5米大7座

探险者固然是一款很好的车型,特别是昆仑巅峰版还带后桥限滑差速器,脱困能力更强。但探险者的价格终归比较高,退而求其次也可以看看全新换代的锐界L,整体实力也不差。新车指导价为22.98-30.98万元,目前终端优惠8800元左右。??优缺点速览优点:1、提供纯燃油和油电混动2、提…...

XV7011BB可为智能割草机的导航系统提供新的解决方案

智能割草机作为现代家庭和商业草坪维护保养的重要工具&#xff0c;其精确的定位和导航系统对于提高机器工作效率和确保安全运行至关重要。在智能割草机的发展历程中&#xff0c;定位和导航技术一直是关键的创新点。 传统的基于RTK(实时动态差分定位技术)技术的割草机虽然在…...

随笔(二)——项目代码优化

文章目录 前言一、传入的props的默认值定义为空数组1.问题&#xff08;提示对象的类型为unknwn&#xff09;2.优化 二、document 上不存在xxx属性1.问题2.做了一个兼容浏览器的关闭全屏方法3. 解决方法 &#xff08;使用declare globa设置全局变量类型&#xff09;&#xff08;…...

什么是erp仓储管理系统?ERP系统的价值体现在哪些方面?

ERP仓储管理系统是一个帮助企业管理仓库的工具。想象一下&#xff0c;如果你是一个仓库管理员&#xff0c;里面堆满了各种各样的产品和货物&#xff0c;如何确保这些产品数量准确、摆放有序&#xff0c;以及快速找到自己需要的产品呢&#xff1f; 这时&#xff0c;如果企业引用…...