C++核心编程<类和对象>(4)

news2024/9/24 5:31:41

C++核心编程<类和对象>

    • 4.类和对象
      • 4.1封装
        • 4.1.1封装的意义
          • 封装的意义1
          • 封装的意义2
        • 4.1.2struct和class区别
        • 4.1.3成员属性设置为私有
      • 4.2对象的初始化和清理
        • 4.2.1构造函数和析构函数
          • 1.1构造函数语法:类名(){}
          • 1.2析构函数语法: ~类名(){}
        • 4.2.2构造函数的分类及调用
          • 2.1构造函数的分类
          • 2.2构造函数的调用
        • 4.2.3拷贝构造函数调用时机
          • 遗留问题(待解决)
        • 4.2.4构造函数调用规则
        • 4.2.5深拷贝与浅拷贝
        • 4.2.6初始化列表
        • 4.2.7类对象作为类成员
        • 4.2.8静态成员
          • 8.1静态成员变量(有访问权限)
          • 8.2静态成员函数(都有访问权限)
      • 4.3C++对象模型和this指针
        • 4.3.1成员变量和成员函数分开存储
        • 4.3.2this指针概念
        • 4.3.3空指针访问成员函数
        • 4.3.4const修饰成员函数
          • 4.1常函数
          • 4.2常对象
      • 4.4友元
        • 4.4.1友元的三种实现
          • 1.1全局函数做友元
          • 1.2类做友元
          • 1.3成员函数做友元
      • 4.5运算符重载
        • 4.5.1加号运算符重载
        • 4.5.2左移运算符重载
        • 4.5.3递增运算符重载
        • 4.5.4赋值运算符重载
        • 4.5.5关系运算符重载
        • 4.5.6函数调用运算符重载
      • 4.6继承
        • 4.6.1继承的基本语法
        • 4.6.2继承方式
          • 2.1继承方式共有三种
            • 1.1公共继承
            • 1.2保护继承
            • 1.3私有继承
        • 4.6.3继承中的对象模型
          • 3.1打开开发人员命令提示工具查看对象模型
        • 4.6.4继承中的构造和析构顺序
        • 4.6.5继承同名成员处理方式
        • 4.6.6继承同名静态成员处理方式
        • 4.6.7多继承语法
        • 4.6.8菱形继承
          • 8.1菱形继承概念
      • 4.7多态
        • 4.7.1多态的基本概念
          • 1.1多态分为两类
          • 1.2静态多态和动态多态
          • 1.3动态多态满足条件
          • 1.4动态多态使用
        • 4.7.2纯虚函数和抽象函数
          • 2.1抽象类特点
        • 4.7.3虚析构和纯虚析构
          • 3.1虚析构和纯虚析构共性
          • 3.2虚析构和纯虚析构区别
          • 3.3语法

4.类和对象

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

4.1封装

4.1.1封装的意义

封装的意义1
  • 设计类的时候属性行为写在一起,表现事物
  • 语法
class 类名{访问权限: 属性 / 行为}
  • 案例
#include<iostream>
using namespace std;


const double PI = 3.14;
class Circle {
	// 访问权限
public:
	// 属性
	double m_r;
	// 行为
	double calculateZC() {
		return 2 * PI * m_r;
	}
};



int main() {

	// 实例化
	// 通过圆类  创建具体的圆(对象)
	Circle cl;
	// 给圆对象的属性进行赋值
	cl.m_r = 5;
	cout << "圆的周长为:" << cl.calculateZC() << endl;


	system("pause");
	return 0;
}
封装的意义2
  • 访问权限有三种
    • public 公共权限
      • 成员 类内可以访问 类外可以访问
    • protected 保护权限
      • 成员 类内可以访问 类外不可以访问 继承中子类可以访问父类
    • private 私有权限
      • 成员 类内可以访问 类外不可以访问 继承中子类不可以访问父类

4.1.2struct和class区别

  • 在C++中struct和class唯一的区别就在于默认的访问权限不同
  • 区别:
    • struct 默认权限为公共
    • class 默认权限为私有
#include<iostream>
using namespace std;
// struct 和 class区别
// struct  默认权限为公共
// class  默认权限为私有
class Circle {
	double PI = 3.14;
	double c_r = 0;
	void setR(int r = 5) {
		c_r = r;
	}
	void circleArea() {
		cout << "圆的面积为:" << PI * c_r * c_r << endl;
	}
};

struct Person {
	int pAge = 15;
	string pName = "张三";
	void setName(string name) {
		pName = name;
	}
	void showInfo(){
		cout << "学生年龄:" << pAge << endl;
		cout << "学生姓名:" << pName << endl;


	}
};


int main() {

	Circle cir;
	// 默认私有权限
	// cir.setR(10);
	Person per;
	per.setName("李四");
	per.showInfo(); 
	per.pAge = 18;
	per.showInfo();


	system("pause");
	return 0;
}

4.1.3成员属性设置为私有

  • 优点
    • 将所有成员属性设置为私有,可以自己控制读写权限
    • 对于写权限,可以检测数据的有效性
  • 案例
#include<iostream>
#include<string>
using namespace std;
// 成员属性设置为私有
// 1.可以自己控制读写权限
// 2.对于写可以检测数据的有效性

class Person {
public:
	// 编辑姓名
	void setName(string name) {
		m_Name = name;
	}

	//获取姓名
	string getName() {
		return m_Name;
	}
	//设置年龄  (设置数据的有效性,默认为0)
	void setAge(int age) {
		if (age < 0 || age >150) {
			m_Age = 0;
			cout << "设置年龄有误" << endl;
			return;
		}
		m_Age = age;
	}

	//获取年龄
	int getAge() {
		return m_Age;
	}
	//设置爱好
	void setHobby(string hobbies) {
		m_Hobbies = hobbies;
	}
private:
	//姓名    rw
	string m_Name;
	//年龄 rw
	int m_Age;
	//爱好 w
	string m_Hobbies;
};





int main() {
	//struct 和 class 区别
	// struct 默认权限是 公用 public
	// class 默认权限是 私有  private


	Person person;
	person.setName("yinbb是真的大佬");
	cout << "原神大佬:" << person.getName() << endl;
	person.setAge(120);
	cout << "年龄:" << person.getAge() << endl; // 120
	person.setHobby("篮球");
	// 不能获取爱好
	// cout << "爱好:" << person.getHobby() << endl;
	
	system("pause");
	return 0;
}

4.2对象的初始化和清理

4.2.1构造函数和析构函数

  • 对象的初始化清理也是两个非常重要的安全问题
    • 一个对象或者变量没有初始状态,对其使用后果是未知的
    • 同样的使用完一个对象变量没有及时清理,也会造成一定的安全问题
  • c++利用了构造函数析构函数解决上述问题:
    • 构造函数: 主要作用在于创建对象时为对象的成员属性赋值构造函数由编译器自动调用,无须手动调用
    • 析构函数: 主要是作用在于对象销毁前系统自动调用,执行一些清理工作
1.1构造函数语法:类名(){}
  • 构造函数,没有返回值也不写void
  • 函数名称类名相同
  • 构造函数可以有参数,因此可以发生重载
  • 程序在调用对象时候会自动调用构造,无需手动调用,而且只会调用一次
1.2析构函数语法: ~类名(){}
  • 析构函数,没有返回值也不写void
  • 函数名称类名相同,在名称前加上符号~
  • 析构函数不可以有参数,因此不可以发生重载
  • 程序在对象销毁前会自动调用析构无需手动调用,而且只会调用一次
#include<iostream>
using namespace std;

//对象的初始化和清理

class Person {
public:
	//构造函数
	Person() {
		cout << "构造函数调用" << endl;
	}
	//析构函数
	~Person() {
		cout << "析构函数调用" << endl;
	}
};

// 在栈上的数据,test()执行完毕后,释放这个对象

void test() {
	Person p;
}


int main() {
	test();// 构造函数调用      析构函数调用

	//在main函数执行完毕,才会调用析构函数
	Person p;// 构造函数调用

	system("pause");
	return 0;
}

4.2.2构造函数的分类及调用

2.1构造函数的分类
  • 两种分类方式
    • 按参数分为: 有参构造无参构造
    • 按类型分为:普通构造拷贝构造
2.2构造函数的调用
  • 三种调用方式
    • 括号法
    • 显示法
    • 隐式转换法
#include<iostream>
using namespace std;

// 构造函数的分类及调用
class Person {
public:
	// 无参构造
	Person() {
		cout << "构造函数的调用(默认)" << endl;
	}
	// 有参构造
	Person(int num) {
		age = num;
		cout << "构造函数的调用(有参)" << endl;
	}
	// 拷贝构造函数
	Person(const Person &person) {
		age = person.age;
		cout << "构造函数的调用(拷贝)" << endl;
	}

	// 析构函数
	~Person() {
		cout << "析构函数调用" << endl;
	}
private:
	int age;
};

// 调用
void test(){
	// 1.括号法
	// 调用无参构造
	Person p1;
	// 调用有参构造
	Person p2(21);
	// 调用拷贝构造函数
	Person p3(p2);
	// 注意事项
	// 调用默认构造函数时候,不要添加括号;编译器会认为是一个函数声明,不认为在创建对象

	
	// 2.显示法
	Person p11;
	Person p22 = Person(20);
	Person p33 = Person(p22);
	// Person(20);//匿名对象,特点: 当前行执行结束后,系统会立即回收匿名对象
	// 注意事项2
	// 不要利用拷贝构造函数  初始化匿名对象  编译器会认为Person (p3) === Person p3;对象声明

	// 3.隐式法
	Person p4 = 10;//相当于 写了  Person p4 = Person(10); 有参构造
	Person p5 = p4;

}

int main() {

	test();
	system("pause");
	return 0;
}

4.2.3拷贝构造函数调用时机

  • C++中拷贝构造函数调用时机通常有三种情况
    • 使用一个已经创建完毕的对象来初始化一个新对象
    • 值传递的方式给函数参数传值
    • 以值方式返回局部对象
#include<iostream>
using namespace std;


class Person {
public:
	Person() {
		cout << "Person默认构造函数的调用" << endl;
	}
	Person(int age) {
		m_Age = age;
		cout << "Person有参构造函数的调用" << endl;
	}
	Person(const Person& person) {
		m_Age = person.m_Age;
		cout << "Person拷贝构造函数的调用" << endl;
	}
	~Person() {
		cout << "Person析构函数的调用" << endl;
	}
	int m_Age;
};

// 1.使用一个已经创建完毕的对象来初始化一个新对象
void test1() {
	Person p1(20);
	Person p2(p1);
	cout << "P2的年龄: " << p2.m_Age << endl;
}
// 2.值传递的方式给函数参数传值
Person doWork(Person p) {
	p.m_Age = 25;
	return p;
}
void test2() {
	Person p(12);
	doWork(p);
	cout << "p的年龄:" << p.m_Age << endl;//12
	cout << "拷贝构造调用之后对值的修改:" << (doWork(p)).m_Age << endl; //25
}

// 3.值方式返回局部对象
Person doWork2() {
	Person p1;
	return p1;

}
void test3() {
	Person p3 = doWork2();
}

int main() {

	// test1();
	// test2();
	test3();
	system("pause");
	return 0;
}
遗留问题(待解决)
  • 以值方式返回局部对象,拷贝函数未执行(不符合) 显示:由于G++优化导致,RVO有相关技术详情

4.2.4构造函数调用规则

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

    • 默认构造函数(无参,函数体为空)
    • 默认析构函数(无参,函数体为空)
    • 默认拷贝构造函数,对属性进行值拷贝
  • 构造函数的调用规则如下

    • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
    • 如果用户定义拷贝构造函数,c++不会提供其他普通构造函数
#include<iostream>
using namespace std;
// 构造函数的调用规则

class Person {
public:
	 Person() {
		 cout << "Person的默认构造函数调用" << endl;
	}
	 Person(int age) {
		 cout << "Person的有参构造函数调用" << endl;
		 m_Age = age;
	 }
	 Person(const Person &p) {
		 m_Age = p.m_Age;
		 cout << "person的拷贝构造函数调用" << endl;
	 }
	 ~Person() {
		 cout << "person的析构函数调用" << endl;
	 }
	 int m_Age;
};

void test1() {
	Person p;
	p.m_Age = 18;


	/*
		如果不自定义拷贝函数,编译器也会提供默认的拷贝函数,进行值拷贝   p2.m_Age 依旧为 18
	*/
	Person p2(p);
	cout << "P2的年龄是:" << p2.m_Age << endl; //18
}

void test2() {
	Person p(28);
	Person p2(p);
	cout << "p2的年龄:" << p2.m_Age << endl;// 28
}

void test3() {
	Person p;
}

int main() {


	// test1();
	// test2();
	// 如果只提供拷贝函数,其他普通函数不提供
	test3();// error
	system("pause");
	return 0;
}

4.2.5深拷贝与浅拷贝

  • 深浅拷贝的区别
    • 浅拷贝简单的赋值拷贝操作
    • 深拷贝在堆区重新申请空间,进行拷贝操作
#include<iostream>
using namespace std;
class Person {
public:
	Person() {
		cout << "默认构造函数的调用" << endl;
	}
	Person(int age,int height) {
		cout << "有参构造函数的调用" << endl;
		m_Age = age;
		m_Height = new int(height);
	}
	~Person() {
		// 析构代码,将堆区开辟数据做释放操作
		if (m_Height != NULL) {
			delete m_Height;
			// 防止野指针
			m_Height = NULL;
		}
		cout << "析构函数的调用" << endl;
	}

	//解决拷贝函数的问题(浅拷贝)
	Person(const Person &p) {
		cout << "Person拷贝构造函数调用" << endl;
		m_Age = p.m_Age;

		// m_Height = p.m_Height; 编译器默认实现就是这行代码
		// 深拷贝
		m_Height = new int(*p.m_Height);
	}



	int m_Age;
	int* m_Height;

};

/*
* 栈区先进后出
* p2释放执行后,p1再去释放(非法操作)
* 浅拷贝带来的问题就是堆区内存重复释放
*/
void test1() {
	Person p1(18,180);
	cout << "p1的年龄:" <<p1.m_Age<<"身高为:" <<*p1.m_Height<< endl; // 18   180

	Person p2(p1);
	cout << "p1的年龄:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;// 18  180

}

int main() {

	test1();
	system("pause");
	return 0;
}

4.2.6初始化列表

  • 作用
    • 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;
	}*/
	// 初始化列表初始化属性
	Person(int a,int b,int c) : m_A(a), m_B(b), m_C(c) {

	}
	int m_A;
	int m_B;
	int m_C;
};

void test1() {
	//Person p(1, 2, 30);
	Person p(10,50,100);
	cout << p.m_A << endl;
	cout << p.m_B << endl;
	cout << p.m_C << endl;
}
int main() {
	test1();
	system("pause");
	return 0;
}

4.2.7类对象作为类成员

  • C++类中的成员可以是另一个类的对象,我们称为该成员为 对象成员
#include<iostream>
#include<string>
using namespace std;
class Phone {
public:
	Phone(string pName,float pPrice): p_Name(pName),p_Price(pPrice) {
		cout << "手机的构造函数调用" << endl;
	}
	~Phone() {
		cout << "手机的析构函数调用" << endl;
	}
	// 手机品牌
	string p_Name;
	// 手机价格
	float p_Price;
};
class Person {
public:
	Person(string name,Phone phone): m_Name(name),m_Phone(phone) {
		cout << "人的构造函数调用" << endl;
	}
	~Person() {
		cout << "人的析构函数调用" << endl;
	}
	//姓名
	string m_Name;
	//物品
	Phone m_Phone;
};

// 当其他类对象作为本类成员,构造时候先构造类对象,再构造自身;析构的顺序相反
void test1() {
	Phone ph("华为", 7999);
	Person p("张三", ph);
}
int main() {
	test1();
	system("pause");
	return 0;
}

4.2.8静态成员

  • 静态成员就是成员变量和成员函数前加上关键字static,称为静态成员
8.1静态成员变量(有访问权限)
  • 所有对象共享同一份数据
  • 编译阶段分配内存(全局区)
  • 类内声明,类外初始化
#include<iostream>
using namespace std;

// 静态成员变量
class Person {
public:
	static int m_A;
};
int Person::m_A = 100;
void test1() {
	Person p;
	cout << p.m_A << endl; // 100

	Person p2;
	p2.m_A = 200;
	cout << p.m_A << endl;// 200

}
void test2() {
	//静态成员变量  不属于某一个对象上,所有对象共享同一份数据
	//1.通过对象进行访问
	Person p;
	cout << p.m_A << endl;
	//2.通过类名进行访问
	cout << Person::m_A << endl;
}
int main() {
	//test1();
	test2();
	system("pause");
	return 0;
}
8.2静态成员函数(都有访问权限)
  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量
#include<iostream>
using namespace std;

class Person {
public:
	static void func() {

		m_A = 200;// 静态成员函数可以访问静态成员变量
		// m_B = 300; // 静态成员函数不能访问非静态成员变量  无法区分那个对象的属性
		cout << "static void func的调用" << endl;
	}
	static int m_A;
	int m_B;
};
int Person::m_A = 100;
void test1() {
	//1.通过对象访问
	Person p;
	p.func();

	//2.通过类名访问
	Person::func();
}

int main() {
	test1();
	system("pause");
	return 0;
}

4.3C++对象模型和this指针

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

  • 只有非静态成员变量才属于类的对象上
#include<iostream>
using namespace std;
class Person {
	int m_A;// 非静态成员变量  属于类的对象上

	static int  m_B;// 静态成员变量 不属于类对象上

	void func() {} // 非静态成员函数 不属于类对象上

	static void func2(){}// 静态成员函数 不属于类对象上
};
int Person::m_B = 100;
void test1() {
	Person p;
	// c++编译器会给每个对象分配一个字节空间,是为了区分空对象占内存的位置
	// 每个对象也应该有一个独一无二的内存地址
	cout << "size of p = " << sizeof(p) << endl;// 4
}
int main() {

	test1();
	system("pause");
	return 0;

}

4.3.2this指针概念

  • this指针指向被调用的成员函数所属的对象
  • this指针的用途
    • 形参成员变量同名时,可以用this指针来区分
    • 在类的非静态成员函数中返回对象本身,可使用return *this
#include<iostream>
using namespace std;

class Person {
public:
	Person(int age) {
		// this指针指向 被调用的成员函数 所属的对象
		this->age = age;
	}
	// 返回Person值对象,会默认调用拷贝构造函数,复制一份当前所调对象
	Person& PersonAddAge(Person &p) {
		this->age += p.age;
		return *this;
	}
	int age;
};

// 解决名称冲突
void test1() {
	Person p(18);
	cout << "年龄大小:" << p.age << endl;// 18
}
// 返回对象本身用*this
void test2() {
	Person p1(10);
	Person p2(20);
	p2.PersonAddAge(p1).PersonAddAge(p1);
	cout << "年龄:" << p2.age << endl;// 40
	
}

int main() {
	//test1(); 
	test2();
	system("pause");
	return 0;
}

4.3.3空指针访问成员函数

  • C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针,如果用到this指针,需要加以判断保证代码的健壮性
#include<iostream>
using namespace std;
// 空指针调用成员函数
class Person
{
public:
	void showClassName() {
		cout << "this is Person class" << endl;
	}
	// m_Age  默认  this->m_Age
	// 报错原因 传入的指针为NULL
	void showPersonAge() {
		// 提高健壮性
		if (this == NULL) {
			return;
		}
		cout << "age=" << m_Age << endl;
	}
	int m_Age;
};

void test01() {
	// 空指针
	Person* p = NULL;
	p->showClassName();
	p->showPersonAge();


}

int  main() {
	test01();
	system("pause");
	return 0 ;
}

4.3.4const修饰成员函数

4.1常函数
  • 成员函数后加const后,我们称为这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明是加关键词mutable后,在常函数总依然可以修改
4.2常对象
  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数
#include<iostream>
using namespace std;
// 常函数
class Person 
{
public:
	// this指针的本质  是指针常量  指针的指向是不可以修改的
	// const Person * const this;
	// 在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
	void showPerson() const
	{
		this->m_B = 120;
		// this->m_A = 100;
		// this = NULL; //this指针不可以修改指针指向的
	}
	void showNum() {
		m_A = 130;
	}

	int m_A;
	mutable int m_B;// 特殊变量,即使在常函数中可以修改,加mutable关键词
};


void test1() {
	Person p;
	p.showPerson();
}
void test2() {
	const Person p2;// 在对象前面加上const,变为常对象
	//p2.m_A = 100;
	// m_B是特殊值,在常对象下也可以修改
	p2.m_B = 120;
	p2.showPerson();
	// 常对象,不可以调用普通成员函数,因为普通成员函数可以修改属性
	// p2.showNum();
}

int main() {
	test2();
	system("pause");
	return 0;
}

4.4友元

  • 在程序中,有些私有属性 也想让类外的特殊的一些函数或类进行访问,就需要用到友元的技术
  • 友元的关键词为friend

4.4.1友元的三种实现

1.1全局函数做友元
#include<iostream>
#include<string>
using namespace std;

class Building
{
	//GoodF全局函数是Building的友元,可以访问Building中的私有成员
	friend void goodF(Building* building);
public:
	string m_SittingRom;
	Building() {
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
private:
	string m_BedRoom;
};
// 全局函数
void goodF(Building* building) 
{
	cout << "全局函数被调用,当前访问:"<< building->m_SittingRoom << endl;

	// 添加了friend关键词,可以访问私有成员
	cout << "全局函数被调用,当前访问:" << building->m_BedRoom << endl;
}

void test1() {
	Building building;
	goodF(&building);
}

int main() {

	test1();
	system("pause");
	return 0;
}
1.2类做友元
#include<iostream>
#include<string>
using namespace std;
// 类做友元
class Building;
class GoodFri
{
public:
	GoodFri();
	void visit();
	Building* building;
};
class Building
{
	friend class GoodFri;
public:
	Building();
public:
	string m_SittingRoom;
private:
	string m_BedRoom;
};

//类外写成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}
GoodFri::GoodFri()
{
	// 创建建筑物对象
	building = new Building;
}
void GoodFri::visit()
{
	cout << "正在访问:" << building->m_SittingRoom << endl;

	cout << "正在访问:" << building->m_BedRoom << endl;

}

void test1() {
	GoodFri gf;
	gf.visit();
}

int main() {

	test1();
	system("pause");
	return 0;
}
1.3成员函数做友元
#include<iostream>
#include<string>
using namespace std;
class Building;
class GoodFri;

class GoodFri
{
public:
	GoodFri();
	void visit();// visit()函数能访问Building中的私有成员
	void visit2();// visit2()函数不能访问Building中的私有成员
	Building *building;
};
class Building
{
	friend void GoodFri::visit();
public:
	Building();
public:
	string m_SittingRoom;
private:
	string m_BedRoom;

};

Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

GoodFri::GoodFri()
{
	building = new Building;
}

void GoodFri::visit()
{
	cout << "当前访问的位置:" << building->m_SittingRoom << endl; //客厅

	cout << "当前访问的位置:" << building->m_BedRoom << endl;// 卧室
}
void GoodFri::visit2()
{
	cout << "当前访问的位置:" << building->m_SittingRoom << endl;// 客厅

	//cout << "当前访问的位置:" << building->m_BedRoom << endl;// error
}

void test1() {
	GoodFri gf;
	gf.visit();
	gf.visit2();
}
int main() {

	test1();
	system("pause");
	return 0;
}

4.5运算符重载

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

4.5.1加号运算符重载

#include<iostream>
using namespace std;

//加号运算符重载
class Person 
{
public:
	//成员函数重载+号
	/*
	* 成员函数重载的本质调用
	* Person p3 = p1.operator+(p2);
	*/
	//Person operator+(Person &p);
	int m_A;
	int m_B;
};

//Person Person::operator+(Person& p) {
//	Person temp;
//	temp.m_A = this->m_A + p.m_A;
//	temp.m_B = this->m_B + p.m_B;
//	return temp;
//}


//全局函数重载+号
/*
* 全局函数重载本质调用
* Person p3 = operator+(p1,p2)
*/
Person operator+(Person& p1,Person &p2) {
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}

void test1()
{
	Person p1;
	p1.m_A = 20;
	p1.m_B = 30;

	Person p2;
	p2.m_A = 40;
	p2.m_B = 20;

	Person p3 = p1 + p2;
	cout << "m_A=" << p3.m_A << endl;
	cout << "m_B=" << p3.m_B << endl;

}
int main() {
	test1();
	system("pause");
	return 0;
}

4.5.2左移运算符重载

  • 直接打印对象的属性
#include<iostream>
using namespace std;
class Person
{
	friend ostream& operator<<(ostream& cout, Person& p);
public:
	Person(int a,int b) 
	{
		m_A = a;
		m_B = b;
	}
private:
	//成员函数重载  左移运算符
	/*
	* 不会利用成员函数重载<<运算符,因为无法实现  cout在左侧	* 
	 */
	//void operator<<() {};
	int m_A;
	int m_B;
};

//全局函数重载左移运算符
ostream& operator<<(ostream &cout, Person &p) 
{
	cout << "m_A = " << p.m_A << " m_B = " << p.m_B << endl;
	return cout;
}

void test1() {
	Person p(10,12);
	cout << p << endl;
}

int main() {
	test1();
	system("pause");
	return 0;
}

4.5.3递增运算符重载

  • 通过重载递增运算符,实现自己的整型数据
#include<iostream>
using namespace std;
class MyInteger
{
	friend ostream& operator<<(ostream& cout, MyInteger myInt);
public:
	MyInteger() 
	{
		m_NUM = 0;
	}
	//重载前置++运算符
	MyInteger& operator++();
	//重置后置++运算符  int代表占位参数,可以用于区分前置和后置递增
	MyInteger operator++(int);
private:
	int m_NUM;
};
MyInteger& MyInteger::operator++()
{
	m_NUM++;
	return *this;
}
MyInteger  MyInteger::operator++(int) {
	// 先 记录当时结果 
	MyInteger temp = *this;
	// 后 递增
	m_NUM++;
	// 最后将记录结果做返回
	return temp;
}

//重载<<运算符
ostream& operator<<(ostream& cout, MyInteger myInt)
{
	cout << myInt.m_NUM;
	return cout;
}


void test1() {
	MyInteger myInteger;
	cout << ++myInteger << endl;
	cout << myInteger << endl;
}
void test2() {
	MyInteger myInteger;
	cout << myInteger++ << endl;
	cout << myInteger << endl;
}
int main() {
	//test1();
	test2();
	system("pause");
	return 0;
}

4.5.4赋值运算符重载

  • c++编译器至少给一个类添加4个函数
    • 默认构造函数(无参,函数体为空)
    • 默认析构函数(无参,函数体为空)
    • 默认拷贝函数,属性进行值拷贝
    • 赋值运算符operator=,对属性进行值拷贝
#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);
	int* m_Age;
};
Person& Person::operator=(Person& p)
{
	//先判断是否有属性在堆区,如果有先释放,在做深拷贝
	if (m_Age != NULL) 
	{
		delete m_Age;
		m_Age = NULL;
	}
	//深拷贝
	m_Age = new int(*p.m_Age);

	return *this;
}
void test1()
{
	Person p1(18);
	cout << "年龄:" << *p1.m_Age << endl;
	Person p2(20);
	cout << "年龄: " << *p2.m_Age << endl;
	p2 = p1;
	cout << "年龄: " << *p2.m_Age << endl;
}

int main()
{
	test1();
	system("pause");
	return 0;

}

4.5.5关系运算符重载

  • 重载关系运算符,可以自定义类型对象进行对比操作
#include<iostream>
using namespace std;
class Person;


class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	bool operator==(Person &p);
	string m_Name;
	int m_Age;
};

bool Person::operator==(Person& p)
{
	if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {
		return true;
	}
	return false;
}

void test1() {
	Person p1("Tom", 20);
	Person p2("Tom", 23);
	if (p1 == p2) {
		cout << "p1和p2是相等的" << endl;
	}
	else {
		cout << "p1和p2是不相等的" << endl;
	}

}

int main() {
	test1();
	system("pause");
	return 0;
}

4.5.6函数调用运算符重载

  • 函数调用运算符()也可以重载
  • 由于重载后使用的方式非常像函数的使用,因此称为仿函数
  • 仿函数没有固定写法,非常灵活
    #include<iostream>
    #include<string>
    using namespace std;
    
    class MyPoint
    {
    public:
       //重载函数调用运算符
       void operator()(string str);
    };
    void MyPoint::operator()(string str) 
    {
       cout << "重载函数调用运算符===>"<<str << endl;
    }
    
    void test1() 
    {
       MyPoint myPoint;
       // 使用起来非常类似于函数调用,因此称为仿函数
       myPoint("hello world");
    }
    int main()
    {
    
       test1();
       system("pause");
       return 0;
    }
    

4.6继承

4.6.1继承的基本语法

  • 继承的好处
    • 减少重复代码
  • 语法
class 子类 : 继承方式 父类
#include<iostream>
using namespace std;

 Java页面
//class Java
//{
//public:
//	void header();
//	void footer();
//	void left();
//	void content();
//};
//
//void Java::header()
//{
//	cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//}
//void Java::footer()
//{
//	cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
//}
//void Java::left()
//{
//	cout << "JAVA、Python、C++...(公共分类列表)" << endl;
//}
//void Java::content()
//{
//	cout << "JAVA学科的视频" << endl;
//}
//
Python页面
//class Python
//{
//public:
//	void header();
//	void footer();
//	void left();
//	void content();
//};
//
//void Python::header()
//{
//	cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//}
//void Python::footer()
//{
//	cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
//}
//void Python::left()
//{
//	cout << "JAVA、Python、C++...(公共分类列表)" << endl;
//}
//void Python::content()
//{
//	cout << "Python学科的视频" << endl;
//}
//
CPP页面
//
Python页面
//class CPP
//{
//public:
//	void header();
//	void footer();
//	void left();
//	void content();
//};
//
//void CPP::header()
//{
//	cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//}
//void CPP::footer()
//{
//	cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
//}
//void CPP::left()
//{
//	cout << "JAVA、Python、C++...(公共分类列表)" << endl;
//}
//void CPP::content()
//{
//	cout << "C++学科的视频" << endl;
//}

//公共部分
class BasePage
{
public:
	void header();
	void footer();
	void left();
};
void BasePage::header()
{
	cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
}
void BasePage::left()
{
	cout << "JAVA、Python、C++...(公共分类列表)" << endl;
}
void BasePage::footer()
{
	cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
}

//JAVA页面
class Java : public BasePage
{
public:
	void content();
};
void Java::content()
{
	cout << "Java视频下载页面" << endl;
}

//Python页面
class Python : public BasePage
{
public:
	void content();
};
void Python::content()
{
	cout << "Python视频下载页面" << endl;
}

//C++页面
class CPP : public BasePage
{
public:
	void content();
};
void CPP::content()
{
	cout << "C++视频下载页面" << endl;
}

void test1() {
	cout << "JAVA下载视频页面如下" << endl;
	Java ja;
	ja.header();
	ja.left();
	ja.content();
	ja.footer();

	cout << "---------------------------------" << endl;
	cout << "Python下载视频页面如下" << endl;
	Python py;
	py.header();
	py.left();
	py.content();
	py.footer();

	cout << "---------------------------------" << endl;
	cout << "C++下载视频页面如下" << endl;
	CPP cpp;
	cpp.header();
	cpp.left();
	cpp.content();
	cpp.footer();
}

int main() 
{

	test1();
	system("pause");
	return 0;
}

4.6.2继承方式

2.1继承方式共有三种

在这里插入图片描述

1.1公共继承
#include<iostream>
using namespace std;
//继承方式----公共继承
class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son1 : public Base1
{
public:
	void func();
};
void Son1::func() 
{
	//父类中的公共权限成员  到子类中依然是公共权限
	m_A = 10;
	//父类中的保护权限成员  到子类依然是保护权限
	m_B = 20;
	//父类中的私有权限成员  子类访问不了
	//m_C = 30;
}

void test1()
{
	Son1 s1;
	s1.m_A = 12;
	//保护权限成员访问不到   类外访问不到
	// s1.m_B = 13;

}
int main()
{
	test1();
	system("pause");
	return 0;
}
1.2保护继承
#include<iostream>
using namespace std;
//继承方式----公共继承
class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son1 : protected Base1
{
public:
	void func();
};
void Son1::func()
{
	//父类中的公共权限成员  到子类中是保护权限
	m_A = 10;
	//父类中的保护权限成员  到子类依然是保护权限
	m_B = 20;
	//父类中的私有权限成员  子类访问不了
	//m_C = 30;
}

void test1()
{
	Son1 s1;
	//保护权限成员访问不到   类外访问不到
	//s1.m_A = 12;
	//保护权限成员访问不到   类外访问不到
	// s1.m_B = 13;

}
int main()
{
	test1();
	system("pause");
	return 0;
}
1.3私有继承
#include<iostream>
using namespace std;
//继承方式----公共继承
class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son1 : private Base1
{
public:
	void func();
};
void Son1::func()
{
	//父类中的公共权限成员  到子类中是私有权限
	m_A = 10;
	//父类中的保护权限成员  到子类中是私有权限
	m_B = 20;
	//父类中的私有权限成员  子类访问不了
	//m_C = 30;
}

void test1()
{
	Son1 s1;
	//私有权限成员访问不到   类外访问不到
	//s1.m_A = 12;
	//私有权限成员访问不到   类外访问不到
	// s1.m_B = 13;

}
int main()
{
	test1();
	system("pause");
	return 0;
}

4.6.3继承中的对象模型

  • 父类中所有非静态成员属性都会被子类继承下去
  • 父类中私有成员属性 是被编译器给隐藏了 因此访问不到,但是确实被继承了
#include<iostream>
using namespace std;

class Person
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;

};
class Son :public Person
{
public:
	int m_D;
};

void test1() {
	/*
	* 父类中所有非静态成员属性都会被子类继承下去
	* 父类中私有成员属性  是编译器给隐藏了 因此访问不到,但是确实被继承了
	*/
	cout << "sizeof = " << sizeof(Son) << endl;// 16

}

int main()
{
	test1();
	system("pause");
	return 0;
}
3.1打开开发人员命令提示工具查看对象模型
  • 找到文件所在位置(可用dir查看一下)
  • 查看命令
cl /d1 reportSingleClassLayout类名 "文件名"

4.6.4继承中的构造和析构顺序

  • 子类继承父类后,当创建子类对象,也会调用父类的构造函数
  • 顺序
    • 先构造父类,再构造子类
    • 先子类析构,再父类析构
#include<iostream>
using namespace std;
class Person
{
public:
	Person() 
	{
		cout << "调用父类的构造函数" << endl;
	}
	~Person() 
	{
		cout << "调用父类的析构函数" << endl;
	}
};

class Son : public Person
{
public:
	Son()
	{
		cout << "调用子类的构造函数" << endl;
	}
	~Son()
	{
		cout << "调用子类的析构函数" << endl;
	}
};

void test1()
{
	/*
	调用父类的构造函数
	调用父类的析构函数
	*/
	// Person p;

	/*
	调用父类的构造函数
	调用子类的构造函数
	调用子类的析构函数
	调用父类的析构函数
	
	*/
	Son son;
}


int main() {

	test1();
	system("pause");
	return 0;
}

4.6.5继承同名成员处理方式

  • 访问子类同名成员 直接访问
  • 访问父类同名成员 需要加作用域
  • 子类中出现和父类同名的成员函数,子类的同名成员会隐藏父类中所有的同名成员函数
#include<iostream>
using namespace std;
class Base
{
public:
	Base()
	{
		m_A = 100;
	}
	void func()
	{
		cout << "Base of func的函数" << endl;
	}
	void func(int a)
	{
		cout << "Base of func(int a)的函数" << endl;
	}
	int m_A;
};
class Son : public Base
{
public:
	Son() 
	{
		m_A = 112;
	}
	void func()
	{
		cout << "Son of func的函数" << endl;
	}
	
	int m_A;
};
// 同名成员属性
void test1()
{
	Son son;
	cout << "Son of m_A = " << son.m_A << endl; // 112
	// 如果通过子类对象访问父类同名成员,需要加作用域
	cout << "Base of m_A = " << son.Base::m_A << endl; // 100


}
// 同名成员函数
void test2()
{
	Son son;
	son.func(); //直接调用  调用是子类的func函数
	son.Base::func();//调用父类的func函数
	//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏父类中所有的同名成员函数
	// son.func(120);
	son.Base::func(12);
}


int main()
{
	// test1();
	test2();
	system("pause");
	return 0;
}

4.6.6继承同名静态成员处理方式

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
  • 子类中出现和父类同名的静态成员函数,子类的同名成员会隐藏父类中所有的同名成员函数
#include<iostream>
using namespace std;
class Base
{
public:
	static void func()
	{
		cout << "Base of func()" << endl;
	}
	static int m_A;

};
int Base::m_A = 100;

class Son : public Base
{
public:
	static void func()
	{
		cout << "Son of func()" << endl;
	}
	static int m_A;
};
int Son::m_A = 120;


// 同名成员属性
void test1()
{
	// 通过对象访问
	Son son;
	cout << "Son of m_A = " << son.m_A << endl;// 120
	cout << "Base of m_A = " << son.Base::m_A << endl; // 100

	// 通过类名访问
	cout << "Son of m_A = " << Son::m_A << endl; //120
	// 第一::代表通过类名方式访问 第二个::代表父类的作用域访问
	cout << "Base of m_A = " << Son::Base::m_A << endl;
}
// 同名成员函数
void test2()
{
	// 通过对象方式访问
	Son son;
	son.func();
	son.Base::func();
	// 通过类名方式访问
	/*
	Son of func()
	Base of func()
	*/
	Son::func();
	Son::Base::func();
}
int main()
{
	// test1();
	test2();
	system("pause");
	return 0;
}

4.6.7多继承语法

  • C++允许一个类继承多个类
  • 语法
class 子类: 继承方式 父类1,继承方式 父类2,...
  • 实际开发环境不建议多继承
#include<iostream>
using namespace std;

class Base1
{
public:
	Base1()
	{
		m_A = 110;
	}
	int m_A;
};

class Base2
{
public:
	Base2()
	{
		m_A = 120;
	}
	int m_A;
};
class Base3
{
public:
	Base3()
	{
		m_A = 130;
	}
	int m_A;
};
class Son :public Base1, public Base2, public Base3
{
public:
	Son() {
		m_A = 200;
	}
	int m_A;
};

void test1()
{
	Son son;
	cout << "Son of m_A = " << son.m_A << endl;// 200
	cout << "Base1 of m_A = " << son.Base1::m_A << endl;// 110
	cout << "Base3 of m_A = " << son.Base3::m_A << endl;// 130
}
int main()
{
	test1();
	system("pause");
	return 0;
}

4.6.8菱形继承

8.1菱形继承概念
  • 两个派生类继承同一个基类
  • 某一个类同时继承两个派生类
  • 这种继承方式称为菱形继承钻石继承
#include<iostream>
using namespace std;
/*
* 菱形继承的问题
*	菱形继承会导致继承两份数据,资源浪费
* 解决方式----虚继承  加上virtual
*/
// 动物类
class Animal
{
public:
	int m_Age;
};
// 羊类
class Sheep :virtual public Animal
{
public:

};
//驼类
class Camel:virtual public Animal
{
public:

};
class Alpaca:public Camel,public Sheep
{
public:

};

void test1()
{
	Alpaca alpaca;
	alpaca.Camel::m_Age = 5;
	alpaca.Sheep::m_Age = 8;
	//cout << "驼的年龄 age = " << alpaca.Camel::m_Age << endl;// 5
	//cout << "羊的年龄 age = " << alpaca.Sheep::m_Age << endl;// 8

	//添加了virtual关键词,实现虚继承
	cout << "驼的年龄 age = " << alpaca.Camel::m_Age << endl;// 8
	cout << "羊的年龄 age = " << alpaca.Sheep::m_Age << endl;// 8
	cout << alpaca.m_Age << endl;// 8
}
int main()
{
	test1();
	system("pause");
	return 0;
}

在这里插入图片描述

4.7多态

4.7.1多态的基本概念

1.1多态分为两类
  • 静态多态
    • 函数重载运算符重载属于静态多态,复用函数名
  • 动态多态
    • 派生类虚函数实现运行时多态
1.2静态多态和动态多态
  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
1.3动态多态满足条件
  • 继承关系
  • 子类重写父类的虚函数(virtual)
1.4动态多态使用
  • 父类的指针引用 指向子类的对象
#include<iostream>
using namespace std;
// 多态
//动物类
class Animal
{
public:
	virtual void speak();
};

// 猫类
class Cat :public Animal
{
public:
	void speak();
};
void Animal::speak()
{
	cout << "动物在讲话" << endl;
}
void Cat::speak()
{
	cout << "喵~喵~喵~~~" << endl;
}
// 地址早绑定  在编译阶段确定函数地址
// 如果想执行猫在说话,那么这个函数地址就不能提前绑定,需要在运行阶段绑定,地址晚绑定
// 可以加virtual,实现晚绑定
void doSpeak(Animal& animal)
{
	animal.speak();
}

void test1()
{
	Cat cat;
	// 未加virtual
	// doSpeak(cat);// 动物在说话
	doSpeak(cat);//喵~喵~喵~~
}
int main()
{
	test1();
	system("pause");
	return 0;
}

在这里插入图片描述
注意点: 上图默认在Cat类重写了speak()函数,如果不重写speak()函数,Cat类的虚拟函数指针指向的虚拟函数表依旧是&Animal::speak

4.7.2纯虚函数和抽象函数

2.1抽象类特点
  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
  • 当类有纯虚函数,这个类也称为抽象类
#include<iostream>
using namespace std;
// 纯虚函数  和  抽象类
class Base
{
public:
	virtual void func() = 0;
};
class Son : public Base
{
public:
	void func() {
		cout << "Son类重写Base的纯虚函数" << endl;
	}
};



void test1() {
	//抽象类不能实例化
	// Base base;
	//new Base;
	// Son son;// 子类必须重写父类中的纯虚函数,否则无法实例化对象
	Base* base = new Son;
	base->func();
}
int main()
{
	test1();
	system("pause");
	return 0;
}

4.7.3虚析构和纯虚析构

  • 多态使用时,如果子类中有属性开辟了堆区,那么父类指针在释放时,无法调用到子类的析构代码
  • 解决方式
    • 将父类中的析构函数改为虚析构纯虚析构
3.1虚析构和纯虚析构共性
  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现
3.2虚析构和纯虚析构区别
  • 如果是纯虚析构,该类属于抽象类无法实例化对象
3.3语法
  • 虚析构
virtual ~类名(){}
  • 纯虚析构
virtual ~类名() = 0;
类名::~类名(){}
#include<iostream>
#include<string>
using namespace std;

class Animal
{
public:
	Animal()
	{
		cout << "Animal构造函数调用" << endl;
	}
	// 利用虚析构可以解决,父类指针释放子类对象释放不干净的问题
	/*virtual ~Animal()
	{
		cout << "Animal析构函数调用" << endl;
	}*/
	// 纯虚析构
	virtual ~Animal() = 0;

	virtual void speak() = 0;
};
Animal::~Animal()
{
	cout << "Animal析构函数调用" << endl;
}

class Cat:public Animal
{
public:
	Cat(string name) {
		cout << "Cat构造函数调用" << endl;
		this->m_Name = new string(name);
	}
	void speak()
	{
		cout <<*m_Name<< "喵~~喵~~~喵~~~~" << endl;
	}
	~Cat()
	{
		if (m_Name != NULL) {
			cout << "Cat的析构函数" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}
	string *m_Name;
};
void test1()
{
	Animal* am = new Cat("Tommy");
	am->speak();
	// 父类指针在析构时候,不会调用子类的析构函数,导致子类如果在堆区属性,出现内存泄露
	delete am;
}

int main()
{
	test1();
	system("pause");
	return 0;
}

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

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

相关文章

【JUC2022】第七章 AQS、ReentrantReadWriteLock 和 StampedLock

【JUC2022】第七章 AQS 文章目录【JUC2022】第七章 AQS一、AQS1.概述2.同步器3.抽象的4.队列式二、ReentrantReadWriteLock1.概述2.案例3.存在的问题三、StampedLock1.概述2.案例3.存在的问题一、AQS 1.概述 AQS(AbstractQueueSynchronizer&#xff0c;抽象的队列式同步器)&am…

tesseract -图像识别

下载链接&#xff1a;https://digi.bib.uni-mannheim.de/tesseract/如下选择最新的版本&#xff0c;这里我选择tesseract-ocr-w64-setup-5.3.0.20221222.exe有如下python模块操作tesseractpyocr 国内源&#xff1a;pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ py…

ThreadLocal 学习常见问题

ThreadLocal 这个此类提供线程局部变量。这些变量不同于通常的对应变量&#xff0c;因为每个访问一个变量的线程(通过 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是希望将状态与线程(例如&#xff0c;用户 ID 或事务 ID)关联的类中的私有静态字段。使…

vue router elementui template CDN模式实现多个页面跳转

文章目录前言一、elementui Tabs标签页和NavMenu 导航菜单是什么&#xff1f;二、使用方式1.代码如下2.页面效果总结前言 写上一篇bloghttps://blog.csdn.net/jianyuwuyi/article/details/128959803的时候因为整个前端都写在一个index.html页面里&#xff0c;为了写更少的代码…

CENTO OS上的网络安全工具(十九)ClickHouse集群部署

一、VMware上集群部署ClickHouse &#xff08;一&#xff09;网络设置 1. 通过修改文件设置网络参数 &#xff08;1&#xff09;CentOS 在CENTOS上的网络安全工具&#xff08;十六&#xff09;容器特色的Linux操作_lhyzws的博客-CSDN博客中我们提到过可以使用更改配置文件的方式…

推荐 7 个 Vue.js 插件,也许你的项目用的上(五)

当我们可以通过使用库轻松实现相同的结果时&#xff0c;为什么还要编写自定义功能&#xff1f;开发人员最好的朋友和救星就是这些第三方库。我相信一个好的项目会利用一些可用的最佳库。Vue.js 是创建用户界面的最佳 JavaScript 框架之一。这篇文章是关于 Vue.js 的优秀库系列的…

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表…

4-3 Linux启动流程

文章目录前言经典启动流程1 按下电源2 开机自检(BIOS)3 MBR引导4 GRUB菜单5 加载内核6 运行init进程7 读取/etc/inittab8 读取/etc/rc.sysinit初始化系统9 运行/etc/rc.d/rcN.d/脚本10 /etc/rc.local11 登录页面logincentos7与centos6前言 Linux系统的启动过程并不是大家想象中…

防静电监控仪可以检测现场设备是否和实际大地接触

随着电子产品集成化度越来越高&#xff0c;对于电子产品装配来说&#xff0c;静电的危害严重影响到产品的质量、成品率和可靠性, 必须对用于电子产品装配的净化间进行系统防静电措施&#xff0c;将生产过程中的静电危害程度降至最低。近年来电子企业对ESD的危害的深入认识&…

代码随想录刷题-数组-二分查找

文章目录写在前面原理习题题目1思路和代码题目-2写在前面 这个专栏是记录我刷代码随想录过程中的随想和总结。每一小节都是根据自己的理解撰写的&#xff0c;文章比较短&#xff0c;主要是为了记录和督促自己。刷完一章后&#xff0c;我会再单独整理一篇文章来总结和分享。 本…

【JVM 由浅入深】JVM入门

JVM入门1. 概述 今天我们对JVM 进行入门讲解&#xff0c;让我们了解下什么是JVM&#xff0c;是专门为Java服务的一款产品吗&#xff1f;&#xff1f;&#xff1f; 好了废话不多说了&#xff0c;让我们开始吧 2. 详解 2.1 Java 是跨平台的 为什么是Java是跨平台的呢&#xff0c…

LeetCode 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶子节点 是指没有子节点…

oneblog_justauth_三方登录配置【Github】

文章目录oneblog添加第三方平台github中创建三方应用完善信息登录oneblog添加第三方平台 1.oneblog管理端&#xff0c;点击左侧菜单 网站管理——>社会化登录配置管理 ,添加一个社会化登录 2.编辑信息如下&#xff0c;选择github平台后复制redirectUri,然后去github获取cl…

Arduino添加ESP32开发板

【2023年3月4日】 最近要在新电脑上安装Arduino&#xff0c;需要进行一些配置&#xff0c;正好记录一下&#xff01; Arduino2.0.1 下的开发板添加操作。 ESP32开发板GitHub链接&#xff1a; GitHub - espressif/arduino-esp32: Arduino core for the ESP32Arduino core for…

ICASSP2023|达摩院语音实验室入选论文全况速览

近日&#xff0c;语音技术领域国际会议ICASSP公布了本届论文审稿结果&#xff0c;阿里巴巴达摩院语音实验室有14篇论文被大会收录。本次被接收的论文研究方向涵盖语音识别、语音唤醒、语音增强、说话人日志、语义理解、多模态预训练等。 01 TOLD: A Novel Two-Stage Overlap-…

实验楼项目

创建虚拟环境命令&#xff1a;python -m venv venv 项目整体分为六个个模块&#xff1a;用户信息相关模块、机构相关模块、课程相关模块、用户操作相关模块&#xff0c;评论区模块 搜索模块。 # 项目功能概括&#xff1a; 1、首先具有完整的用户登录&#xff0c;邮箱注册以及…

JDBC的实现(IDEA版)

前期准备 开发环境&#xff1a; IDEA 2021.1.3 JAVA 1.8 MYSQL 8.0.32 msql用户名:root 密码&#xff1a;123 下载MySQL JDBC 驱动 前往MySQL官网下载对应版本的MySQL Connector/J驱动 &#xff08;下载地址&#xff1a;https://dev.mysql.com/downloads/connector/j/&#xff…

基于rootfs构建Docker镜像

1. 背景 在实际工作中&#xff0c;由于系统本身版本过低&#xff0c;在接受新项目时出现系统版本过低而无法开始工作的问题。 为了解决该问题&#xff0c;使用Docker构建基于ubuntu-18.04的Docker镜像&#xff0c;以解决版本兼容问题。 2. 构建rootfs 2.1. 下载ubuntu-18.0…

HCIA复习1

HCIA复习 抽象语言---->编码 编码---->二进制 二进制--->电信号 处理电信号 OSI参考模型----OSI/RM 应用层 表示层 会话层 传输层 端口号&#xff1a;0-65535&#xff1b;1-1023是注明端口 网络层 IP地址 数据链路层 物理层 ARP协议 正向ARP---通过IP地址获取目的MAC地…

云服务器Ubuntu(无桌面)安装远程桌面

如果主机上安装的ubuntu是桌面版&#xff0c;打开桌面共享即可使用Windows的远程桌面进行链接。详细参考ubuntu20.10中设置桌面共享的三种方式(任选其一) 的第一部分。 本文主要说明如何在Ubuntu云服务器上安装远程桌面。 一、安装桌面环境 在 Ubuntu 源仓库有很多桌面环境供…