C++ 核心编程(1)

c++面向对象编程

1.内存分区模型

程序运行前为代码区和全局区。程序运行后才有栈区和堆区。。

在这里插入图片描述

1.1 程序运行前

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
/*全局区
	全局变量、静态变量、常量 */
//全局变量
int g_1 = 20;
int g_2 = 30; 

//const修饰的全局变量,全局常量--也在全局区 
const int c_g_a = 10;
 
int main() {

	//普通局部变量--写到 main 函数体内, 
	int a=10;
	int b=10;
	
	//静态变量
	static int s_1 = 40;
	
	//常量
	//字符串常量
	cout<<"字符串常量地址为:"<<(int)&"hello world"<<endl; 
	//const修饰的常量
	const int c_a = 49; 
	
	//不在全局区中,1.局部变量  2.const修饰的局部变量(局部常量)
	//在全局区中,1.静态变量  2. 静态变量  3.常量(字符串常量,const修饰的常量) 
	
	return 0;
}

在这里插入图片描述

1.2 程序运行后

栈区:编译器自动分配和释放,存放函数参数值,局部变量等。
注意:不要返回局部变量地址,栈区开辟的数据由编译器自动释放

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
/* 栈区 --栈区数据由编译器管理开辟和释放 
	不要返回局部变量地址 */

int *fu(){
	int a = 10; //局部数据,存放在栈区, 
	return &a;
}
int main() {
	//接收返回值 
	int *p = fu();
	//第一次可以打印正确数据,因为编译器做了一次保留 
	cout<<*p<<endl;
	//第二次乱码 
	cout<<*p<<endl;
	return 0;
}

堆区:程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
在C++中主要利用new在堆区开辟内存

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
/* 堆区 --堆区数据由程序员管理开辟和释放 */

int *fu(){
	int *a = new int (10); //利用new 将数据开辟到堆区 
	return a;
}
 
int main() {

	//接收返回值 
	int *p = fu();
	//输出10 
	cout<<*p<<endl;
	//输出10
	cout<<*p<<endl;
	return 0;
}

1.3 new操作符

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//1.new 基本用法
int *f(){
	//堆区创建整型数据
	//new 返回的是 该数据类型的指针
	int *p = new int(10);
	return p; 
} 
void test01(){
	int *p = f();
	cout<<*p<<endl;
	cout<<*p<<endl;
	//释放堆区数据 -使用delete 
	delete p;
	//已释放,再次访问就是非法操作 
	//cout<<*p<<endl;
}
//堆区利用new开辟数组
void test02(){
	//创建10整型数据的数组,在堆区
	int *arr = new int[10];
	for(int i=0;i<10;i++){
		arr[i] = i+100;
		cout<<arr[i]<<endl;
	} 
	//释放堆区数组--释放数组需要加 [] 
	delete[] arr; 
} 

int main() {

	return 0;
}

2.引用

2.1 引用的基本使用

作用:给变量起别名
语法:数据类型 &别名 = 原名

2.2 引用注意事项

1.引用必须初始化
2.引用初始化后不可改变

#include<iostream>
#include <bits/stdc++.h>
using namespace std;

int main() {
	int a = 10;
	int &b = a;
	int c = 20;
	b = c; //赋值操作,不是更改引用 
	return 0;
}

2.3 引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//值传递
void swap1(int a,int b); 
//地址传递
void swap2(int *a,int *b);{
	int t = *a;
	*a = *b;
	*b = t;
}
//引用传递 
void swap3(int &a,int &b);{
	int t = a;
	a = b;
	b = t;
}
int main() {
	int a=1,b=3;
	swap1(a,b); //值传递
	swap2(&a,&b); //地址传递
	swap3(a,b); //引用传递 
	return 0;
}

2.4 引用做函数返回值

作用:引用可以作为函数返回值
注:不要返回局部变量引用

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//引用做函数返回值
int& test01(){
	int a = 1; //局部变量存放栈区 
	return a;
} 
//函数调用可以作为左值
int& test02(){
	static int a = 10; //静态变量存放全局区,全局区数据程序结束后释放 
	return &a;
} 
int main() {
	
	int &ref = test01();
	cout<<ref<<endl;  //第一次打印正确,因为编译器做了保留 
	//cout<<ref<<endl;	//再次打印乱码
	
	int &ref1 = test02();
	cout<<ref1<<endl;  //多次打印输出都正确,输出1 
	cout<<ref1<<endl;
	//函数做左值,若函数返回值是引用,那么函数可作为左值
	test02() = 1000;
	cout<<ref1<<endl;  //输出1000 
	cout<<ref1<<endl; 
	
	return 0;
}

2.5 引用的本质

本质:引用的本质在C++内部实现是一个指针常量

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//发现是引用,转换为 int *const ref = &a; 
void fc(int & ref){
	ref  = 1000; //ref是引用,转换为 *ref = 1000; 
}
int main() {
	
	int a = 10;
	//自动转换为 int *const ref = &a; 指针常量:指针指向不可改,也说明为什么引用不可更改
	int &ref = 0;
	ref = 20; // 内部发现ref是引用,自动转换为 *ref = 20;
	
	cout<<a<<endl;
	cout<<ref<<endl;
	
	fc(a); 
	
	return 0;
}
                 

2.6 常量引用

作用:修饰形参,防止误操作
函数形参中可以加const修饰形参,防止形参更改实参

#include<iostream>
#include <bits/stdc++.h>
using namespace std;

void test01(const int &a){
	//不可修改 
	//a = 1000;
	cout<<a<<endl;
} 

int main() {
	/*int a = 20;
	int &ref = 10; //错误,引用必须引一块合法的内存空间
	//加上const之后,编译器将代码修改 int temp = 10;  const int &ref = temp;
	const int& ref = 10; //正确,编译器优化代码,
	//加入const后变为只读,不可修改 
	//ref = 20;*/
	int a = 100;
 	test01(a);
 	
	system("pause");
	return 0;
}
                  

3.函数提高

3.1 函数默认参数

c++中,函数的形参列表中的形参是可以有默认值的
语法:返回值类型 函数名 (参数=默认值) {}

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//函数默认参数 
//若某个位置有了默认参数,那么后续的形参都要有默认参数,如b=10,则c必须要有值
int fc(int a,int b = 10,int c = 20){
	return a+b+c;
}
//声明和实现只能有一个有默认参数 ,否则会出现二义性问题 
int fc2(int a=10,int b=10);
int fc2(int a,int b){
	return a+b;
} 
int main() {
	//传入b=20,则使用20 
 	cout<<fc(10,20)<<endl;
 	//未传入b的值,使用10 
 	cout<<fc(10)<<endl;
	system("pause");
	return 0;
}
                 

3.2 函数占位参数

C++函数形参可以有占位参数,用来做占位,调用函数时必须填补该位置
语法:返回值类型 函数名(数据类型){},如 int f(int a,int);

int fc(int a,int);
int fc1(int a,int = 23); 

3.3 函数重载

3.3.1 函数重载概述

作用:函数名可以相同,提高复用性
满足条件:
1.同一作用域
2.函数名称相同
3.函数参数类型不同 或者 个数不同 或者 顺序不同
注:函数返回值不可以作为函数重载的条件

//均是重载函数 函数重载需要函数都在同一个作用域下
void f1(int a);
void f1(); 
void f1(int a,int b);
void f1(int);
void f1(int a,double b);
void f1(double a,int b); 
//非函数重载----函数返回值不可以作为函数重载的条件
int f2(int a);
double f2(int a);

3.3.2 函数重载注意事项

1.引用作为函数重载条件
2.函数重载碰到函数默认参数

#include<iostream>
#include <bits/stdc++.h>
using namespace std;

//函数重载注意事项 
//1.const可以作为函数重载条件 
int f1(int &a);   
int f1(const int &a);
//2.函数重载碰到默认参数
int f2(int a,int b=10);
int f2(int a); 
int main() {
	
	//1.const可以作为函数重载条件 
	int a = 10;
	f1(a); //调用 int f1(int &a);
	f1(10); //调用 int f1(const int &a); 若调用 int f1(int &a); 相当于 int &A = 10 (不合法) 
	
	//2.函数重载碰到默认参数
	f2(10);  //既可以调用 int f2(int a,int b=10);  也可调用  int f2(int a); 出现二义性
	f2(1,10); //调用 int f2(int a,int b=10);
	return 0;
}
                 

4 类和对象

C++面向对象三大特性:封装 继承 多态
在这里插入图片描述

4.1 封装

4.1.1 封装的意义

封装是C++面向对象三大特性之一
封装的意义:
1.将属性和行为作为一个整体,表现生活中的事物
2.将属性和行为加以权限控制

封装的意义一:设计类时候,属性和行为写在一起,表现事物。
语法:class 类名 { 访问权限: 属性 / 行为 };

1.示例:设计圆类,求周长

#include<iostream>
#include <bits/stdc++.h>
using namespace std;

const double PI = 3.14;
class Circle{
	//访问权限
	//公共权限:类内类外都可访问 
	//类中的属性和行为 都成为成员
	//属性:成员属性 成员变量
	//行为:成员函数 成员方法 
public:
	//属性
	//1.半径 
	int m_r; 
	
	//行为 
	//1.获取圆周长
	double calZC(){
		return 2*PI*m_r;
	}
	//2.给半径赋值
	void setMR(int r){
		m_r = r;
	}
};
int main() {
	//1.通过圆类, 创建具体的圆(对象)
	//实例化,通过一个类创建一个对象的过程
	Circle c1;
	c1.m_r = 10; //或 c1.setMR(5);
	cout<<"周长:"<<c1.calZC()<<endl; 
	return 0;
}
                 

封装的意义二:类在设计时,可以把属性和行为放在不同的权限下,加以控制
三种权限:
1.public : 共有
2.protected:保护权限
3.private:私有权限

#include<iostream>
#include <bits/stdc++.h>
using namespace std;

const double PI = 3.14;
class Circle{
//1.public : 公共权限  成员 类内可以访问  类外可以访问
public:
	string name;
//2.protected:保护权限  成员 类内可以访问  类外不可以访问  儿子可访问父亲保护内容 
proteced:
	string car;
//3.private:私有权限  成员 类内可以访问  类外不可以访问   儿子不可访问父亲私有内容 
private:
	string password;

public:
	void func(){
		name = "123";
		car = "345";
		password = "2452432";
	} 
};
int main() {
	Circle c1;
	c1.name = "王";
	//car 类外不可访问 
	c1.car = "米su7"; 
	//password 类外不可访问 
	c1.password = "1234"; 
	return 0;
}
                 

4.1.2 struct和class区别

C++中struct和class唯一区别在于默认的访问权限不同
区别:struct默认权限公共,class默认权限为私有

4.1.3 成员属性设置为私有

优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,可以检测数据的有效性

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//成员属性设置为私有
 //1.可以自己控制读写权限
 //2.对于写可以检测数据有效性 
class Circle{
public:
	//通过公有方法对私有属性读写
	void setName(string name){
		m_name = name;
	} 
	string getName(){
		return m_name;
	}
	void setAge(int age){
		if(age>0){
			m_age = age;
		}
	}
	int getAge(){
		return m_age;
	}
	void setIdo(int idol){
		m_idol = idol;
	}
private:	
	string m_name;  ///可读可写 
	int m_age = 18;	//只读  也可以写 
	int m_idol; //只写 
};

int main() {
	Circle c1;
	c1.setName("张");
	cout<<c1.getName()<<endl;
	cout<<c1.getAge()<<endl;
	//读不到 
	cout<<c1.m_idol<<endl;
	return 0;
}
                 
                 

案例2 点和圆的关系

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//点和圆关系 
class Point{
	public:
		void setX(int x){
			m_X = x;
		}
		int getX(){
			return m_X;
		}
		void setY(int y){
			m_Y = y;
		}
		int getY(){
			return m_Y;
		}
	private:
		int m_X,m_Y;
}; 
class Circle{
public:
	void setR(int r){
		m_R = r;
	}
	int getR(){
		return m_R;
	}
	void setCenter(Point p){
		m_Center = p;
	}
	Point getCenter(){
		return m_Center;
	}
private:	
	int m_R;
	Point m_Center;
};
//判断点和圆关系
void isInCircle(Circle &c,Point &p){
	//计算两点距离平方
	int distance = 
	(c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) + 
	(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
	//计算半径平方
	int rDis = c.getR() * c.getR();
	if(rDis == distance){
		cout<<"点在圆上"<<endl; 
	}else if(rDis > distance){
		cout<<"点在圆外"<<endl; 
	}else{
		cout<<"点在圆内"<<endl; 
	} 
} 
int main() {
	//圆 
	Circle c1;
	//半径 
	c1.setR(10);
	//设置圆心 
	Point p; 
	p.setX(10);
	p.setY(0);
	c1.setCenter(p);
	
	//创建点
	Point p1;
	p1.setX(10);
	p1.setY(10); 
	
	isInCircle(c1,p1);
	return 0;
}
                 
//正常实现需要分离为Point.h Point.cpp Circle.h Circle.cpp main.cpp
Point.h:
class Point{
	public:
		void setX(int x);
		int getX();
		void setY(int y);
		int getY();
	private:
		int m_X,m_Y;
}; 
Point.cpp:
		#include <Point.h>
		//需要指明所属作用域
		void Point::setX(int x){
			m_X = x;
		}
		int Point::getX(){
			return m_X;
		}
		void Point::setY(int y){
			m_Y = y;
		}
		int Point::getY(){
			return m_Y;
		}
Circle.h 和 Circle.cpp类似

4.2 对象的初始化和清理

4.2.1 构造函数和析构函数

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

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
class Circle{
public:
	//1.构造函数,
	//无返回值;不用写void; 函数名与类名相同; 构造函数可有参数; 可重载;创建对象时候构造函数会自动调用,只调用一次
	Circle(){
		cout<<"构造"<<endl; 
	} 
	
	//2.析构函数
	//无返回值;不写void;函数名同类目 在名称前加~ ; 析构不可有参数;不可重载; 对象销毁前会自动调用析构函数,只调用一次
	~Circle(){
		cout<<"析构"<<endl; 
	} 
};
void test01(){
	Circle c;
}
int main() {
	//调用构造函数 和 析构 
	//因为test01中的Circle c是局部变量,是栈上的数据,执行完会释放对象 
	test01();
	
	//加上 system("pause"); 则系统会中断不停止,不会执行析构,不加还是会执行析构。点击按任意键继续后会执行析构 
	Circle c1;
	system("pause"); 
	return 0;
}    

4.2.2 构造函数的分类及调用

两种分类方式:
1.按参数分为:有参构造和无参构造
2.按类型分为:普通构造和拷贝构造
三种调用方式:
1.括号法
2.显示法
3.隐式转换法

#include<iostream>
#include <bits/stdc++.h>
using namespace std;

class Circle{
public:
	//普通构造 
	//无参和有参 
	Circle(){
		cout<<"构造"<<endl; 
	} 
	Circle(int a){
		cout<<"有参构造"<<endl; 
	}
	//拷贝构造
	Circle(const Circle &c){
		cout<<"拷贝构造"<<endl; 
		age = c.age;
	} 
	~Circle(){
		
	}
	
	int age;

};
void test01(){
	//1.括号法
	Circle c; //默认构造调用
	Circle c1(10); //有参构造调用
	Circle c2(c1);	//拷贝构造函数 
	//调用默认构造函数时候,不要加() ,此时执行则什么都不输出
	//因为下边代码,编译器会认为是一个函数的声明 
	Cirlce c3(); 
	
	//2.显示法
	Circle c1;
	Cirlce c2 = Circle(10);
	Cirlce c3 = Circle(c2); 
	//1.匿名对象, 特点:当前行执行结束后,系统会立即回收匿名对象。先执行构造再执行析构
	Circle(10); 
	//2.不要利用拷贝构造函数初始化匿名对象。此时编译器会认为 Circle(c3) = Circle c3; 两个c3,名称重复 
	Circle(c3);
	//3.隐式转换法 
	//相当于 Cirlce c4 = Circle(10); 
	Cirlce c4 = 10;	//有参构造 
	Cirlce c5 = c4;	//拷贝构造 
}

int main() {

	return 0;
}
                 

4.2.3 拷贝构造函数的调用时机

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//拷贝构造调用时机 
class Circle{
public:
	Circle(){
		cout<<"构造"<<endl; 
	} 
	Circle(int a){
		age = a;
		cout<<"有参构造"<<endl; 
	}
	//拷贝构造
	Circle(const Circle &c){
		cout<<"拷贝构造"<<endl; 
		age = c.age;
	} 
	~Circle(){
		
	}
	int age;

};
//1.使用一个已经创建完毕的对象初始化一个新对象 
void test01(){
	Circle c1(20);
	Circle c2(c1);
}
//2.值传递方式 给函数参数传值
void doWork(Circle c){
	
}
void test02(){
	Circle c1;
	//执行函数过程也会执行 构造函数,因为是值传递 
	doWork(c1);
}
//3.值方式返回局部对象 
Circle doWork2(){
	Circle c1;
	//返回过程会根据 c1 创建一个新的对象 
	return c1;
}
void test03(){
	//c和 doWork2中 Circle c1; 不是一个对象 
	Cirlce c = doWork2();
}
int main() {
	
	//调用两次构造 两次析构; Circle c1;一次。 doWork(c1);一次 
	testo3();
	//调用两次构造 两次析构:Circle c1;一次。return c1;一次 
	test03();
	return 0;
}
                 

4.2.4 构造函数调用规则

在这里插入图片描述

提供有参构造,则编译器提供拷贝构造,但不提供无参构造
提供拷贝构造,则编译器有参构造和无参构造都不提供,需要手动编写

4.2.4 深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请框架,进行拷贝操作

浅拷贝
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//深拷贝和浅拷贝 
class Circle{
public:
	Circle(){
		cout<<"构造"<<endl; 
	} 
	Circle(int a,int height){
		age = a;
		m_H = new int(height);
		cout<<"有参构造"<<endl; 
	}

	~Circle(){
		//析构代码,将堆区开辟数据做释放操作
		if(m_H != NULL){
			delete m_H;
			//防止野指针 
			m_H = NULL;
		}
		cout<<"析构"<<endl; 
	}
	int age;
	int *m_H;
};
void test01(){
	//先进后出,c2的析构先被释放 
	Circle c1(18,160);
	cout<<"c1年龄:"<<c1.age<<" c1身高:"<<*c1.m_H<<endl;
	Circle c2(c1);
	cout<<"c2年龄:"<<c2.age<<" c2身高:"<<*c2.m_H<<endl;
}

int main() {
	test01();
	return 0;
}
                 

浅拷贝的问题:堆区的内存重复释放。案例中p2先执行析构释放了 *m_H。然后p1又进行释放,重复释放引发异常。

在这里插入图片描述

解决:利用深拷贝解决。重新在堆区申请一块内存,使p1中的m_H和p2中的m_H申请不同的内存。
如果有堆区开辟的数据进行浅拷贝,则需要使用拷贝构造进行深拷贝。

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//深拷贝和浅拷贝 
class Circle{
public:
	Circle(){
		cout<<"构造"<<endl; 
	} 
	Circle(int a,int height){
		age = a;
		m_H = new int(height);
		cout<<"有参构造"<<endl; 
	}
	//自己实现拷贝构造函数,解决浅拷贝带来的问题
	Circle(const Cirlce &p){
		cout<<"拷贝构造函数"<<endl;
		age = p.age;
		//编译器默认执行的是 m_H = p.m_H;
		
		// 深拷贝操作
		m_H = new int(*p.m_H); 
	} 

	~Circle(){
		//析构代码,将堆区开辟数据做释放操作
		if(m_H != NULL){
			delete m_H;
			//防止野指针 
			m_H = NULL;
		}
		cout<<"析构"<<endl; 
	}
	int age;
	int *m_H;
};
void test01(){
	//先进后出,c2的析构先被释放 
	Circle c1(18,160);
	cout<<"c1年龄:"<<c1.age<<" c1身高:"<<*c1.m_H<<endl;
	Circle c2(c1);
	cout<<"c2年龄:"<<c2.age<<" c2身高:"<<*c2.m_H<<endl;
}
int main() {
	test01();
	return 0;
}
                 

4.2.6 初始化列表

C++提供了初始化列表语法,用来初始化属性。
语法:构造函数():属性1(值1),属性2(值2),… {}

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//深拷贝和浅拷贝 
class Circle{
public:
	//传统方式 
	/*Circle(int a,int b,int c){
		a_1 = a;
		b_1 = b;
		c_1 = c;
	}*/
	//初始化列表初始化属性
	Circle(int a,int b,int c) :a_1(a),b_1(b),c_1(c){
		
	}
	int a_1,b_1,c_1;
};
void test01(){
	//Circle c(1,2,3);
	Circle c(1,2,3); //等价 Circle c(1,2,3);
}
int main() {
	test01();
	return 0;
}    

4.2.7 类对象作为类成员

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

class A{}
//B中有对象A作成员,A是对象成员,此时会先执行对象成员的构造函数,即先调用A的构造函数
class B{
	A a;
}
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//类对象作为类成员
class Phone{
public:
	Phone(string pName){
		m_Name = pName;
		cout<<"Phone构造函数"<<endl; 
	}
	string m_Name;
};
class Person{
public:
	//等价 Phone m_phone = pname; 
	Person(string name,string pname):m_name(name),m_phone(pname){
		cout<<"Person构造函数"<<endl; 
	}
	//姓名
	string m_name;
	//手机
	Phone m_phone; 
}; 
//当其他类对象作为本类成员,构造时候先构造其它类对象构造函数,再调用本类构造。析构函数相反 
void test01(){
	Person p("张","苹果max");
}
int main() {
	test01();
	return 0;
}    

4.2.8 静态成员

静态成员即在成员变量前加 static,称为静态成员。
在这里插入图片描述

静态成员变量
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//静态成员变量 
class Person{
public:
	//1.所有对象共享同一份数据
	//2.编译阶段就分配内存
	//3.类内声明,类外初始化操作 
	static int m_A; 
}; 
void test01(){
	Person p;
	//出错,因为去找m_A具体值时候找不到,因此需要初始化 
	cout<<p.m_A<<endl;
}
int main() {
	test01();
	
	return 0;
}    
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//静态成员变量 
class Person{
public:
	//1.所有对象共享同一份数据
	//2.编译阶段就分配内存
	//3.类内声明,类外初始化操作 
	static int m_A; 
	
	//静态成员变量也有访问权限
private:
	static int m_B; 
}; 
int Person::m_A = 100;
int Person::m_B = 100;

void test01(){
	Person p;
	cout<<p.m_A<<endl;
	Person p1;
	p1.m_A = 200;
	//打印200 
	cout<<p.m_A<<endl;
}
void test02(){
	//静态成员变量,不属于某个对象上,所有对象共享同一份数据
	// 因此静态成员变量有两种访问方式
	//1.通过对象进行访问
	Person p; 
	cout<<p.m_A<<endl;
	//2.通过类名进行访问
	cout<<Person::m_A<<endl;
	
	//私有作用域:无法通过这种方式访问 
	cout<<Person::m_B<<endl;
} 
int main() {
	test01();
	return 0;
}     
静态成员函数
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//静态成员函数
//所有对象共享同一个函数
//静态成员函数只能访问静态成员变量 
class Person{
public:
	static void func(){
		m_A = 100;
		//静态成员函数,不可以访问,非静态成员变量,因为无法区分到底是哪个对象的 m_B 
		m_B = 200;
		cout<<"static void func 调用"<<endl;
	}
	static int m_A; 
	int m_B;
	//静态成员函数的访问权限 
private:
	static void func2(){
		cout<<"static void func2 调用"<<endl;
	}
	
}; 
int Person::m_A = 10;

//两种访问方式 
void test01(){
	//1.通过对象访问
	Person p;
	p.func();
	
	//2.通过类名访问
	Person::func();
	//私有作用域下无法访问 ,类外访问不到私有静态成员函数 
	Person::func2();
}
int main() {
	test01();
	return 0;
}    

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

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

C++中,类内的成员变量和成员函数分开存储。
只有非静态成员变量才属于类的对象上

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//成员变量和成员函数是分开存储的 
class Person{
}; 
class Person1{
	 int m_A;
}; 
class Person2{
	int m_A;
	static int m_B;
};
class Person3{
	void func();
}; 
void test01(){
	Person p;
	//空对象占用的内存空间为 1 
	//C++会给每个空对象也分配一个字节空间,为了区分空对象占内存的位置,每个空对象也应有内存地址 
	cout<<"sizeof(p):"<<sizeof(p)<<endl; 
}
void test02(){
	Person1 p1;
	//输出 4 
	cout<<"sizeof(p1):"<<sizeof(p1)<<endl; 
}
void test03(){
	Person2 p2;
	//输出 4, 静态成员变量在对象中不占内存空间 
	cout<<"sizeof(p2):"<<sizeof(p2)<<endl; 
}
void test04(){
	Person3 p3;
	//输出1 
	cout<<"sizeof(p3):"<<sizeof(p3)<<endl; 
}
int main() {
	test01();	test02();	test03();	test04();
	return 0;
}    

4.3.2 this指针概念

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//this指针 
class Person{
public:
	Person(int age){
		//不加this会出问题,this指针指向的是被调用的成员函数 所属的对象 
		this->age = age;
	}
	int age;
	Person& PersonAddAge(Person &p){
		this->age += p.age;
		//this指向p2的指针,*this指向p2本身 
		return *this;
	}
}; 
//1.解决名称冲突 
void test01(){
	Person p(19);
	cout<<p.age<<endl;
}
//2.返回对象本身用*this 
void test02(){
	Person p1(10);
	Person p2(19);
	//输出 19+10+10
	p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
	cout<<p2.age<<endl;
}
int main() {
	test01();test02();
	return 0;
}    

4.3.3 空指针访问成员函数

C++中空指针也是可以调用成员函数的, 但是也要注意有没有用到this指针。
若用到this指针,需要加以判断保证代码健壮性。

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//空指针调用成员函数 
class Person{
public:
	
	void showClassName(){
		cout<<"this showClassName"<<endl;
	}
	void showAge(){
		// 等价于 cout<<"age= "<<this->m_age<<endl; 
		cout<<"age= "<<m_age<<endl;
	}
	int m_age;
}; 
void test01(){
	Person *p = NULL;
	//正常调用 
	p->showClassName();
	//提示空指针 
	p->showAge();
}
int main() {
	test01();
	return 0;
}    

4.3.4 const修饰成员函数

在这里插入图片描述

常函数和常对象
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//空指针调用成员函数 
class Person{
public:
	//this指针本质:是指针常量 指针的指向不可修改 指向的值可修改 
	void showAge1(){
		//可修改 
		m_age = 100; 
		//this = NULL;	//this指针不可修改指针指向 
	}
	//加上 const后 this指针的指向不可修改 指向的值也不可修改 
	void showAge() const {
		//m_age = 100; 
		this->m_b = 23;
	}
	int m_age;
	mutable int m_b;	//特殊变量 使其在常含数也可修改,加上关键字 mutable
}; 
//常函数 
void test01(){
	Person p;
	p.showAge(); 
}
//常对象 
void test02(){
	const Person p;	//在对象前加const,变为常对象
	//p.m_age = 20; //不可修改
	p.m_b = 234;	//可修改,添加了mutable的成员变量在常对象下也能修改 
	//常对象只能调用常含数,不能调用普通成员函数,因为普通成员函数可修改不加mutable的属性 
	p.showAge(); 
}
int main() {
	test01();test02();
	return 0;
}    

4.4 友元

在这里插入图片描述

友元的三种实现:
1.全局函数友元
2.类做友元
3.成员函数做友元

4.4.1 全局函数做友元

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//
class Building{
	//goodGay全局函数是 Building好朋友,可以访问 Building 中私有成员 
	friend void goodGay(Building *build); 
public:
	Building(){
		m_setRoom = "客厅";
		m_bedRoom = "卧室"; 
	}
public:
	string m_setRoom;
private:
	string m_bedRoom;
};
//全局函数做友元:把函数放入类中即可 
//访问build的全局和私有属性 
void goodGay(Building *build){
	 cout<<"goodGay正在访问:"<< build->m_setRoom<<endl;
	 //出错,私有属性无法访问,此时将本函数全部放入building类中 即可访问 
	 cout<<"goodGay正在访问:"<< build->m_bedRoom<<endl;
} 
void test01(){
	Building build;
	goodGay(&build);
}
void test02(){

}
int main() {
	test01();test02();
	return 0;
}    

4.4.2 类做友元

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//类做友元 
class Building;
class GoodGay{
	public:
		GoodGay();
		void visit();	//参观函数访问Building类中的属性
		  
		Building * build;
};
class Building{
	//加入后 GoodGay 中的函数即可访问  Building 的私有成员 
	friend class GoodGay;
	public:
		Building();
	public:
		string m_setRoom;
	private:
		string m_bedRoom;
};
//类外写成员函数
Building::Building(){
	m_setRoom = "客厅";
	m_bedRoom = "卧室"; 
}
GoodGay::GoodGay(){
	build = new Building;
}
void GoodGay::visit(){
	cout<<"GoodGay正在访问:"<< build->m_setRoom<<endl;
	//此时不可访问,此时只需要将 friend class GoodGay; 加入Building中即可访问 
	cout<<"GoodGay正在访问:"<< build->m_bedRoom<<endl;
}
void test01(){
	GoodGay gg;
	gg.visit();
}

int main() {
	test01();
	return 0;
}    

4.3.4 成员函数做友元

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//成员函数做友元 
class Building;
class GoodGay{
	public:
		GoodGay();
		void visit();	//让 visit 函数可访问Building类中的私有属性
		void visit1();  //不可访问 Building类中的私有属性
		Building * build;
};
class Building{
	//告诉编译器 GoodGay 中的visit函数作为友元函数,可访问 Building 的私有成员 
	friend void GoodGay::visit(); 
	public:
		Building();
	public:
		string m_setRoom;
	private:
		string m_bedRoom;
};
//类外写成员函数
Building::Building(){
	m_setRoom = "客厅";
	m_bedRoom = "卧室"; 
}
GoodGay::GoodGay(){
	build = new Building;
}
void GoodGay::visit(){
	cout<<"visit 正在访问:"<< build->m_setRoom<<endl;
	//此时不可访问,此时只需要将 friend void GoodGay::visit(); 加入Building中即可访问 
	cout<<"visit 正在访问:"<< build->m_bedRoom<<endl;
}
void GoodGay::visit1(){
	cout<<"visit1 正在访问:"<< build->m_setRoom<<endl;
	//此时不可访问
	//cout<<"visit1 正在访问:"<< build->m_bedRoom<<endl;
}
void test01(){
	GoodGay gg;
	gg.visit();
	gg.visit1(); 
}
int main() {
	test01();
	return 0;
}    

4.5 运算符重载

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

4.5.1 加号运算符重载

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

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//加号运算符重载 
class Person
{
public:
	//1.成员函数重载 + 号
/*	Person operator+(Person &p){
		Person temp;
		temp.ma = this->ma + p.ma;
		temp.mb = this->mb + p.mb;
	} */
public:
	int ma;
	int mb;	 
};
//2.全局函数重载 + 号
Person operator+(Person &p1,Person &p2){
	Person temp;
	temp.ma = p1.ma + p1.ma;
	temp.mb = p2.mb + p2.mb;
	return temp;
} 
//运算符重载也可以发生函数重载
Person operator+(Person &p1,int a){
	Person temp;
	temp.ma = p1.ma + a;
	temp.mb = p1.mb + a;
	return temp;
} 
void test01(){
	Person p1;
	p1.ma = 1;
	p1.mb = 2;
	Person p2;
	p2.ma = 1;
	p2.mb = 2;
	Person p3 = p1 + p2;
	cout<<"p3.ma: "<<p3.ma<<" p3.mb: "<<p3.mb<<endl;
	
	Person p4 = p3 + 100;
	cout<<"p4.ma: "<<p4.ma<<" p4.mb: "<<p4.mb<<endl;
}
int main() {
	test01();
	return 0;
}    

总结:1.对于内置的数据类型的表达式的运算符是不可能改变的
2.不要滥用运算符重载

4.5.2 左移运算符重载

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

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//左移运算符重载 
class Person
{
	friend ostream & operator<<(ostream &cout,Person &p);
public:
	//利用成员函数重载,左移运算符. p.opeartor<<(cout) 简化版本 p << cout
	//不会利用成员函数重载 << 运算符,因为无法实现cout在<<左侧 
	//void opeartor<<(){	}
	Person(int a,int b){
		ma = a;
		mb = b;
	} 
private:
	int ma;
	int mb;	 
};
//只能利用全局函数重载 左移运算符
ostream & operator<<(ostream &cout,Person &p){		//本质 opeartor<< (cout ,p) 简化 cout<< 
	cout<<"ma:"<<p.ma<<" mb:"<<p.mb;
	return cout;
} 
//未返回cout,不能追究endl 
/*void operator<<(ostream &cout,Person &p){		//本质 opeartor<< (cout ,p) 简化 cout<< 
	cout<<"ma:"<<p.ma<<" mb:"<<p.mb<<endl;
} */
// 输出对象p,直接输出其属性 
void test01(){
	Person p1(10,10);
	//此时无法直接输出,需要重载 << 符号。利用全局函数重载 
	//cout<<p;
	//利用全局函数重载 << 后追加endl后就无法输出,因为重载的左移运算符返回的不是 cout 。将重载返回cout后即可使用 
	cout<<p1<<endl; 
}
int main() {
	test01();
	return 0;
}    

4.5.3 递增运算符重载

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//递增运算符重载 
class MyInteger{
	friend ostream & operator<<(ostream &cout,MyInteger &p);
public:
	MyInteger(){
		m_num = 0;
	}
	//1.重载前置++运算符。返回引用是为了一直对一个数据递增。返回引用。 
	//不加引用的话,MyInteger mint; ++(++mint);执行后,默认的m_num是1,不是2; 
	MyInteger& operator++(){
		//先进行++,再返回自身 
		m_num++;
		return *this;	
	}
	//2.重载后置++运算符。返回值。因为中间使用了临时变量,若返回其引用,后续调用就是非法操作 
	//int 代表占位参数,可以用于区分前置和后置 
	MyInteger operator++(int){
		//先 记录当时结果
		MyInteger temp = *this;
		//后 递增
		m_num++;
		//返回未+1前的记录结果 
		return temp; 
	}
private:
	int m_num;
};
//只能利用全局函数重载左移运算符
ostream & operator<<(ostream &cout,MyInteger &p){		//本质 opeartor<< (cout ,p) 简化 cout<< 
	cout<<"m_num:"<<p.m_num;
	return cout;
} 
//测试重载前置运算符 
void test01(){
	MyInteger mint;
	cout<<++(++mint)<<endl;
}
//测试重载后置运算符 
void test02(){
	MyInteger mint1;
	mint1++;
	cout<< mint1 <<endl;
}
int main() {
	test01();test02();
	return 0;
}    

4.5.4 赋值运算符重载

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//赋值运算符重载 
class Person{
	public:
		Person(int age){
			mage = new int(age);//创建变量到堆区 
		}
		~Person(){
			if(mage!=NULL){
				delete mage;
				mage = NULL;
			}
		}
		//重载赋值运算符
		Person& operator=(Person &p){
			//编译器是提供浅拷贝,但是对于堆区的数据存在问题 
			//mage = p.mage; 
			//应该先判断是否有属性在堆区,若有则先释放干净,然后再深拷贝
			if(mage != NULL){
				delete mage;
				mage = NULL;
			}
			//深拷贝
			mage = new int(*p.mage); 
			//返回对象本身
			return *this;
		} 
		int *mage;
};
void test01(){
	Person p1(10);
	Person p2(16);
	Person p3(20);
	p3 = p2 = p1;	//赋值操作,写析构函数中的 if(mage!=NULL){	delete mage;mage = NULL;} 后会崩。没写可以输出。 
	cout<<"p1的年龄为:"<<*p1.mage<<endl;
	cout<<"p2的年龄为:"<<*p2.mage<<endl;
	cout<<"p3的年龄为:"<<*p3.mage<<endl;
}
int main() {
	test01();
	return 0;
}    

4.5.5 关系运算符重载

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

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//关系运算符重载 
class Person{
public:
	Person(string name,int age){
		mname = name;
		mage = age; 
	}
	//重载==
	bool operator==(Person &p){
		if(this->mname == p.mname && this->mage == p.mage){
			return true;
		}
		return false;
	} 
	//重载!=
	bool operator!=(Person &p){
		if(this->mname != p.mname || this->mage != p.mage){
			return false;
		}
		return true;
	} 	
	int mage;
	string mname; 
};
void test01(){
	Person p1("tom",10);
	Person p2("tom",10);
	if(p1 == p2){
		cout<<"相等"<<endl; 
	}else cout<<"不相等"<<endl; 
}
int main() {
	test01();
	return 0;
}    

4.5.6 函数调用运算符重载

在这里插入图片描述

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//函数调用运算符重载 
class MyPrint{
public:
	//重载()
	void operator()(string test){
		cout<<test<<endl;
	} 
	int mage;
	string mname; 
};
void MyPrint2(string t){
	cout<<t<<endl;
}
void test01(){
	MyPrint mp;
	mp("hello");	//因为使用起来非常类似于函数调用,因此称为仿函数
	MyPrint2("hello1"); //同上 
}
int main() {
	test01();
	return 0;
}    
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//函数调用运算符重载--仿函数 
class MyAdd{
public:
	int operator()(int n1,int n2){
		return n1+n2;
	}
};
void test02(){
	MyAdd ad;
	int ret = ad(2,5); //仿函数 
	cout<<ret<<endl;
	//匿名函数对象。匿名对象(类名+()) 
	cout<< MyAdd()(100,10)<<endl;
}
int main() {
	test02();
	return 0;
}    

4.6 继承

继承是面向对象三大特性之一。

在这里插入图片描述

4.6.1 继承的用法

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//公共页面 
class BasePage{
public:
	void header(){
		cout<<"头部信息"<<endl; 
	}
	void footer(){
		cout<<"底部信息"<<endl; 
	}
	void left(){
		cout<<"左侧信息"<<endl; 
	}
}; 
//java页面 继承 公共页面
//语法: class 子类 : 继承方式 父类
//子类 也称 派生类
//父类 也称 基类
class Java:public BasePage{
public:
	void context(){
		cout<<"java内容"<<endl; 
	}
}; 
//Python页面 继承 公共页面 
class Py:public BasePage{
public:
	void context(){
		cout<<"Python内容"<<endl; 
	}
};

void test01(){
	Java ja;
	ja.header();ja.footer();ja.left();ja.context();
	Py p;
	p.header();p.footer();p.left();p.context();
}
int main() {
	test01();
	return 0;
}    

继承好处:减少代码量
在这里插入图片描述

4.6.2 继承方式

继承语法:class 子类:继承方式 父类
继承方式一共有三种:
1.公共继承:
2.保护继承:
3.私有继承:
在这里插入图片描述
1.公有数据:类外可访问
2.保护数据:类外不可访问
3.私有数据:类外不可访问

4.6.3 继承中的对象模型

问题:从父类继承过来的成员,哪些属于子类对象中?

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//继承中的对象模型 
class Base{
public:
	int ma;
protected:
	int mb;
private:
 	int mc;
}; 
class Son : public Base{
public:
	int md;
};

//利用开发人员命令提示工具查看对象模型,
//https://www.bilibili.com/video/BV1et411b73Z?p=136&vd_source=fa51e45d3ee148477e96ca9b174edea6  p136 14:55处
//跳转盘符 F:
//跳转文件路径 cd 具体路径下
//查看命名
// c1 /d1 reportSingleClassLayout类名 文件名 
void test01(){
	// 输出 16。即父类中所有非静态成员属性都会被子类程序继承下去
	// 父类中私有成员属性,是被编译器给隐藏了,因此访问不到,但是确实被继承下去了 
	cout<<"size of Son = "<<sizeof(Son)<<endl;
}
int main() {
	test01();
	return 0;
}    

4.6.4 继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类的构造和析构顺序是谁先谁后?

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//继承中构造和析构的顺序 
class Base{
public:
	Base(){
		cout<<"Base构造函数!"<<endl; 
	}
	~Base(){
		cout<<"Base析构函数!"<<endl; 
	}
}; 
class Son : public Base{
public:
	Son(){
		cout<<"Son构造函数!"<<endl; 
	}
	~Son(){
		cout<<"Son析构函数!"<<endl; 
	}
};
void test01(){
	//Base b; 
	//先创建父类在创建子类。即先父类构造,再有子类构造;然后是父类析构,子类析构(析构顺序与构造相反) 
	Son s;
}
int main() {
	test01();
	return 0;
}    

4.6.5 继承同名成员处理方式

问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据?
1.访问子类同名成员,直接访问
2.访问父类同名成员 需要加作用域

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//继承中构造和析构的顺序 
class Base{
public:
	Base(){
		ma = 10;
	}
	int ma; 
	void func(){
		cout<<"Base-func调用"<<endl; 
	}
	void func(int a){
		cout<<"Son-func(int a)调用"<<endl; 
	}
}; 
class Son : public Base{
public:
	Son(){
		ma = 20;
	}
	int ma;
	void func(){
		cout<<"Son-func调用"<<endl; 
	}

};
//同名属性处理方式 
void test01(){
	Son s;
	//访问子类自身的ma,直接访问 
	cout<<"Son ma:"<<s.ma<<endl;
	//访问父类的同名 ma,加作用域
	cout<<"Base ma:"<<s.Base::ma<<endl;
}
void test02(){
	Son s;
	s.func();	//直接调用,调用的是子类的同名成员
	//调用父类中同名的成员函数
	s.Base::func(); 
	//若子类中出现和父类中同名的 成员函数,子类的同名成员会隐藏吊父类所有同名成员函数 
	//如果想访问父类中被隐藏的同名成员函数,需要加作用域
	s.Base::func(100);
}
int main() {
	test01();test02();
	return 0;
}    

在这里插入图片描述

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

问题:继承中通吗的静态成员函数在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一样。
1.访问子类同名成员 直接访问即可
2.访问父类同名成员 需要加作用域

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//继承中的同名静态成员处理方式 
class Base{
public:
	static int ma;
	static void func(){
		cout<<"Base static void func()"<<endl;
	} 
}; 
int Base::ma = 100;
class Son : public Base{
public:
	static int ma;
	static void func(){
		cout<<"Son static void func()"<<endl;
	} 
	static void func(int a){
		cout<<"Son static void func(int a)"<<endl;
	} 
};
int Son::ma = 200;
//1.静态同名属性
void test01(){
	//1.通过对象访问静态数据 
	Son s;
	cout<<"Son ma:"<<s.ma<<endl;
	cout<<"Base ma:"<<s.Base::ma<<endl;
	//2.通过类名访问
	cout<<"通过类名访问:"<<endl; 
	cout<<"Son 下ma:"<<Son::ma<<endl;
	//第一个 ::代表通过类名方式访问    第二个::代表访问父类作用域下 
	cout<<"Base 下ma:"<<Son::Base::ma<<endl;
}
//2。同名静态函数成员 
void test02(){
	//1.通过对象访问 
	Son s;
	s.func();
	s.Base::func();
	//2.通过类名访问
	Son::func(); 
	Son::Base::func();
	
	//子类和父类出现同名函数,子类会将父类所有同名静态成员函数,也会隐藏掉父类所有童名成员函数
	//若要访问父类被隐藏同名函数,需要加作用域
	Son::Base::func(100); 
}
int main() {
	test01();test02();
	return 0;
}    

4.6.7 多继承语法

C++允许一个类继承多个类。
语法:class 子类 : 继承方式 父类1,继承方式 父类2 …

多几次可能会引发父类中同名成员出现,需要加作用域区分。
C++实际开发中不建议使用多继承。

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//多继承语法 
class Base1{
public:
	Base1(){
		ma = 100;
	}
	int ma;
}; 
class Base2{
public:
	Base2(){
		mb = 200;
	}
	int mb;
}; 
//子类:继承base1 和 base2 
class Son : public Base1,public Base2{
public:
	Son(){
		mc = 300;
		md = 400;
	}
	int mc;
	int md;
};

void test01(){
	Son s;
	//输出16,四个成员变量 
	cout<<"sizeof(Son):"<<sizeof(s)<<endl; 
}

int main() {
	test01();
	return 0;
}    
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
//多继承语法 
class Base1{
public:
	Base1(){
		ma = 100;
	}
	int ma;
}; 
class Base2{
public:
	Base2(){
		ma = 200;
	}
	int ma;
}; 
//子类:继承base1 和 base2 
class Son : public Base1,public Base2{
public:
	Son(){
		mc = 300;
		md = 400;
	}
	int mc;
	int md;
};

void test01(){
	Son s;
	//输出16,四个成员变量 
	cout<<"sizeof(Son):"<<sizeof(s)<<endl;
	cout<<"Base 1 ma=:"<<s.Base1::ma<<endl;
	cout<<"Base 2 ma=:"<<s.Base2::ma<<endl;
}

int main() {
	test01();
	return 0;
}    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

以场景驱动CMDB数据治理经验分享

数据治理是 CMDB 项目实施中难度最大、成本最高的环节&#xff0c;是一个长期治理的过程&#xff0c;而行业很少提出 CMDB 数据治理的技术实现方案。CMDB 数据治理不仅需要解决配置管理工程性的技术问题&#xff0c;还要基于运维组织的特点&#xff0c;建立适应性的配置运营能力…

查看HDF5文件软件(HDFView)

HDFView&#xff1a;下载地址 note&#xff1a;我们需要下载 win10 、App软件&#xff08;win10在win11也能运行&#xff09;&#xff0c;因为App软件是轻量版&#xff0c;不需要安装就可以使用。 eg&#xff1a; 下载完后解压就可以使用。

空间数据索引的利器:R-Tree原理与实现深度解析

空间数据索引的利器&#xff1a;R-Tree原理与实现深度解析 R-Tree的原理插入操作分裂操作查询操作 R-Tree的伪代码R-Tree的C语言实现讨论结论 R-Tree是一种平衡树&#xff0c;用于空间数据索引&#xff0c;特别是在二维或更高维度的几何对象存储和检索中。它由Antony Guttman和…

书生·浦语 大模型(学习笔记-9)OpenCompass 大模型评测实战

目录 一、评测实现双赢 二、评测遇到的问题 三、如何评测大模型&#xff08;大概总结4大类方法&#xff09; 四、评测工具链及流水线 五、实战评测 GPU的环境安装 查看支持的数据集和模型 启动评测(会缺少protibuf库&#xff0c;提前安装&#xff09; 测评结果 一、评…

【蓝桥2025备赛】容斥原理

容斥原理 背景&#xff1a;两个集合相交 高中的韦恩图&#xff0c;我们知道两个集合相交时我们可以通过简单的计算来认识相关的性质 集合相交的区域是 A ∩ B A\cap B A∩B ,集合的并集是 A ∪ B A\cup B A∪B ,那怎么用集合表示 A ∪ B A\cup B A∪B 我们可以看作是A集合…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.3

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

mybatis的使用技巧9——mysql按年、季度、月、周等不同时间维度查询或分组统计

在实际项目开发过程中&#xff0c;按不同时间维度查询业务数据的操作异常频繁。比较多的操作如支持按时间周期范围做列表数据的筛选&#xff0c;或者是按年月日等维度的图表展示&#xff0c;亦或者是首页的概况&#xff0c;三维大屏的展示等&#xff0c;都离不开不同时间周期查…

网络靶场实战-Qiling Fuzz实例分析

背景 在上一小节中&#xff0c;介绍了qiling框架的背景和基础使用&#xff0c;并以相关的CTF和qilinglab实例进行练习加深对qiling框架的使用&#xff0c;后续并简单介绍了qiling fuzz的功能。 在这一小节&#xff0c;我们将对qiling fuzz iot设备进行测试以及以实例的方式对…

中级信息系统管理工程师-必会题锦集

文章目录 中级信息系统管理工程师-必会题锦集题目一CPU[解析]试题二 CPU[解析] 中级信息系统管理工程师-必会题锦集 题目一CPU CPU中&#xff08;1&#xff09;不仅要保证指令的正确执行&#xff0c;还要能够处理异常事件。 A. 运算器 B. 控制器 C. 寄存器组 D. 内部总线 [解…

1.C++入门(上)

目录 1.C关键字 2.命名空间 作用域方面的优化 a.命名空间定义 b.命名空间使用 3.C 输入&输出 1.C关键字 C有63个关键字&#xff0c;C语言有32个关键字&#xff0c;存在重叠如荧光笔标出 2.命名空间 作用域方面的优化 如果变量&#xff0c;函数和类的名称都存在于全…

SpringBootWeb请求

文章目录 前言一、Postman介绍 二、简单参数三、实体参数四、数组集合参数五、日期参数六、JSON参数七、路径参数 前言 在上一篇文章中&#xff0c;已经基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello Wor…

C++笔试强训day7

目录 1.字符串中找出连续最长的数字串 2.岛屿数量 3.拼三角 1.字符串中找出连续最长的数字串 链接 我的思路很简洁&#xff0c;就是双指针遍历&#xff0c;然后不断更新左位置left和右位置right和长度len。 然后我写代码的时候代码思路没跟上原本思路&#xff0c;直接把所有…

遇坑分享24.4.25

在对数组进行排序算法时&#xff0c;如果我使用多个下标进行元素交换的时候&#xff0c;可能会出错。 以下面的直接选择排序&#xff08;排列升序&#xff09;为例&#xff1a; public static void selectSort1(int[] arr){int left0;int rightarr.length-1;while(left<rig…

2024HWqax线上产品培训试题(天眼)

最近做了qax笔试题&#xff0c;分享一下&#xff0c;仅供学习参考&#xff0c;侵删

力扣HOT100 - 200. 岛屿数量

解题思路&#xff1a; 岛屿题目一般使用dfs。 1.判断是否越界 2.用0&#xff0c;1&#xff0c;2三个状态标识当前格子的状态&#xff08;三个状态比两个状态更清晰&#xff09; 3.向周围四个方向遍历 class Solution {public int numIslands(char[][] grid) {int cnt 0;fo…

【Spring篇 | 补充】三级缓存解决循环依赖

文章目录 7.三级缓存解决循环依赖7.1何为循环依赖&#xff1f;7.2三级缓存解析7.3三级缓存解决循环依赖7.3.1实例化A7.3.2创建B的需求7.3.3实例化B7.3.4注入A到B7.3.5B创建完成7.3.6回溯至A7.3.7清理二级缓存 7.4为什么不能用二级缓存解决循环依赖&#xff1f; 7.三级缓存解决循…

删除docker的容器与镜像

如果您想要卸载通过 docker pull influxdb 命令下载的 InfluxDB 容器&#xff0c;您需要执行以下步骤&#xff1a; 1. **停止正在运行的 InfluxDB 容器**&#xff1a; 首先&#xff0c;您需要停止任何正在运行的 InfluxDB 容器。您可以使用以下命令来查找正在运行的 InfluxD…

Xilinx 7系列 clock IP核的使用(二)

在 Clocking Wizard 中的输出时钟设置部分&#xff0c;主要目的是生成并配置系统所需的特定时钟频率和信号。这一功能在硬件设计和开发中非常关键&#xff0c;因为它允许用户精确地控制各个部分的时钟信号&#xff0c;以满足特定的性能、功耗和时序要求。 1 配置输出时钟 要启…

宝宝洗衣机买什么样的好?诚意推荐四款实力超群的婴儿洗衣机

近几年家用洗衣机标准容积的大大增加&#xff0c;从5Kg、6Kg升级到9Kg、10Kg。大容量洗衣机满足了家庭中清洗大件衣物、床上用品的需求。但由于普通大型洗衣机所洗衣物混杂&#xff0c;很多时候由于宝宝小件衣物数量不多&#xff0c;却也并不适合放在一起扔进大型洗衣机中清洗。…

macOS 一些系统图标的存放位置 icns

macOS 一些系统图标的存放位置 icns macOS 中有很多好看的图标&#xff0c;有时候就想用一下它&#xff0c;我来告诉你他们的具体位置。 系统图标位置&#xff0c;像各种通用文件类型的图标都在这里面&#xff0c;里面好多高清的系统图标 /System/Library/CoreServices/Core…